Scoreboard & Fake Player Issue
RoarkCats opened this issue · 6 comments
Version Info
Version: 1.19.2
Fabric Version: 0.14.9
Carpet Version: 1.4.83+v220727
I ran into some weird results while using vanilla scoreboard commands to get the highest score on a scoreboard of people which appears to be an issue with the Carpet mod fake players I was using as test dummies. It appears to arbitrarily choose which fake player has the highest score, despite the outcome not making sense in the context of the code.
This issue can be produced with these commands
Setup:
/player Alex spawn
/player Steve spawn
/scoreboard objectives add test dummy
/scoreboard objectives setdisplay sidebar test
Make sure to separate the players so you can see the output
Then give yourself and the fake players different scores (/scoreboard players set <player_name/selector> test <positive int>
)
Commands per test (order matters, setblock commands technically not necessary, just visualization)
/execute as @a at @s run setblock ~ ~ ~ air
/scoreboard players set $high_score test 0
/execute as @a if score @s test > $high_score test run scoreboard players operation $high_score test = @s test
/execute as @a if score @s test = $high_score test at @s run setblock ~ ~ ~ glass
The expected result is that the player(s) (fake or not) with the highest score will have glass at their feet
The result that occurs with fake players appears unpredictable, but certainly incorrect, if it does appear to be correct, switch the scores around so a different score order is present and try again
In my tests, Steve is always considered the highest one whether or not he is actually
That's what I said? Maybe it was not clear.
Regardless,
Let's say we have this scoreboard
P1 | 100 |
P2 | 75 |
P3 | 50 |
The order the players joined is P1, P2 and then last P3.
We initialize $high_score
to 0,
- Check against P1, since P1's score is larger than
$high_score
, it will set$high_score
to 100. then set it to 75, then 50.$high_score
, holds value 50. - Check against P2, since P2's score is larger than
$high_score
, it will set$high_score
to 100. then set it to 75, then 50.$high_score
, holds value 50. - We first check against P3, since P3's score is not larger than
$high_score
, it will not run this case at all.
Final result, $high_score
ends up with value 50.
You can simply confirm this by running a local server. Spawn Alex, then Steve then relog yourself. This way the order of player's will be Alex, Steve, you. Now if you give them scores in order, Alex 100, Steve 75, you 50, run the /execute as @a if score @s test > $high_score test run scoreboard players operation $high_score test = @s test
command and you will find It setting $high_score
to 50.
Kill Steve and respawn. Set $high_score
to 0, run the execute command again and now you will observe the $high_score
is set to 75. Repeat for Alex and you will find $high_score
setting itself to 100.
This is how this command works.
I hope this example conveys what I wanted to say earlier but failed.
And to achieve your desired effect, correct command will be
/execute as @a if score @s test > $high_score test run scoreboard players operation $high_score test > @a test
or even simply
scoreboard players operation $high_score test > @a test
Note the operation is >
not =
This is not a bug in carpet or fake players. It's working as intended because that's how vanilla commands work. Could be considered a vanilla bug? Idk
The order of players in the list (the way scoreboard traverses is) is the order in which they join the server.
The $high_score
variable will always point to the last player to join's score. It's not unpredictable as it looks.
The command flow of this execute command goes as follows,
Go through each player and check if the $high_score
value is higher than it's target.
If it evaluates to true, then it will set $high_score
variable to each player's score in order. Last player being the one whose score get's persistently assigned to $high_score
Move to next player.
Therefore, regardless when it triggers, it will always point to the last player's score who joins the server.
I apologize if this comes off as condescending, but that's not how execute as <entities> if <clause> run <cmd>
works
It will check the if statement as each entity and individually run the command thereafter, using as @a
will 'split' the command running it for each player, so to speak.
If you want an easy example where this is obvious, take this command:
/execute as @e if entity @s[type=armor_stand] run give @a diamond
If this would run as you describe, you wouldn't get diamonds but if even 1 armor stand existed you would get a diamond for every entity that exists in the world
In reality, it runs as all entities and if an individual entity is an armor stand it will give you an item as that armor stand, 1 diamond for each player from each armor stand
Edit: And, I'm not sure if I mentioned this or not, but I was able to test those commands with real players, though only briefly and only with one other, however it did work as expected there, normally and properly, not like with carpet's fake players
Vanilla commands are a bit odd, the "correct" solution is unintuitively just scoreboard players operation $high_score test > @a test
After some testing, it seems we were both wrong (Crec0)
The reason that it was breaking was not as you described, but rather simply that when working in one command like this the score does not update until after the command finishes running, simply queuing updates to the scoreboard as it runs through the entities.
Your command at the end there would be correct in this case, so thanks for the recommendation. I guess even after 3 years you can still miss simple operations like that.