-
Bug
-
Resolution: Fixed
-
23w41a, 23w42a, 23w43a, 23w44a, 23w45a, 23w46a, 1.20.3 Pre-Release 1
-
None
-
Confirmed
-
Commands
-
Normal
-
Platform
The bug
The command quota given by maxCommandChainLength is decreased by 1 for each modifier stage in command execution. When the command quota reaches 0, the execution in an execution context is canceled.
However, because this condition only applies when the command quota is exactly 0, execution will not be properly canceled when the command quota becomes negative. If your function has commands with two or more redirect modifiers, the function may or may not ignore maxCommandChainLength and keep running unpredictably depending on the command quota before each of the commands is executed. By carefully adjusting the number of command executions, it is even possible to define a function that recurses infinitely as in https://github.com/intsuc/23w44a_infinite_recursion.
How to reproduce
/gamerule maxCommandChainLength 1
/say !
→ A message is printed; this would be the correct behavior because the command quota is 1 when the executable stage say ! is about to be executed.
/execute as @s run say !
→ A message is not printed and Command execution stopped due to limit (executed 1 commands) is printed; this would also be the correct behavior because the command quota is 0 when the executable stage say ! is about to be executed. Note that the modifier stage execute as @s -> execute consumes 1 command quota but the modifier stage execute run ... does not because the latter does not have a redirect modifier.
/execute as @s as @s run say !
→ A message is printed; this would be an incorrect behavior. Because each modifier stage execute as @s -> execute consumes 1 command quota, the command quota is -1 when the executable stage say ! is about to be executed. As mentioned earlier, because -1 is not exactly 0, the executable stage say ! is executed ignoring maxCommandChainLength.
/execute summon armor_stand summon armor_stand run say !
→ Two armor stands are created and a message is printed; this would be an incorrect behavior. This result indicates that each redirect modifier is executed and decrements the command quota regardless of the current command quota.
Code analysis
public void runCommandQueue() { // ... if (this.commandQuota == 0) { // At this point, commandQuota can be negative. LOGGER.info("Command execution stopped due to limit (executed {} commands)", this.commandLimit); break; } // ... }
In net.minecraft.commands.execution.tasks.BuildContexts#execute, ExecutionContext#incrementCost, which decrements commandQuota, is called for each modifier stage with a redirect modifier. During these stage executions, commandQuota is not checked for execution cancellation, which could result in a negative commandQuota.