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

Removing scoreboard objectives captured by a score result consumer causes duplication of player scores

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Open
    • Resolution: Unresolved
    • Affects Version/s: 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
    • Fix Version/s: None
    • Labels:
    • Confirmation Status:
      Confirmed
    • Category:
      Commands

      Description

      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 definitely 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 definitely 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;
              });
          }
      }
      

       

        Attachments

          Activity

            People

            Assignee:
            Unassigned Unassigned
            Reporter:
            intsuc intsuc
            Votes:
            3 Vote for this issue
            Watchers:
            3 Start watching this issue

              Dates

              Created:
              Updated:
              CHK: