Uploaded image for project: 'Minecraft: Java Edition'
  1. Minecraft: Java Edition
  2. MC-158085

Removing a scoreboard objective captured by a callback causes duplication of player scores

XMLWordPrintable

    • Icon: Bug 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

      1. /scoreboard objectives add _ dummy
      2. /execute store result score _ _ run scoreboard objectives remove _
      3. /scoreboard objectives add _ dummy
      4. /execute store result score _ _ run scoreboard objectives remove _
      5. 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;
              });
          }
      }
      

       

            boq [Mojang] Bartosz Bok
            intsuc intsuc
            Votes:
            10 Vote for this issue
            Watchers:
            8 Start watching this issue

              Created:
              Updated:
              CHK: