Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Registered properties of type VariantArray throw exception when declared as lateinit #569

Open
MartinHaeusler opened this issue Jan 30, 2024 · 6 comments

Comments

@MartinHaeusler
Copy link
Contributor

The following property declaration works without any issues and behaves as expected:

@Export
@RegisterProperty
lateinit var myNode: Node3D

However, the following property declaration:

@Export
@RegisterProperty
lateinit var playerAFrontRowPaths: VariantArray<NodePath>

... will throw an exception:

ERROR: Godot-JVM: An exception has occurred!
   at: check_exceptions (modules/kotlin_jvm/src/jni/env.cpp:69)
Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property playerAFrontRowPaths has not been initialized
	at com.cardsmith.client.ui.components.field.FieldManager.getPlayerAFrontRowPaths(FieldManager.kt:29)
	at godot.entry.FieldManagerRegistrar$register$1$1$4.get(FieldManagerRegistrar.kt:41)
	at godot.core.KtProperty.callGet(Properties.kt:28)

I can only assume that the VariantArray has something to do with it; it's the only difference I could spot. It doesn't make a difference if the property is ever used or not, the exception happens always when the scene loads.

As a workaround, the following declaration works again without issues:

@Export
@RegisterProperty
var playerAFrontRowPaths: VariantArray<NodePath>? = null

... but forces the programmer to deal with the null value at all call sites.

@piiertho
Copy link
Member

lateinit variables works, see tests
I suppose you call your variable too early and godot did not set it yet.

@chippmann
Copy link
Contributor

The lifecycle info can be found in the danger box here: https://godot-kotl.in/en/stable/user-guide/properties/#exporting-properties

@MartinHaeusler was this the issue and this can be closed?

@MartinHaeusler
Copy link
Contributor Author

MartinHaeusler commented Feb 8, 2024

@chippmann unfortunately no. Please consider the following class:

@RegisterClass
class ArrayCrashTest: Node3D() {

    @Export
    @RegisterProperty
    lateinit var testArray: VariantArray<Node3D>

}

The steps to reproduce the issue are quite simple:

  • Add the above class to your project
  • gradlew build the project
  • Start the Godot editor using a Terminal (the error message I've put into the opening post only appears there)
  • Create a Scene in the godot editor and add a Node3D to it
  • Attach the ArrayCrashTest script to the newly created node
  • In the inspector, add an entry to the testArray of type NodePath
  • Put any Node3D into the NodePath slot
  • Start the game

You will be faced with the following error immediately upon scene load:

image

This only happens if all of the following conditions are met:

  • The script exports a registered property which is lateinit and of type VariantArray (using a nullable VariantArray<T>? works fine, it has to be a non-nullable lateinit VariantArray<T>)
  • The editor must have at least one entry in the array (it doesn't happen if the array in the editor is empty)

This happens for me with no autoloads and no other scripts in the scene. As you can see, my script never attempts to access the testArray (frankly, it has no methods at all) and the error still happens 100% of the time. I can only assume that it has something to do with the array content from the editor being assigned to the property.

@MartinHaeusler
Copy link
Contributor Author

By the way, it would be super cool if this "Godot-JVM: An Exception has occurred!" would actually print the exception as well 😅

@CedNaru
Copy link
Member

CedNaru commented Mar 31, 2024

I have to investigate but knowing Godot's internal, I have my theory.
Outside the case of Object and all its child class, Godot doesn't have the notion of null for the other types of property. It always expects a default value to be already set.
When it's primitive or some math type like Vector2, Godot will simply override the current value, null or not, so it doesn't cause any danger.
The issues start when using a container type of property (VariantArray, Dictionary, PackedXArray), when setting the value, Godot probably doesn't set the container directly but fill the existing one. But when using lateinit on such container, there is no existing instance to fill.

If that's true, the crash will happen with the other container types as well. And there is not really any solution to allow lateinit like this to work. The rule would be to not use lateinit on containers in the first place and directly instantiate an empty one instead.

@chippmann
Copy link
Contributor

chippmann commented Mar 31, 2024

By the way, it would be super cool if this "Godot-JVM: An Exception has occurred!" would actually print the exception as well 😅

@MartinHaeusler the exception which caused this, should actually be printed right before that.
We get the exception in cpp land and let jni print it to the console. After that depending on whether we are in editor, in debug mode or in release mode we either print it, or let the program crash with that exception.

So the root cause should always be printed right before the line "Godot-JVM: An Exception has occurred!"

If that's not the case, this is a bug and I would be more than happy if it should occur, you file a separate issue so we can track it :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants