-
Bug
-
Resolution: Unresolved
-
None
-
1.14.4, 19w34a, 1.15 Pre-release 5, 1.15.2, 20w06a, 20w07a, 20w08a, 20w09a, 20w10a, 20w11a, 20w12a, 20w14a, 20w16a, 20w17a, 20w18a, 20w20b, 1.16 Pre-release 5, 1.16.1, 1.16.2 Pre-release 1, 1.16.2, 1.16.3, 1.16.4 Pre-release 1, 1.16.4, 20w45a, 20w46a, 20w48a, 20w49a, 20w51a, 21w03a, 1.16.5 Release Candidate 1, 1.16.5, 21w10a, 21w15a, 1.17.1, 21w37a, 1.19.4, 23w18a, 1.20.2, 23w46a, 24w33a, 1.21.1
-
Confirmed
-
Commands
-
Important
-
Platform
The bug
When a scoreboard objective captured by a command callback (a score result consumer) is removed from the server scoreboard, the objective remains in the callback and create an inaccessible score.
How to reproduce
/scoreboard objectives add _ dummy
/execute store result score _ _ run scoreboard objectives remove _
/scoreboard objectives add _ dummy
/execute store result score _ _ run scoreboard objectives remove _
- Save
→ data.PlayerScores in data/scoreboard.dat has two identical objects.
Code analysis
(1) The argument objective is captured by the callback. The callback is executed after objective is removed from the server scoreboard.
// net.minecraft.server.commands.ExecuteCommand private static CommandSourceStack storeValue(CommandSourceStack source, Collection<String> targets, Objective objective, boolean isResult) { Scoreboard scoreboard = source.getServer().getScoreboard(); return source.withCallback((context, success, result) -> { for (String target : targets) { Score score = scoreboard.getOrCreatePlayerScore(target, /* (1) */ objective); int count = isResult ? result : (success ? 1 : 0); score.setScore(count); } }, CALLBACK_CHAINER); }
(2) playerScore is an empty map in this case since all the scores of the objective in playerScores are removed when the objective is removed.
(3) Since playerScore is empty, the mapping function is applied and a score with count 0 is created using the removed objective. This causes duplication of player scores.
// net.minecraft.world.scores.Scoreboard public Score getOrCreatePlayerScore(String playerName, Objective objective) { if (playerName.length() > 40) { throw new IllegalArgumentException("The player name '" + playerName + "' is too long!"); } else { // (2) Map<Objective, Score> playerScore = playerScores.computeIfAbsent(playerName, n -> Maps.newHashMap()); // (3) return playerScore.computeIfAbsent(objective, o -> { Score score = new Score(this, o, playerName); score.setScore(0); return score; }); } }