How can I number entities returned by a target selector?
I would like to test for entities in a certain area and number them.
A target selector always returns a list of selected entities in the order of the sort
parameter.
For example, the following target selector will return a list of players within 100 blocks of the running command block, in random order:
@a[distance=..100,sort=random]
I would like to be able to number the entities returned by the target selector in two ways: through scoreboard objectives, and through scoreboard tags. For example, if the target selector returns players Alice, Joe, Bob
, I would like for Alice
to get scoreboard value 1, Joe
to get 2, and Bob
to get 3. If it returns players Joe, Alice, Bob
, Joe
would get 1, Alice
would get 2, and Bob
would get 3.
Using scoreboard tags, a tag named Player1
would be added to the first player, Player2
for the 2nd, and so on.
What is the best way to construct a functioning command mechanism for this purpose? Note, I would like an answer for all 3 non-arbitrary sorting methods, meaning nearest
, furthest
, and random
.
Use [distance=..100]
as your target selector template argument so that everyone's answer is consistent in what part is their target selector.
pppery's answer already has the best method for doing this with tags, it requires one command per tag. That's because tags are not made for this, they're made for binary states like "does this player have this property".
The missing piece to do it better with scoreboards is /scoreboard players operation
, which allows you to copy a score from one place to another.
Let's start with pppery's idea of giving everyone a score of -1 initially, it works better than a tag:
scoreboard players set @a[distance=..100] id -1
And you need a fake player (or anything) to hold the current number:
scoreboard players set #current id 0
Now you repeat increasing that and copying it to another player who doesn't have an ID yet:
scoreboard players add #current id 1
scoreboard players operation @p[scores={id=-1}] id = #current id
If you don't rely on it being done all in one tick, you can simply loop the last two commands. If you have access to the world save, you can put them into a function that then calls itself if there are players left to handle, for example like this:
execute if entity @p[scores={id=-1}] run function fabian:assign_id
If you don't have access to the server data, there are some tricks you can do with command blocks to make them loop by changing their directions around, it's a bit complicated. You could also just put more chain command blocks down, containing the same two commands many times. But in that case, you can just hard-code the ID and only use one command block per ID.
I've found one way of doing this, which is unfortunately a bit verbose (requires one command block for each player who needs to be identified), but works:
Using scoreboard objectives:
/scoreboard players set @a[distance=..100] Id -1
/scoreboard players set @a[scores={Id=-1},sort=...,limit=1] Id 1
/scoreboard players set @a[scores={Id=-1},sort=...,limit=1] Id 2
/scoreboard players set @a[scores={Id=-1},sort=...,limit=1] Id 3
...
Using tags:
/tag @a[distance=..100,sort=...,limit=1,tag=!Player1,tag=!Player2,tag=!Player3,...] add Player1
/tag @a[distance=..100,sort=...,limit=1,tag=!Player1,tag=!Player2,tag=!Player3,...] add Player2
/tag @a[distance=..100,sort=...,limit=1,tag=!Player1,tag=!Player2,tag=!Player3,...] add Player3
...