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

Result consumer created by "/execute store" is invoked for every command within a function

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • 23w41a
    • 1.19.4, 23w16a, 23w17a, 23w18a, 1.20 Pre-release 7, 1.20, 1.20.1, 23w31a, 23w32a, 1.20.2
    • None
    • Plausible
    • Commands, Performance
    • Normal
    • Platform

      The bug

      /execute store is a command that runs subsequent commands in a new command environment with a result consumer that consumes command results. This result consumer is carried over across /function, and is invoked every time a command within the function is executed. This behavior is natural when considering that other elements such as positions and entities are similarly carried over. However, this behavior introduces overhead when obtaining the result of /function with /execute store. In particular, when obtaining the result of a recursive function with /execute store, the cost per command is quadratic with respect to the recursion depth.

      How to reproduce

      Simple case

      1. Run
        /function mc-262027:simple-a
      data/mc-262027/functions/simple-a.mcfunction
      data modify storage mc-262027: simple set value 0
      execute store result storage mc-262027: simple int 1 run function mc-262027:simple-b
      
      data/mc-262027/functions/simple-b.mcfunction
      # This command prints 0.
      tellraw @a {"nbt": "simple", "storage": "mc-262027:"}
      
      say mc-262027:
      
      # This command prints 1 because the result consumer was applied to the result value of `/say <message>` (1).
      tellraw @a {"nbt": "simple", "storage": "mc-262027:"}
      

      Recursive case

      In the case of recursion, reproduction steps become a bit more complicated. This is because result consumers perform a set operation, making it difficult to observe whether they have been executed multiple times.

      1. Run
        /gamerule maxCommandChainLength 12
      2. Run
        /function mc-262027:recursive-a
      3. Run
        /tellraw @a {"nbt": "recursive", "storage": "mc-262027:"}

        → formatted output:

        [
          {_: 0},
          {_: 0}, {_: 0},
          {_: 0}, {_: 0}, {_: 0},
          {_: 0}, {_: 0}, {_: 0}, {_: 0},
          {_: 0}, {_: 0}, {_: 0}, {_: 0}, {_: 0},
        ]
        

        From the above output, we can see that the function mc-262027:recursive-b has recursed 5 times, and the result consumers have been invoked a total of 15 times (= (5 * (5 + 1)) / 2), which is quadratic.

      data/mc-262027/functions/recursive-a.mcfunction
      data remove storage mc-262027: recursive
      function mc-262027:recursive-b
      
      data/mc-262027/functions/recursive-b.mcfunction
      # The NBT path [{_: 1}] adds {_: 1} to the target list tag if it is not present.
      # Then, the NBT path _ sets 0 to the _ in all compound tags within that list tag.
      # As a result, the list tag loses {_: 1} once again.
      # By repeating this process, elements can be continuously added to the list tag.
      # Note that for this reproduction function, the cost is **quartic** due to searching within the list tag.
      execute store result storage mc-262027: recursive[{_: 1}]._ int 0 run function mc-262027:recursive-b
      

            Unassigned Unassigned
            intsuc intsuc
            Votes:
            8 Vote for this issue
            Watchers:
            6 Start watching this issue

              Created:
              Updated:
              Resolved:
              CHK: