Script limit

From Fancade Wiki

For every game, there is a hard script limit. After a certain number of scripts in the editor, adding another will cause a "Too many scripts" error. The game will refuse to run as long as the extra scripts are not removed. Games that do not optimise scripts affecting multiple objects into loops are most likely to run into this issue.

Error_Too_many_scripts.png

History

Advanced inspect

Script limit has been a mystery for a long time. It was an elusive upper limit that creators randomly hit, and are forced to scrap their ambitious project. Fancade 1.2 significantly improved the scenario by introducing the Advanced setting of Inspect blocks. Those blocks, when enabled, showed how much of the script limit has the game consumed already.

Optimised custom scripts

Script limit was not forgiving for custom script blocks, as each instance essentially was operating with multiplication towards the script limit. This behaviour was altered in Fancade 1.13, with script limit reduced upon their usage and effectively giving more freedom in coding complex games. Only the first instance calculated the accumulation inside towards the limit.

Details

The introduction of advanced inspect allowed for more robust testing, which helped in discovering some key insights regarding the elusive script limit. Among those, Andrew Clark's discoveries were crucial for our current understanding of it.

The script limit, as of Fancade 1.14, is 4096. Internally, the game calls it ASTs. There are four things that consume the script limit.

Script blocks

Every script block placed counts as 1 script towards the limit. It does not matter which script block it is, as long as it is an inbuilt script block, it adds 1 to the counter. The attached image has 4 script blocks, thus the script consumption is 4/4096 or roughly 0.1%.

Current Frame, List Element Object, Touch Sensor, and Line vs Plane.

Wire splits

A wire split, defined as a connection of one-to-many, or many-to-one, counts as 1 script towards the limit. Thus the following image adds 4 scripts (3 script blocks + 1 wire split) to the counter:

Script_Limit_wire_split_1_to_2.png

It should be noted that according to the definition, a large split is thus still the same. The attached script adds 10 scripts (9 script blocks + 1 wire split) to the counter:

Script_Limit_wire_split_1_to_8.png

The same applies in case of joining. The following script above counts as 4 scripts, whereas the script below counts as 10 scripts:

Script_Limit_wire_split_2_to_1.png
Script_Limit_wire_split_8_to_1.png

Pointer dereferencing

This section will be easier to understand if you are somewhat familiar with the concept of pointers. If you are not, you can think of them as an address. Specifically, a memory address, that points to a variable. When a pointer is dereferenced, i.e. converted to value, it counts as 1 script. In this image, the Get Variable block outputs a pointer, which is then dereferenced to a value for the Negate block. Thus the script adds 3 scripts (2 script blocks + 1 pointer dereference) to the counter.

Script_Limit_dereferencing.png

This behaviour, when combined with wire splitting, results in a peculiar quirk where adding another script block can actually consume less script limit than without that block. In the following picture, case 1 adds 8 scripts (4 script blocks + 1 wire split + 3 pointer dereferences) to the counter. Almost half of them are just from dereferencing. To avoid that, in case 2 another script block is added to dereference the pointer before the split. Thus case 2 only adds 7 scripts (5 script blocks + 1 wire split + 1 pointer dereference) to the counter.

Script_Limit_dereferencing_lesser.png

Known script blocks that output a pointer are all the Get Variable and List Element blocks.

Known script blocks that accept a pointer as input are the Variable inputs of the List Element blocks, the Variable input of the Menu Item block and Increase and Decrease Number.

Custom scripts

When a custom script block has built-in script blocks inside it, only the first instance of that custom block computes the amount in it. Together, the amount of instances of the script adds 1 to the script limit. For example, the attached script would count as 5 scripts (4 script blocks + 1 instance). Adding two more would count as 7 scripts (4 script blocks + 3 instances).

Script_Limit_custom_1.png
Script_Limit_custom_3.png

For every input and output port given to the script block, the first instance counts it as 1 more script. Rules for wire splitting still apply to these. To give an example, take a look at this vector interpolation found in Fanscripts by Sounak9434. This consumes 9 script blocks in total (3 script blocks inside + 4 IO ports + 1 wire split at top input port + 1 instance of the custom block):

Script_Limit_custom_LERP.png

Tips

Here are some general tips to conserve the limit as much as possible:

  • Use wire splits whenever possible.
  • Do not use variables unless absolutely needed.
  • If a pointer is dereferenced at least three times, dereference it before the split.
  • You may find it useful to pack scripts that repeat throughout the script into a script.
  • Comments do not count towards any limits. Use them as much as possible.