-
Notifications
You must be signed in to change notification settings - Fork 52
Proposal: Moving from Script API to GDExtension #799
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
Comments
It's not all perfect.
It's certainly possible to reimplement those feature to some extent manually with a few editor plugins, but it would be more complex than just overriding a few virtual methods from the Language class than today. The source change is particularly nasty, the Godot editor only refreshes imported resource (which scrips are not), scripts global names and already loaded resources when a file is changed. |
We might see improvements on those points in the near future if Godot takes the direction mentioned in this discussion. In the long term, we might have no other choice than using the extension system, so it makes the choice easier. |
Uh oh!
There was an error while loading. Please reload this page.
Overview
This proposal outlines a major architectural shift for the Godot Kotlin project: transitioning from the current use of the Godot Script API to leveraging the more modern and flexible GDExtension API. This change would transform our Kotlin/JVM integration from a script-based system into an extension-based one, aligning better with the design philosophy of the Godot engine and delivering a more streamlined and maintainable user experience.
Currently, Godot Kotlin is implemented as a Godot module that exports Kotlin classes as scripts. This allows users to attach Kotlin classes to nodes in the editor, similar to GDScript or C#. While functional, this model introduces several constraints and maintenance burdens:
The .gdj system was created as a workaround to provide "scripts" support for classes that don’t come from an existing file in the Godot project but directly from the jar (when coming from an imported library for example)—but in hindsight, this is a hack built on top of a file-centric system.
Scripts do retain some advantages—such as the ability to dynamically switch scripts on a node at runtime—but these are features we do not actively use, and they come at the cost of complexity and performance overhead.
Why Consider Extensions Now?
As Godot's GDExtension API has evolved, it now provides powerful new capabilities that make it a compelling alternative for our use case. Several of its limitations have been fixed since the original release of Godot 4 and there is no obvious drawback to using this approach.
Dynamic Type Registration
Previously, GDExtension required individual function pointers for every method in an extended type. This made it incompatible with dynamic JVM-based systems where script types are discovered at runtime when opening the .jar. The alternative before that changes would be to modify the entry generator to a C++ output and then compile the dynamic library again, which is not an acceptable workflow for JVM devs.
Recent versions of the GDExtension API now allow passing custom data alongside the function pointer. We can use that custom data to dynamically find the correct JNI method to call, just like we currently do with scripts.
Extensions not running in the editor
By default, extensions are all running in the editor, just like regular nodes. There was previously no way for extension to act like "non-tool scripts" (except checking for the editor hint in every single implemented virtual method), but it's no longer the case (only difference is that extension are "tools" by default, but you can opt out). It means we automatically get "Tool mode" support. This also means getting reloading of our "tool scripts" with state restoration because the editor can also handle that (But I think it that so far it can only reload the entire dynamic library, meaning all the jars at once in our case, with no more fine-grained control over that reloading).
Removing complexity
Making a complete move to extension removes the need for any custom build of Godot. Just adding the extension to a project would be enough to dynamically turn any jar into a set of extended types.
It will also remove all the complexity we have built over the years regarding file management:
From Godot’s point of view, the JVM classes just become a built-in engine type—completely language-agnostic.
It goes well with our new ClassGraph implementation. The idea is that any KtClass found in the .jar can become an extended type. It doesn't matter if it was written in Java, Kotlin, Scala and where the source file was initially located. As long as it is present in the jar, it's enough.
Performance Consideration
There has always been some concern about potential performance loss when moving to an extension-based system, given the thin overhead of using a dynamic library instead of statically linked code. While we won’t know for certain until we benchmark it, current investigation suggests the performance impact would be minimal or even negligible in most cases:
In practice, this overhead is insignificant. Core types like VariantArray, Dictionary, and PackedArray are already bottlenecked by JNI overhead. Others, like StringName or NodePath, are cached or used infrequently. For the rest, most types are fully implemented in Kotlin and don’t cross the JNI boundary at all.
Why This Makes Sense for Godot Kotlin
In reality, our current architecture already behaves more like an extension system than a script system:
The current implementation has required workarounds to fit into a system designed for file-based, interpreted code.
Switching to GDExtension would bring our architecture in line with how we actually operate, reduce technical debt, and provide a cleaner, more scalable foundation for the future.
Conclusion
Initially, the plan was to keep using the Script system—both as a module or an extension. The original vision was to reimplement our current Language/Script classes using GDExtension, but still export user classes as scripts.
However, in the interest of reducing maintenance burden, supporting all major JVM languages cleanly, and keeping the project’s scope manageable, I’ve come to believe that we should fully embrace the extension model.
If we adopt this proposal, our goal will be to shift to a dynamic library that converts exported KtClass instances into proper extended types via a smaller C++ layer.
The text was updated successfully, but these errors were encountered: