Godot: Export arrays in singleton end up shared if initialized to same value

Created on 25 Jul 2018  路  17Comments  路  Source: godotengine/godot

Godot version:
Godot v3.0.5.stable.official.6a88e22 win64

OS/device including version:
Windows 10 Pro v.1803 (on 2 different pc)

Issue description:
Loading values in a two-dimensional array has different results if the array is local or global.

On a new project, I added a script (g.gd) as autoload-singleton enabled with the following code:
extends Node

extends Node
export var gridTiles = []
export var gridStat = []

I create a new scene assigned as main scene on project setting, on the root node added a script with the following code:

extends Node
func _ready():
    var x
    var y
    var gridTiles = []
    var gridStat = []

    for y in range(3):
        gridTiles.append([])
        gridStat.append([])
        for x in range(4):
            gridTiles[y].append(null)
            gridStat[y].append(0)

    gridStat[0][0] = 1  
    print(gridStat)
    print(gridStat.size(), " x ", gridStat[0].size())
    print(gridTiles)
    print(gridTiles.size(), " x ", gridTiles[0].size())

    for y in range(3):
        g.gridTiles.append([])
        g.gridStat.append([])
        for x in range(4):
            g.gridTiles[y].append(null)
            g.gridStat[y].append(0)

    g.gridStat[0][0] = 2
    print(g.gridStat)
    print(g.gridStat.size(), " x ", g.gridStat[0].size())
    print(g.gridTiles)
    print(g.gridTiles.size(), " x ", g.gridTiles[0].size())

The resulting output is:

[[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
3 x 4
[[Null, Null, Null, Null], [Null, Null, Null, Null], [Null, Null, Null, Null]]
3 x 4
[[2, 0, Null, 0, Null, 0, Null, 0], [Null, 0, Null, 0, Null, 0, Null, 0], [Null, 0, Null, 0, Null, 0, Null, 0], [], [], []]
6 x 8
[[2, 0, Null, 0, Null, 0, Null, 0], [Null, 0, Null, 0, Null, 0, Null, 0], [Null, 0, Null, 0, Null, 0, Null, 0], [], [], []]
6 x 8

Is it a bug or my poor knowledge of gdscript (python)?
Thanks for the attention.

bug confirmed hero wanted! gdscript

Most helpful comment

I tried using:

Godot: 3.2.2 beta 3 - Launched Today
OS: Ubuntu 20.04

This is a script attached to a Node

Screenshot from 2020-05-22 09-37-47

got the same issue

There is a workaround?

yes, there is, just attach different initial values

Screenshot from 2020-05-22 09-46-59

All 17 comments

I could reproduce the issue, here's a simpler example:

# g.gd, defined as "g" singleton
extends Node
export var arr1 = []
export var arr2 = []

```gdscript

main script

extends Node
func _ready():
print(g.arr1, " ", g.arr2)
g.arr1.append(1)
g.arr2.append(2)
print(g.arr1, " ", g.arr2)

Output:

[] []
[1, 2] [1, 2]


If I remove `export` from the arrays in the singleton, or define them with a different starting value, the error goes away.

So it seems like `export` arrays defined in a singleton with the same value will end up shared (so they're actually the same array, modifying one modifies the other).

The bug also happens if both are initialized to the same non-null value, e.g.:
```gdscript
# g.gd, defined as "g" singleton
extends Node
export var arr1 = [3]
export var arr2 = [3]

Output:

[3] [3]
[3, 1, 2] [3, 1, 2]

If the export keyword is removed from one or both of them, the issue disappears.

I could reproduce the issue on 3.0.5-stable and in the current master branch (f778bd8e).

I removed the keyword export because it's not for my purposes (I just looked at the documentation of godot - my lack of knowledge of gdscript as I wanted to prove) and it works.
I hope the bug does not create problems for others and is easy to solve.
Thanks akien-mga for the prompt reply, it's great to see how godot is supported by the community.

Just tested when asked by @bojidar-bg: if g.gd is not a singleton, but e.g. a script attached to a child node, then there is no bug.

So it's buggy only in the singleton + export + same initialization case.

Can you still reproduce this issue in the current master branch?

Just downloaded Godot v3.1.beta2.official and tested, nothing has changed:
with "export" the bug is present, without "export" no bug.

Same issue in Godot v 3.1.2-stable in linux. Remove "export" solve the issue.

Godot version:
Godot_v3.1.2-stable_x11.64 for linux.

OS/device including version:
Ubuntu 18.04

Similar issue in Godot_v3.2.1-stable_win64. However, it applies to non-singleton scripts as well.

extends Node2D
export (Array) var arr_A = []
export(Array) var arr_B = []

func _ready():
        for i in range(10):
            arr_A.append("A")
            #arr_B.append("B") # even bypassed it appends
        print(arr_B)

Output:

[A, A, A, A, A, A, A, A, A, A]

3.2.2 beta3

I tried using:

Godot: 3.2.2 beta 3 - Launched Today
OS: Ubuntu 20.04

This is a script attached to a Node

Screenshot from 2020-05-22 09-37-47

got the same issue

There is a workaround?

yes, there is, just attach different initial values

Screenshot from 2020-05-22 09-46-59

  1. export var a: Array = Array() # broken
  2. export var b: Array = [] # broken
  3. export var c: Array # works
  4. export var b := [] # works

I will look into this on my time off from FBX over the weekend.

1. export var a: Array = Array()  # broken

2. export var b: Array = []  # broken

3. export var c: Array # works

4. export var b := []  # works

Based on this info, it seems like an issue with type inference where it has two sources of type. Here both are Array but that seems inconsequential.
In cases 1 and 2, we have type Array specified on both sides of =. Hence, fails.
In cases 3 and 4, we have type Array specified on only 1 side of =. Hence, successes.
I am just guessing right now. Will test it soon.

Update: @boruok I couldn't get case 4 to work if both are like that.
But, it works if they are different.

#failed
export var arr1:= []
export var arr2:= []
#works
export var arr1:Array
export var arr2:= []

Quick look at the code:
The main place where constant values might get merged together and mixed up is the GDScript compiler. It has the following code, when processing a ConstantNode:
https://github.com/godotengine/godot/blob/3be9c74d8b0337d387fb90b8b2c228ebaaae3317/modules/gdscript/gdscript_compiler.cpp#L376-L386

Now, this does not happen for all empty arrays. This is so because _reduce_expression in the parser converts an ArrayNode to a ConstantNode only when its elements are constant and when it is in a place where it should try to convert an expression to a constant (p_to_const).
https://github.com/godotengine/godot/blob/3be9c74d8b0337d387fb90b8b2c228ebaaae3317/modules/gdscript/gdscript_parser.cpp#L1740-L1753

This function is called through the place parsing TK_PR_VAR. p_to_const is set to true only when exporting the variable (code is a bit more convulted, but that's what autoexport || member._export.type != Variant::NIL means)
https://github.com/godotengine/godot/blob/3be9c74d8b0337d387fb90b8b2c228ebaaae3317/modules/gdscript/gdscript_parser.cpp#L4920

This does not happen in case 3 and 4 above, since when there is no assigned value _parse_and_reduce_expression does not get called. Instead, the code tries to generate a valid default value for the type, and that piece of code returns a proper ArrayNode for arrays.
https://github.com/godotengine/godot/blob/3be9c74d8b0337d387fb90b8b2c228ebaaae3317/modules/gdscript/gdscript_parser.cpp#L5025

@bojidar-bg case 4 is broken too.

Alright. Updated my comment.
Note that export(Array) var b should likewise work, as it does not have an assigned value, even though it lacks a data type.

This is fixed by #41983 in 4.0, but a fix is still needed for 3.2, so reopening.

Was this page helpful?
0 / 5 - 0 ratings