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

/function with /execute "An unknown error occurred while attempting to perform this command"

    Details

    • Type: Bug
    • Status: Resolved
    • Resolution: Fixed
    • Affects Version/s: Minecraft 1.12 Pre-Release 1, Minecraft 1.12 Pre-Release 2, Minecraft 1.12 Pre-Release 3
    • Environment:
      • OS: Windows 10
      • Java: 1.8.0_112
    • Confirmation Status:
      Unconfirmed

      Description

      Description

      When executing the following command:

      /function test:test
      

      and the definition of test:test is as follows:

      execute @s ~ ~ ~ function test:test
      

      you get this error message in the chat and server log:

      An unknown error occurred while attempting to perform this command
      

      Note: this also gets around the maxCommandChainLength limit (untested, but assumed true based on the code analysis below).

      Edit: as of 1.12-pre3, the execute command must be the last command in the function

      Why this is a problem

      Though the example above may be solvable by just replacing the execute command with a function command, it turns out that the execute command is the only way to create real conditionals, terminating loops and recursive functions. If I have a recursive function equivalent to a for loop from 1-1000, for example, it takes up a lot of space on the stack (see code analysis below). It may even be possible for more complex functions to run out of stack space, which kind of defeats the point of the maxCommandChainLength gamerule.

      Code analysis

      The bug is probably caused by a StackOverflowError. The current code used to execute functions is quite clever: it treats function commands inside functions differently to other commands such to avoid stack overflows from occurring. This is why we need it inside an execute command in the function definition, so the game doesn't recognize it as a function command.

      What calls what:

      • CommandFunction.execute(MinecraftServer, ICommandSender, String[]) (line 46)
      • FunctionManager.runFunction(Function, ICommandSender) (line 114)
      • FunctionManager$LineOfCode.execute(ArrayDeque<LineOfCode>, int) (line 176)
      • Function$CommandInsn.execute(FunctionManager, ICommandSender, ArrayDeque<LineOfCode>, int) (line 60)
      • AbstractCommandManager.execute(ICommandSender, String) (line 62)
      • AbstractCommandManager.tryExecute(ICommandSender, String[], ICommand, String) (line 92)
      • CommandExecute.execute(MinecraftServer, ICommandSender, String[]) (line 118)
      • CommandFunction.execute(MinecraftServer, ICommandSender, String[]) (line 46)
      • ...

      The current implementation of FunctionManager.runFunction looks like this:

      public int runFunction(Function func, ICommandSender sender)
      {
          int maxChainLength = getMaxCommandChainLength();
          // Attempted fix for this bug (I think)
          // This should also be a >= 1 (if I'm not mistaken)
          if (this.linesLeftToExecute.size() > 1)
          {
              if (this.linesLeftToExecute.size() < maxChainLength)
              {
                  this.linesLeftToExecute.addFirst(new LineOfCode(this, sender, new FunctionInsn(func)));
              }
              return 0;
          }
          
          try {
              int linesExecuted = 0;
              // ... read function to add all lines ...
              while (!this.linesLeftToExecute.isEmpty())
              {
                  // Since the line of code is removed *before* it is executed,
                  // linesLeftToExecute could be empty before we reach the lines above again
                  this.linesLeftToExecute.removeFirst().execute(this.linesLeftToExecute, maxChainLength);
                  if (++linesExecuted >= maxChainLength)
                  {
                      return linesExecuted;
                  }
              }
              return linesExecuted;
          } finally {
              this.linesLeftToExecute.clear();
          }
      }
      

      Alternative solution

      (doesn't fix bug, but add a way round it)
      Add proper support for conditionals in functions

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                dinnerbone [Mojang] Dinnerbone (Nathan Adams)
                Reporter:
                Earthcomputer Earthcomputer
              • Votes:
                4 Vote for this issue
                Watchers:
                8 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: