Godot: Some usages of `class_name` can produce cyclic errors unexpectedly

Created on 26 Aug 2018  ·  69Comments  ·  Source: godotengine/godot

Windows 10 64-bit - Godot 3.1 alpha

Edit - Refined issue to focus on cyclic errors.

Certain uses of class_name can produce cyclic errors in situations where there are no cyclic references, and in fact sometimes no other references outside of the single one given.

I've noticed this most frequently in tool script when using is to make a class comparison, but it seems to happen at times in normal script situations.

Recreating it in a sample project has eluded me.

godot master_2018-08-27_00-47-02

bug 3.2 confirmed high priority gdscript

Most helpful comment

The fix I was working on is still WIP and causing crashes. I'm not confident to merge such a big change it in beta stage, so it'll likely be done in a 3.2.x release later.

All 69 comments

It'll work if you give the base script a class_name and use that in the extends line.
I'm also getting the error when using file names at first (https://github.com/godotengine/godot/blob/master/modules/gdscript/gdscript_parser.cpp#L4947), but it also disappears and doesn't come back after it worked once (as in, after I use the mentioned workaround)

@PetePete1984 Cool, thanks Pete. This is how it is intended to be used then?

While double checking and testing this, I was looking at the get_class() method, and wondering if it should return Node2D? The is CustomClass part is returning _true_, but is_class("CustomClass") on the other hand is _false_.

CC @reduz @vnen @willnationsdev

I wouldn't say it's intended behaviour, especially not with an error message that implies cyclic inclusions where there aren't any.
get_class() and is_class() are confusing to me in general, can't comment on that really. The source might solve that mystery in the long run, but so far is has been enough for me.

@vnen Would this be something that is in need of a clearer error message?

@PetePete1984 Yes, is has been an amazing thing now in 3.1. Though I did come across a situation where I would've liked to use the new match like match custom_thing.get_class() and compare against several strings. But nothing critical, just a readability and less typing thing.

@avencherus You might be able to get away with:

match custom_thing.get_script():
    <typename>: stuff # script classes
    <typename>: stuff
    _: match custom_thing.get_class():
        "Node": stuff # engine classes

I'm not sure why it's giving this error, I'll have to investigate.

@willnationsdev Not entirely sure what the check for from get_script() part, for me at least, it returns a GDScript object [GDScript:1057].

@vnen Thanks vnen. X) Some other weird things seem to be going on when I went back to try out these suggestions to see if I could give you some better information. I'm going to pull and rebuild and fiddle with it in a little bit. See if there is anything else about it to add. I may have bumped into a different issue as well along the way.

@avencherus the "class names" of script classes are global variables that reference the script. So...

class_name CustomType
match get_script():
    CustomType:
        print("I am a CustomType!") # should print

@willnationsdev Oh neat, I see now. I wasn't expecting class_name to work like that, or the custom type to be available to the match pattern . Thanks for sharing this tip Will.

@vnen Yes, some additional wrinkles to the story. Let me see how well I can articulate them.

Firstly, Pete's solution works, when a base class is given a class_name all appears fine. What I did notice is that going back to the base class and pulling out the class_name CustomBase that I added, it half works. It no longer appears in the node list when adding nodes, but is will work, and no strange errors.

Then to confirm this, I went to recreate the steps of creating a base2.gd -> custom2.gd extended script. Then inside custom.gd use class_name CustomClass2, but no class naming in the base class.

Another issue appeared. The node list had the first one, but listed with an icon that said "Error"

godot master_2018-08-27_20-01-26

So I modified the first base class so they both used class_name. But got odd results again.

Not everything is showing up in the node list, and then the is keyword only returned true for one of them. Though in this screenshot you can see the node list recognizes CustomClass2, yet in the code behind it, is CustomClass2 is returning false.

godot master_2018-08-27_20-05-11

It seems like lot of things are getting switched up or are lingering about behind the scenes. Hard to quite predict what it may be doing with my limited knowledge.

Hope that helps. X)

edit disregard this comment. I hadn't realized that class_name is not implemented in 3.0.6, this issue refers to a 3,1 build anyway.

class_name seems to be broken in 3.0.6. I will make a separate issue, but:

image

@bfishman script classes are not a feature in 3.0.x. That is why it is not working.

I just tried the following and each version worked. All script classes showed up in the CreateDialog, the scripts compiled fine, the scene ran successfully, etc.

With a scene hierarchy like this:

  • Node (base.gd)

    • Node (derived.gd)

Script:

# res://base.gd
extends Node
class_name BaseNode
func _ready():
    print("base")

# res://derived.gd
extends BaseNode
extends "base.gd" # I tried both of these 'extends' lines and they each worked
class_name DerivedNode # I tried with and without this line
func _ready():
    print("derived")

Every time I ran the test, it printed

base
derived
base

...as it should.

My suggestion would be to try clearing your editor's file cache (if perhaps there were changes in the way the file cache was stored in the midst of your programming / updating the engine, that could somehow mess things up until you clear it away).

@willnationsdev Thanks for looking at, I'll try this out. Is this the files from AppData/Roaming folder? I'll go ahead and clear the whole thing for now. If so, wondering if there is something more specific that I should delete?

@avencherus I'm not quite sure. I just recall @karroffel mentioning something about it when I added some fixes to the script class icon stuff earlier.

Tested on https://github.com/godotengine/godot/commit/1093c0ff51b980634dffdd9618eaa53061da6419
I don't see any error but it is not showing the Node in "Create new Node" Dialog.

I'm also getting the error shown in the original post (Windows x64 Godot 3.1 alpha):

image

However, it happens in a different context. I have 3 scripts which each have a different class_name, and each of them extend Node2D. The error does not occur unless one of the scripts references it with the is operator. For example if some_var is Incomer. The weird thing though is that if the same script references the other class_name, there's no problem. And if the other script references the class_name in question, no problem either.

So there's a ClassA, ClassB, ClassC. If ClassC references ClassA with the is operator, then error at load time. If ClassC references ClassB, no error. If ClassB references ClassA, no error.

None of the scripts are in the same scene, and all of the nodes are siblings (no ancestor relationship).

@AlexHolly Same situation with me now as well. The error seems to be gone, but yes, classes won't appear in the add node window when not having a base script that also uses class_name.

This is in alpha 3.1 c320d93

A small example by using "is". The same thing can happen if you use "as".
This was mentioned by @Oranjoose too.
cycle.zip

None of the scripts are in the same scene, and all of the nodes are siblings (no ancestor relationship).

This has nothing to do with the nodes in the scene. Cycles happens in the script themselves.

For now it's quite easy to get cycles when using is and as and if it happens it's not really a bug, it's the way it is. I'll try to improve this but not for 3.1. The original report is a bug because there are no actual cycles in the code.

I couldn't reproduce the error in 451e5fd as well. The node didn't show up in the create window tho.

I can confirm that this Issue is not present in 0dbe014 (the latest master I pulled). We can probably close this Issue.

Also, I was able to make it so that the script classes that extend scripts still show up in the CreateDialog which was still a bug. PR is linked above.

@willnationsdev Thanks Will. X) I have been bumping into various issues with class_name, but haven't been able to recreate this one. They have be terribly hard to recreate, but I have since stopped using them.

I have noticed sometimes vague and unrelated errors can appear in a script, because of errors in a script that is being referenced. They become more meaningful when opening the culprit script, and when fixed these other ones clear out as well.

I will re-tailor the topic of the post though, because this cyclic error does seem to still be happening in odd situations and is elusive when trying to recreate it. So there is probably still something to it, I'll leave it open for vnen.

@avencherus I see. Well I hope you can eventually identify under what conditions it is happening. Without the context, we can't fix it. XD

I have noticed some cyclic reference errors that happened in my godot-next work where I was able to use the static typing to say I was returning the same type as the class name of the script, but if I tried to actually reference the class name of the script (to create an instance for later returning in the function), it wouldn't work because the script would be referencing itself and therefore need to recursively load itself infinitely.

The solution I found was to use load(get_script().resource_path).new(). Quite a mouthful, I know, but it allowed the parser to safely construct the data and get through the compilation process safely, let the typename officially get registered, and then only reference the type at RUNTIME once it had been completely initialized.

I will periodically keep making an effort to tease it out. Wish I could be more help, but I'm imagining these will have to wait until those with deep knowledge of their implementation are working on their own projects and run head first into these problems. I would wager they'd know where to set their sights in that situation.

A quick summary of some of the issues (I'll get these posted sometime in the coming weeks):

  1. I would get these cyclic errors when doing obj is MyClass and there was no other connection or reference between the two. Though I don't know if it is scanning the scene tree and counting its children as cyclic references.
  2. I have also run into the inconvenience you've described. I wish it wasn't so, because it works fine with Built-In / native class names in the same types of situations. So you can be writing a whole bunch of code with this end aim in mind thinking all is well, until you begin to add your custom types, and you have to resort to a hack/work-around/older method, and then what was the value of it if not to avoid verbose path names?
  3. I've had these things error-free in the editor and executing clean during test runs, but then go on to have errors and failures in the exported game. An example below (though most likely unrelated to this issue) is the class_name usage couldn't find their icons in the build, but no issues in runs and tests from the editor.

gtc_2018-10-12_13-21-35

Just overall my experiences with class_name has been a nearly daily encounter with a lot of unpredictable, difficult to explain, and hard to reproduce errors. A lot of time is wasted dancing around it and trying to resolve them.

I was really looking forward to them, and they absolutely would make coding certain things a whole lot more compact and readable. For practical time reasons I have to set them aside and opt out of using them in my current project.

Maybe in 4.0. X)

I'm going to revert to the old method of script paths. In doing so it would just be my own preference to use them all the way through to avoid any random class_name surprises, and to keep the code more consistent.

As I said before, it's very easy to create cycles with class_name, especially when using is and as. It's less easy with paths, but you might bump into similar problems too. The problem is GDScript was never wired to work like that, so it depends too much on resource loading. I have some ideas to fix this (in particular just ignoring resource loading and doing shallow parse of dependencies directly), but this requires a structural change of the parser.

Probably it will properly solved in next versions, since it's not a good time to mess with this. class_name will be a "lesser feature" for now. You can still use it in some cases, but not as extensively as you expect.

@vnen Hey. Welcome back. X)

Thanks for the reply and the timeline.

Yep, that's why I have to shelf it for now. I was a bit slow to realize it wasn't such a smooth, fitting, or natural integration. (I was sort of getting hints of this when I saw all the entries in the project file.)

I suppose it seems like a "lesser" feature, but it will have some very serious benefits if the wrinkles can be worked out. I really do look forward to it, and feel its loss after using it some already. It is definitely a good path forward for GDScript.

One major thing I will be missing is the ability to draw upon hints when a custom class is combined with your new type hinting. var thing : NewThing

When making a large class over a few days, and perhaps a few days later the first time to use it comes up, I find I don't 100% remember the names precisely of every function until I've used them a few times. So getting these hints were quite an unexpected and significant productivity bump for me. I was able to stay and work in one area for much longer.

Certainly the core of the issue is as you say, easy cycles, but I think my most worst experiences have been that currently some of the errors are not pointing in the right places. It's been some weeks now, but I've had situations where I used a custom class and all was well for a few days. Then I wrote some more code that called upon that custom class, and at some point during the development I started getting the cycle errors. In some cases these errors were not helpful in explaining where the error was occurring. In order to find it I had to hunt down and remove all class name usages one by one until the error stopped.

Just a few incidents like that have reclaimed much of the gains. I dread to imagine sparking a problem like that somewhere 30,000 lines in.

The one incident that made me yank all of them, was the one where the cycle errors only appeared in the export build, and were throwing unrelated script errors. I think one was saying the icon textures weren't found, but that really wasn't it, and it cleared up when I ditched some class_name usages. I suspect the class_name was failing, and the error was falling on the following line.

I've seen it happen sometimes where errors are happening on the line above or below the cause.

It's really hard for me and my zero knowledge here to pinpoint the source of many of these issues. A lot of it is simply behind the curtain to me, and I'm grateful you're able to decipher some of this.

In 3.1 there has been a lot of unusual events with the warnings and errors, so I sit wonder if the problem is the thing itself, or an issue with the error reporting logic? I don't exactly know yet where to begin, and I can only hope that some of this ends up being helpful, and also with time that I might be able to narrow them down into small projects.

EDIT: Nevermind, somehow I just resolved the cyclic inclusion simply by not doing it...
The global classes in project.godot get overwritten anyway on launching editor.

@vnen or @avencherus It would be good to update the OP with a clear description of what this bug is about in the end, as it's otherwise a lengthy discussion to read before having a good idea of it.

@akien-mga I'm not too sure about its status anymore, or what specifically to write about it.

If I recall there are at least to parts to it. The expected cyclics errors as a limitation of GDScript in its present form, and then unexpected ones that appear that are difficult to isolate and reproduce in small projects (sometimes because of inaccurate error messages).

Since it's not a feature I'm currently using, I haven't been following the progress. I think some things here and there have been fixed since this time.

At a glance in these other issues it looks as if the talk is in regards to changes needed in GDScript. With the revisions aimed at 3.2. If that is the plan, then wouldn't be under the 3.1 milestone anymore.

It's probably fine to close this, unless vnen is using it for bookkeeping, or if you prefer to use it to refer to for the upcoming duplicate issues when 3.1.0 stable lands.

The feature is a usability win I'm interested in, so I will very likely revisit it in 3.2, and post any findings.

Well this should not be closed, no, the sheer amount of duplicate reports definitely show that there is something to fix :)

I ran into the following error message that is problematic to me updating a project from 3.0 to 3.1. Previously the following code worked:

extends Node2D
export(String, FILE, "*.gd") var _class_name = "res://Test.gd"
onready var SomeClass = load(_class_name)

func foo():
    for c in get_children() :
        if c is SomeClass : 
            do_stuff()

When I change the code to:

extends Node2D
class_name SomeClass

func foo():
    for c in get_children() :
        if c is SomeClass : 
            pass

I get the following error:

Parse Error: Using one name in class file is not allowed (creates a cyclic reference)

This is frustrating to me as the children nodes I am looking for all inherit from this class and are mixed with other children nodes that don't. Is the ability to type check for other instances of a script class intended not to work this way and is there another way I should be doing this.

@Diaspater I think that all you have to do is change if c is SomeClass to if c is get_script(). Both options execute a runtime check, but the SomeClass version requires that you load the script again at editor-time (resulting in a cyclic reference) while the get_script() method does not attempt to do anything at editor-time.

@willnationsdev I understand that is a valid solution (or workaround to my issue as I realized after writing my original comment that the first way is still working. That being said I would prefer if the second way could be properly supported. I also figured out that this works:

DummySomeClass.gd
extends Node2D
class_name DummySomeClass

func foo():
    pass


SomeClass.gd
extends DummySomeClass
class_name SomeClass

func foo():
    for c in get_children() : 
        if c is DummySomeClass: 
            pass

That being said, I understand your explanation as to why it is currently giving an error. With multiple solutions or workarounds to this issue I feel this isn't an essential bug to fix but still a bug that I would like to see fixed at some point in the future.

After using this method in slightly more complex uses it seems this is causing other cyclic errors at other locations in other coupled scripts so this probably isn't a good solution / workaround to the problem.

This might be a naive question but shouldn't we get rid of this non-cyclic restriction.
I mean other languages like Java, C#, C++, etc... can deal with types without issues...

@harraps that will be done (as I said a few times in the thread), but it requires some fundamental changes in how GDScript is wired. I won't touch this now since we're close to release and this change will likely break compatibility.

In case any others encounter this, I have found another instance of a "cyclic reference" related error, but it's different from the others listed here.

If you make a class with a static factory method that references the name of the current class in the return value, it won't report any sort of error within the editor itself, but once the editor closes, you'll get an error reporting that resources are still being used.

extends Reference
class_name MyClass

static func make() -> MyClass:
    # can't even use get_script() for static funcs, but that's a separate issue
    var c = load("res://my_class.gd").new()
    return c

You then get this error:

ERROR: SelfList<class GDScriptFunction>::List::~List: Condition ' _first != 0 ' is true.
   At: .......\godot\core/self_list.h:111
ERROR: SelfList<class GDScript>::List::~List: Condition ' _first != 0 ' is true.
   At: .......\godot\core/self_list.h:111
WARNING: ObjectDB::cleanup: ObjectDB Instances still exist!
     At: core\object.cpp:2093
Leaked instance: GDScript:17060 - Resource name:  Path: res://my_class.gd
Leaked instance: GDScriptNativeClass:1058
ERROR: ResourceCache::clear: Resources Still in use at Exit!
   At: core\resource.cpp:425

Reposting from #944,

I'm also running into cyclic GDscript dependencies regularly. Here are some use cases where I'm currently forced to revert to untyped GDscript:

  1. My characters use an ActorController that run different Moves (like states in a state machine). The ActorController forwards _physics_process() and such to the active Move. Vice versa, the move accesses the ActorController to switch to another Move or access properties (like jump height, running speed, etc.)

  2. Same situation with my MoveRepository - the repository stores reusable Moves, yet the Moves also need to access the repository if the want to fetch another Move to select it into their ActorController

actor-framework

This is a non-issue in C# or C++, but GDscript (with optional static typing) requires me to fall back to variants (dynamic typing) or create needless indirections using events and such.

I'm also having this issue. I have a setup that's similar but simpler.
Class TurnManager has a variable of type Actor and every Actor has a typed reference to the TurnManager
I'm not even using is I have literally just declared the variables and it is enough to provoke this error.
Using the new stable version 3.1 on Linux 64-bit.

image

Just ran into this. Seems related.

image

Very odd indeed.

image

I, too, seem to be encountering this. I was wondering if it's because I had the scripts mostly detached from objects? I have a base Attribute class, which extends node, and PrimaryAttribute and SecondaryAttribute extending it, and then like 12 PrimaryAttribute nodes and a SecondaryAttribute node. The editor doesn't seem to like it though? Even though the game runs fine when I play it.

Of note, I switched out the resource paths for the class_names, and that got things working fine for about 5 minutes before they went back to giving me errors for no reason. :/

Hmm. The errors seem to have to do with the order that things are loaded in the editor window? I can get them to go away by closing and reopening files in the order in which they inherit. So, probably some kind of trouble around it not declaring things in some files. :/

Edit: Actually no, it doesn't matter what order I open files, or even if I have other files open at all. When I first load one of these files, they don;t show any errors, and won't until I change them, and then all the errors show up. :/

@Angular-Angel I've noticed that restarting Godot resolves the errors. It's disappointing that I can't use static typing in Godot with custom classes/types yet without bizarre errors popping up.

"Just" closing / reopening the projet has been enough for me. Shift+Ctrl+Q (go to project list), confirm prompt, then double click the project again. Still a bit frustrating, but you get used to it (until it's fixed).

Sorry, but I am a bit frustrated too, and I didn't know about that workaround since you guys commented, I stopped typing my code a while a go, but I still miss it A LOT.

Agreed. This became a very key habit very fast for myself and others. Will probably see if the restart workaround is enough for the moment. (Godot starting fast will be great)On May 16, 2019 8:27 AM, Ricardo Alcantara notifications@github.com wrote:Sorry, but I am a bit frustrated too, and I didn't know about that workaround since you guys commented, I stopped typing my code a while a go, but I still miss it A LOT.

—You are receiving this because you commented.Reply to this email directly, view it on GitHub, or mute the thread.

Godot 3.1.1 (not mono)
Stack.zip

minimal reproducible example

https://cdn.discordapp.com/attachments/477544613511692358/584811385838632977/unknown.png

_please_ someone fix this.

image

loading works fine, preloading does not

Note that there is in fact an uncaught script error in the StackList.gd. Specifically its a syntax error in the dictionary key declaration. However it is only caught once the dictionary is changed to a constant.

Just wanted to make a note that I hit the "is" class_name bug today as well.

I have reverted to using object.has_method("name") rather than testing if object is className

I've been hitting this one often in 3.1.1, and jumping through hoops to avoid it.
Hoping it will be fixed in 3.2!

I ask to please not bump issues without new information. We all know this happens, have already plenty of examples, won't magically go away with more comments.

I'm currently trying to fix this at @JavaryGames, the solution is already underway. Not sure if it'll make into 3.2 since it's a big change (had to rewire somewhat how GDScript works internally) and we're close to release, but maybe we can integrate in 3.2.1 or something. Will definitely be fixed in 4.0.

The fix I was working on is still WIP and causing crashes. I'm not confident to merge such a big change it in beta stage, so it'll likely be done in a 3.2.x release later.

@vnen Is there branch with fix for this bug, which we can compile and test?

Not sure if more information at this point helps, but a workaround (that worked for me) is to change the class_name of the problematic class and then change it back. Which makes it seem to be cache-related.

It happened to me at a line where I was casting a class member using as right after changing the signature of a method of the class to which the variable was cast to, but before I could fix the actual method. Then after fixing the method, the line kept giving this error, and it only stopped when I finally changed the cast class' name to something else, and then changed it back.

@vnen Is there branch with fix for this bug, which we can compile and test?

Yes, I have it in my public fork: https://github.com/vnen/godot/commits/gdscript-no-cycles

Not sure if more information at this point helps, but a workaround (that worked for me) is to change the class_name of the problematic class and then change it back. Which makes it seem to be cache-related.

You might get some caching artifacts in the editor since everything is being reloaded all the time as you're editing. But if you reach this problem it most likely won't work when running even if you manage to get it error-free in the editor.

https://github.com/godotengine/godot/issues/38265 seems to be a duplicate report of this with some reproduction details.

When this fix will be come in the main line?
It is super annoying because i use type save scripts and run very often into this problem.
Today i run into not solfable situation.

extends Resource
class_name WfcSlotHistory

func _init(slot:WfcSlot):
    pass
extends Resource
class_name WfcSlot

function saveHistory():
   var history:WfcSlotHistory = WfcSlotHistory.new(self)
   ...

res://assets/wfc/WfcSlotHistory.gd:5 - Parse Error: The class "WfcSlot" couldn't be fully loaded (script error or cyclic dependency).
modules/gdscript/gdscript.cpp:576 - Method failed. Returning: ERR_PARSE_ERROR

@MikeSchulze In this case, I feel like it's partly a design issue: WfcSlot should probably not know about WfcSlotHistory. The save action should probably be on the latter.

(This behavior/bug _is_ annoying, though, don't get me wrong.)

@Jeto143 thanks for your response, I solved my problem by copy all Slot members manualy into the SlotHistory.
Hopefully this bug will be solfed

Hi Devs when this long ... long bug will be finally solved?

At first I need multiple constructors for a class but this is not allowed, but why?
Second try I use static function to solve this problem but this also not working.

I have a class like this (simpe example)
````
extends Resource
class_name ClassA

_init(valueA:int, valueB:int):
prints(valueA, valueB)

static func of(value:int) -> ClassA:
return ClassA.new(value, value)
````

````
var o:ClassA = ClassA.new(1,2) # is working fine

var o2:ClassA = ClassA.of(2) # produces the error

````

The usage of the static func producedes this error.
"Parser Error: Using own name in class file is not allowed (creates a cyclic reference)"

So this blocks me to write a class to use multiple constructors where be standard behavior in modern languages.

I use current a hacky workaround like this
```
static func of( value:int) -> ClassA:
var clazz = load("res://../../ClassA.gd")
return clazz.new(value, value)
````

Best regards
Mike

Hi Devs when this long ... long bug will be finally solved?

It will be resolved in 4.0 as part of the GDScript rewrite.

Will this work for static functions as well when fixed?

class_name Cls

static func f():
    print(Cls)

@vnen so we are on the 3.2.X release, can we expect a fix anytime soon?

@Ploppy3 These fixes are due to massive changes that come with the GDScript rewrite and it's likely not possible to cherry-pick them. You will have to upgrade to 4.0 if you want to have these fixes.

I have this issue as well here are my two scripts.

class_name Entity extends Area2D

export var resource : Resource

onready var sprite   = get_node("sprite")
onready var collider = get_node("collider")

func _process(delta: float) -> void:
    if sprite   == null: sprite   = get_node("sprite")
    if collider == null: collider = get_node("collider")
    if resource is EntityConfig:
        sprite.frame = wrapf(sprite.frame+resource.gfx_speed, resource.gfx_start, resource.gfx_start+resource.gfx_count)
class_name EntityConfig extends Resource

export var gfx_image   := ""
export var gfx_start   := 0
export var gfx_speed   := 1.0
export var gfx_count   := 0
export var gfx_xoffset := 0.0
export var gfx_yoffset := 0.0
export var gfx_xsize   := 16.0
export var gfx_ysize   := 16.0
export var hit_xoffset := 0.0
export var hit_yoffset := 0.0
export var hit_xsize   := 16.0
export var hit_ysize   := 16.0

func new()->Area2D:
    var entity = Entity.new()
    entity.sprite            = Content.load_texture(gfx_image)
    if entity.sprite != null:
        entity.sprite.frame      = gfx_start
        entity.sprite.position.x = gfx_xoffset
        entity.sprite.position.y = gfx_yoffset
        entity.sprite.hframes    = entity.sprite.texture.get_width () / gfx_xsize
        entity.sprite.vframes    = entity.sprite.texture.get_height() / gfx_ysize

    entity.collider.position.x      = hit_xoffset
    entity.collider.position.y      = hit_yoffset
    entity.collider.shape.extents.x = hit_xsize / 2
    entity.collider.shape.extents.y = hit_ysize / 2
    return entity

I just discovered this problem while working on my capstone group project in Godot 3.2.3. For our collision callbacks, we have something like this:

extends Area2D

func _on_Throwable_body_entered(body: Node) -> void:
    if body is Player:
        if body.pick_up():
            queue_free()

So we want to check if body is Player before calling Player-specific methods. The problem is, Player may or may not be available, even if in global scope, whether declared via class_name or preload-ed. This happens even if Player isn't preloading our script to cause an obvious circular dependency.

We're currently working around this by simply ensuring that the Player is the only thing in the Area2D's collision mask and not checking anything in code at all. At first we were checking if body.name == "player".

I am happy to learn that it is being worked on, even if it's taking a long while, and I hope all goes well.

I had a similar dependency issue when that prevented me to implement a Builder pattern using a static method:

class_name MyClass

static func build() -> MyClass:
  var new_instance  = MyClass.new()
  ...
  return new_instance

Workaround described here to load class from source file path is nice but require to not change file tree without changing source code.

I use this trick to have current source file path even in static method (in which get_script() is not available):

static func build() :
    var new_instance = load(GodotExt.current_script_path()).new()
    ...
    return new_instance

with

class_name GodotExt

static func current_script_path():
    return get_stack()[1].source    <- return usable script path

This trick to get current class seems to work from anywhere.
It is raw and can be used to avoid similar cyclic dependency issue waiting the global dependency fixing.

hi @AliBouarab nice trick

Was this page helpful?
0 / 5 - 0 ratings