Static Variable Evaluating to `null`, I'm Struggling to See Why

Godot Version

v4.4.1.stable.official.49a5bc7b6

Question

I’m working on a project, currently trying to write some functionality into my scripting to support evaluation of precondition checks to see if instantiation of relevant objects is allowed. It’s not very relevant to the issue, but just for a matter of framing, I’ll share a brief description:

  • The game is an RTS game, and among other things, the player can build Structures in the game world
  • The validity of structure placement depends on 1) the structure in question, 2) the potential position of the structure, etc. for example, a given structure might only be buildable where there are resources
  • The structures are packed as scenes, to be instantiated and positioned after the player issues the command
  • I’ve put the logic for checking the validity of a structure’s placemnet in a class relevant to the structure type, and unfortunately it’s not straightforward to access the member functions of the relevant type, so I’m writing static functions to perform the checks, and I have enums that point to the validity-checking-functions, packed scenes, etc. relevant to the structure in question.

So, there’s the explanation of why my code is structured in the manner that it is. Now, in order to facilitate the passing around of the structure properties, I made a StructureSpec class to hold references to specifics of certain structures, and I have a dictionary mapping the enumerated Structure types to their specs. I think the failure in my code starts around here:

  • I have class definitions for different types of units, structures, etc. in my game
  • Different unit types will have different types of commands that they can receive, and for that reason, I’ve defined static functions and objects which track valid commands for the different unit types. Some child classes basically have a superset of the capabilities of their parent classes, so I access the static references of the parent classes to aggregate them with other logic.
  • The above was working completely fine, but when I added the StructureSpec implementation to my project, I found that the accessing of the just mentioned static references suddenly returned null values.
  • In my probing around, I specifically found that the issue occurs when I pass around references to the Scripts that implement the Structures that I’ve been discussing. In the snippet below, note that the existing definition of structure_type_spec_map fails to run, but the commented-out version runs without issue:
class_name StructureSpec

var cube_grid_arrangement: Array
var type: Script

func _init(
        a_script: Variant,
        a_cube_grid_arrangement: Array
) -> void:
        type = a_script
        cube_grid_arrangement = a_cube_grid_arrangement

static var structure_type_spec_map: Dictionary[int, StructureSpec] = {
        Structure.Type.MINE: StructureSpec.new(Mine, [Vector3i.ZERO]),
        Structure.Type.DWELLING: StructureSpec.new(Structure, [Vector3i.ZERO]),
        Structure.Type.OUTPOST: StructureSpec.new(Outpost, [Vector3i.ZERO, Vector3i(-1,1,0), Vector3i(-1,0,1), Vector3i(0,-1,1), Vector3i(0,1,-1), Vector3i(1,0,-1), Vector3i(1,-1,0)]),
        Structure.Type.LAB: StructureSpec.new(Lab, [Vector3i.ZERO, Vector3i(-1,1,0), Vector3i(-1,0,1)]),
        Structure.Type.COMPOUND: StructureSpec.new(Structure, [Vector3i.ZERO, Vector3i(-1,1,0), Vector3i(-1,0,1)]),
        Structure.Type.ARMORY: StructureSpec.new(Structure, [Vector3i.ZERO])
}

#static var structure_type_spec_map: Dictionary[int, StructureSpec] = {
        #Structure.Type.MINE: StructureSpec.new(null, [Vector3i.ZERO]),
        #Structure.Type.DWELLING: StructureSpec.new(null, [Vector3i.ZERO]),
        #Structure.Type.OUTPOST: StructureSpec.new(null, [Vector3i.ZERO, Vector3i(-1,1,0), Vector3i(-1,0,1), Vector3i(0,-1,1), Vector3i(0,1,-1), Vector3i(1,0,-1), Vector3i(1,-1,0)]),
        #Structure.Type.LAB: StructureSpec.new(null, [Vector3i.ZERO, Vector3i(-1,1,0), Vector3i(-1,0,1)]),
        #Structure.Type.COMPOUND: StructureSpec.new(null, [Vector3i.ZERO, Vector3i(-1,1,0), Vector3i(-1,0,1)]),
        #Structure.Type.ARMORY: StructureSpec.new(null, [Vector3i.ZERO])
#}
  • Because of this, I believe that my project throws an error, where my static field returns null, due to some sort of circular initialization.
    I’m not really sure what’s going on, but I’d appreciate if somebody could describe the issue I’m seeing, with any direct methods of addressing it!

Maybe some refactoring is in order to avoid this situation to begin with, but that would be a way longer discussion.

Project available here

Why are you typing this as a Script? Wouldn’t it make more sense to call it as a Structure (or possibly a Variant)?
It looks like this class is unfinished as there is nowhere that uses type so it is impossible for me to infer how it is used.

It looks like this class is unfinished as there is nowhere that uses type so it is impossible for me to infer how it is used.

Fair enough. I suppose my next response will explain:

Why are you typing this as a Script? Wouldn’t it make more sense to call it as a Structure (or possibly a Variant)?

The thing I’m trying to maintain a reference to is the static functions contained within the Script that defines the Structures. I can’t pass a reference to an instance of a Structure because I necessarily won’t have an instantiated object of that type yet; rather, I’m looking to check if I can instantiate a structure with some function call (e.g. is this position a valid position for this type of structure?)


I decided to test a refactoring where I supply the static function I need as a Callable, rather than the whole Script itself, and that circumvents the issue I’m seeing with the null-valued static var, changes here. However, I would still like to understand how it is that the value ends up being null.