Command tab completion with player position?

I’m registering a command in my plugin and was wondering if there is a way to get the tab completion parameters for the player location (or a default value). Essentially what I want to achieve is something similar to a teleport command, where if you press TAB the first time it will tab complete with the player X (or 0 if the command is being run from the console), the second time will have the player Y and the third time the player Z. But it looks like there isn’t something like that, since the GenericArguments.location CommandElement did not implement this feature. So, how can you achieve this result? Do I need to write a custom Command Element for this?

Yep. You will need to write your own sadly. Or use the GenericOption.sugestions not sure if there is one for dynamic suggestions

Edit:
There is one with dynamic suggestions

1 Like

Cool, that worked. But now what if I want to get the poisition of the block the player is looking at? I tried using this
player.get(Keys.TARGETED_LOCATION)
but that returns some incorrect values. For instance, if the player is at 167/69/262 and it looks at a block at 165/69/263, the TargetedLocation vector that returns is 174/64/256.
Is this the right key to get or there is another way to get this value?

Take a look at BlockRay

You can apply any entity (including Player) and it will loop through each block the player is looking at, you can apply filters to ignore air blocks and stuff

Edit:

I tried doing this

BlockRay<World> hit = 
     BlockRay.from(player)
             .skipFilter(BlockRay.onlyAirFilter())
             .stopFilter(BlockRay.allFilter())
             .build();
                
hit.end().ifPresent(h -> LogUtils.info(h.getBlockPosition().toString()));

but the position that logs is something like this, looking at the same block.
(157, 63, 272)

Looking at the documentation is seems that with BlockRay I get all the blocks the player is looking at (aka all bocks that are visible on the screen), which surely contains the block I’m actually aiming at but how can I get that specific location?

EDIT:
I’ve also tried this

BlockRay<World> blockRay = BlockRay.from(player)
                        .skipFilter(BlockRay.onlyAirFilter()).stopFilter(BlockRay.allFilter()).build();
                Optional<BlockRayHit<World>> hitOpt = blockRay.end();
                if (hitOpt.isPresent()) {
                    BlockRayHit<World> hit = hitOpt.get();
                    System.out.println("Found " + hit.getLocation().getBlockType() + " block at "
                            + hit.getLocation() + " with intersection at " + hit.getPosition());
                }

which is directly taken from the Sponge Documentation here

but it prints out this

Found Block{minecraft:air} block at Location{(156.0, 63.0, 272.0) in WorldServer{Name=world, DimensionId=0, DimensionType=minecraft:overworld}} with intersection at (157.0, 63.04171552784428, 272.17907057622017)

so it looks like the filter for skipping air blocks is not working

    @Override
    public CommandResult execute(CommandSource src, CommandContext args) throws CommandException {
        if(!(src instanceof Player)){
            return CommandResult.success();
        }
        Player player = (Player)src;
        BlockRay.from(player).skipFilter(BlockRay.onlyAirFilter()).build().forEachRemaining(ray -> {
            player.sendBlockChange(ray.getBlockPosition(), BlockTypes.BEDROCK.getDefaultState());
        });
        return CommandResult.success();
    }

results in the following

The tree is not set. By inverting the skipFilter, this is the result

It does work however you have read it the wrong way around

Edit:
Here is the code I used

        BlockRay.BlockRayBuilder<Extent> ray = (BlockRay.BlockRayBuilder<Extent>) (Object)BlockRay.from(player);
        ray.skipFilter(r -> !BlockRay.onlyAirFilter().test(r)).build().forEachRemaining(r -> {
            player.sendBlockChange(r.getBlockPosition(), BlockTypes.BEDROCK.getDefaultState());
        });

I thought I was checking in reverse, however for some reason it doesn’t let me use the negate() function of the predicate to reverse it. By the way I came up with this function ot get the block location, or the player location if is not aiming at a block

public static Vector3i getTargetedBlockLocation(Player player) {
        BlockRay.BlockRayBuilder<Extent> ray = (BlockRay.BlockRayBuilder<Extent>) (Object)BlockRay.from(player);
        BlockRay<Extent> hit = ray.skipFilter(r -> !BlockRay.onlyAirFilter().test(r)).build();
        if(hit.hasNext()) {
            return hit.next().getBlockPosition();
        }
        return player.getPosition().toInt();
    }

EDIT: I use the next() function on the BlockRay iterator to get the first value. This because the iterator returns a whole bunch of locations, while the block the player is targetting it seems to be the first value of the iterator

Glad you have a solution

1 Like