Skip to content

Commit 413eeb1

Browse files
piierthoCedNaruchippmann
authored
feat: Move from KSP to classgraph for registration code and initial Scala support (#761)
* feat: Add java and kotlin parser to get fqdn from source code * feat: remove script source file path from KtClass and registration. * feat: First draft of ClassGraph symbol processor * enh(entry-gen): Disallow multiple constructors with same argument count and simplify constructor registration so we get rid of nullable info need * feat(entry-gen): Manage type parameters with classgraph * feat(classgraph): get signal parameter names from annotation * feat(classgraph): generate registrars using classgraph * feat: configure gradle plugin to use classgraph instead of KSP and first steps in scala support * feat: Add update of gdj files * feat: Add support of scala source script in engine * feat: set scala version configurable from gradle plugin * chore: remove classes to test parser * feat: Remove support for registering constructors with arguments * feat: Remove multi args constructor constraints and simplify code according to this modification * chore: update scala implementation code to godot 4.4 and add scala call test * chore: rename fqdn to fqName * enh: fail on errors at the end of classgraph process * doc: Add scala support to documentation * enh: Add context on classgraph errors * chore: regenerate api with godot kotlin json --------- Co-authored-by: Ced Naru <cednaru.dev@gmail.com> Co-authored-by: chippmann <cedric@hippmann.ch>
1 parent d93c76f commit 413eeb1

File tree

476 files changed

+3421
-1841
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

476 files changed

+3421
-1841
lines changed

docs/src/doc/contribution/knowledge-base/entry-generation.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
To make it more language agnostic, the entry generator provides a set of model classes which represent the needed information about the source code by the entry genenerator.
66
It expects this information to be gathered and assembled by the calling tool and to be provided in the entry point of the entry generator.
77

8-
For Kotlin and Java, this tool is `godot-kotlin-symbol-processor` (a Kotlin compiler's plugin), which analyses the source code, and gathers the information needed by the entry generator.
8+
For Kotlin, Java and Scala, this tool is `godot-class-graph-symbol-processor`, which analyses the byte code, and gathers the information needed by the entry generator.
99
It then calls the entry generator which in turn generates the needed entry files.
1010

1111
## The godot-kotlin-symbol-processor

docs/src/doc/index.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ The items in this list are explicitly mentioned here as these will be implemente
3636
Also consider the [API Differences](user-guide/api-differences.md) section for general differences
3737
and limitations which will not be or cannot be adressed in the near forseable future or ever.
3838

39-
- Each registered constructor must have a unique number of arguments, constructor overloading is not yet supported.
39+
- Only a default no arg constructor can be registered.
4040
- No tool mode (you can set it already in the `@RegisterClass` annotation but it has no effect yet).
4141
- No addon support, you cannot use Godot Kotlin/JVM to write plugins and addons yet (you can however [write libraries](develop-libraries/introduction.md) with godot specific code).
4242
- Web is currently not supported. See [Supported platforms](#supported-platforms) to see what platforms we currently support
@@ -48,7 +48,7 @@ If you don't have Discord or you don't want to use it, please file an issue on G
4848

4949
## Supported languages
5050

51-
The main language supported is Kotlin. We do however support Java experimentally. It should be possible to support other JVM-based languages as well but this is not the focus of this project. If you want to have support for other languages, have a look at [support for other JVM-based languages](contribution/support-for-other-jvm-based-languages.md).
51+
The main language supported is Kotlin. We do however support Java and Scala experimentally. It should be possible to support other JVM-based languages as well but this is not the focus of this project. If you want to have support for other languages, have a look at [support for other JVM-based languages](contribution/support-for-other-jvm-based-languages.md).
5252

5353
## Supported platforms
5454

@@ -85,7 +85,7 @@ Contrary to the official binaries, there are two builds of the editor per Platfo
8585
`release` editors are the editors you use normally. `debug` editors provide debug symbols and are intended to provide better stacktraces in case of crashes of the editor. Please use those when submitting bugreports.
8686

8787
!!! warning
88-
This module will NOT work with the official Godot Editor and Export Templates! To be able to use Kotlin and Java scripts in Godot, you need our Editor and Export Templates builds.
88+
This module will NOT work with the official Godot Editor and Export Templates! To be able to use Kotlin, Java and Scala scripts in Godot, you need our Editor and Export Templates builds.
8989

9090
## Developer discussion
9191

docs/src/doc/user-guide/advanced/cleanup-operations.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
When running Kotlin/Java code, the JVM is embedded and managed directly by Godot, which offer little control when the game is closing.
1+
When running Kotlin/Java/Scala code, the JVM is embedded and managed directly by Godot, which offer little control when the game is closing.
22
If you use third-party library that needs resources to be freed/saved or threads to be closed.
33
To that end, we provide a simple method that allows you to register callbacks that will be called when the JVM is shutdown.
44

docs/src/doc/user-guide/api-differences.md

+19-4
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ Godot Kotlin/JVM offers two different ways to attach scripts:
55
- Source files
66
- Registration files.
77

8-
## Source files .kt and .java
8+
## Source files .kt, .java and .scala
99

10-
Just like you would do with GDScript, you can directly attach your Kotlin/Java files to Nodes as scripts.
10+
Just like you would do with GDScript, you can directly attach your Kotlin/Java/Scala files to Nodes as scripts.
1111
This is the most straightforward method to use Kotlin scripts but not the most flexible.
1212

1313
The limitations are the following:
@@ -20,7 +20,7 @@ The limitations are the following:
2020
var test_script: MyScript = load("res://pathToScript/MyScript.kt").new() // Wrong
2121
var test_script: Node = load("res://pathToScript/MyScript.kt").new() // Correct
2222
```
23-
The same applies if you use a Godot object with a .kt/.java attached to it
23+
The same applies if you use a Godot object with a .kt/.java/.scala attached to it
2424

2525
If those limitations don't apply to you, feel free to use Kotlin source files directly.
2626
@@ -32,7 +32,7 @@ They have several benefits over source files:
3232
- .gdj can be placed wherever you want in your Godot project, you are not limited to the source set.
3333
- Each script get its own .gdj. This includes scripts in different modules and libraries.
3434
- If a source file contains several scripts. A different .gdj will be generated for each.
35-
- Registration files are language agnostic, they are generated for Kotlin and Java files with no difference.
35+
- Registration files are language agnostic, they are generated for Kotlin, Java and Scala files with no difference.
3636
- When creating a script from code using its registered name. The module is going to use the registration file as the script. Therefore, registration files are treated as the default way to use scripts inside the module.
3737
3838
By default, these files are generated into a folder called `gdj` in the root of your project.
@@ -186,6 +186,14 @@ This kind of operation can be costly so we provide extension functions which cac
186186
```
187187
///
188188
189+
/// tab | Scala
190+
```scala
191+
val stringName = StringNames.asCachedStringName("myString")
192+
val nodePath = NodePaths.asCachedNodePath("myNode/myChildNode")
193+
val snakeCaseStringName = StringNames.toGodotName("myString")
194+
```
195+
///
196+
189197
You can also use the non-cached version of them if you simply want ease of conversion:
190198
191199
/// tab | Kotlin
@@ -202,6 +210,13 @@ You can also use the non-cached version of them if you simply want ease of conve
202210
```
203211
///
204212
213+
/// tab | Scala
214+
```scala
215+
val stringName = StringNames.asStringName("myString")
216+
val nodePath = NodePaths.asNodePath("myNode/myChildNode")
217+
```
218+
///
219+
205220
206221
## Logging
207222

docs/src/doc/user-guide/classes.md

+5-15
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,9 @@ This also works for any type you define.
139139

140140
## Constructors
141141

142-
Godot requires you to have a default constructor on your classes.
143-
You can define additional constructors but you have to register them by annothing them with `@RegisterConstructor`.
144-
Default constructors, on the other hand, are always registered automatically.
145-
146-
Constructors can also have **a maximum of 8 arguments** and must have a unique argument count as constructor overloading is not yet supported.
147-
This limitation is only for registered constructors.
142+
Godot requires you to have a default constructor on your classes. These are automatically registered for you.
143+
Registering constructor with arguments is currently not supported. But you can freely use them from Kotlin/Java/Scala
144+
just not from GDScript or any other non Godot Kotlin/JVM language.
148145

149146
### Instantiate Kotlin script classes in GDScript
150147

@@ -154,15 +151,8 @@ From GDScript it is possible to create an instance of a Kotlin class using the d
154151
var instance := YourKotlinClass.new()
155152
```
156153

157-
Additional constructors must use the `load` function:
158-
159-
```kt
160-
var instance := load("res://gdj/YourClass.gdj").new(oneArg, anotherArg)
161-
```
162-
163-
!!! info
164-
The limitation of max 16 arguments for constructors is arbitrary. We decided to introduce this limitation to prevent performance bottlenecks for creating objects as each argument passed to a constructor needs to be unpacked by the binding. The more arguments, the more unpacking is needed which means more overhead.
165-
154+
Using other constructors is not possible. Only the default no arg constructor is registered.
155+
But you can create the object and set the required properties after instantiation.
166156

167157
## Customization
168158

docs/src/doc/user-guide/signals_and_callables.md

+56
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ public MyScript extends Node {
3535
```
3636
///
3737

38+
/// tab | Scala
39+
```scala
40+
@RegisterClass
41+
class MyScript extends Node {
42+
@RegisterSignal("reverse")
43+
val mySignal: Signal1[Boolean] = Signal1.create(this, "mySignal") // Only one way to do it in Scala.
44+
}
45+
```
46+
///
47+
3848
!!! warning Signal parameter count
3949
In GDScript, signals can have any number of arguments, this is not possible in Kotlin as it is a statically typed language.
4050
At the moment, you can create signals and expose them to Godot up to 16 parameters.
@@ -55,6 +65,12 @@ reverseChanged.emit(false);
5565
```
5666
///
5767

68+
/// tab | Scala
69+
```scala
70+
reverseChanged.emit(false)
71+
```
72+
///
73+
5874
## Callables
5975

6076
You can use a classic Callable referencing a Godot Object and one of its method or conveniently use to avoid to creating a separate function.
@@ -80,6 +96,19 @@ You can use a classic Callable referencing a Godot Object and one of its method
8096
```
8197
///
8298

99+
/// tab | Scala
100+
```scala
101+
val regularCallable = Callable.create(this, "myMethod".toGodotName())
102+
val customCallable = LambdaCallable1.create(
103+
classOf[Void],
104+
classOf[String],
105+
(string: String) => {
106+
println(string);
107+
}
108+
)
109+
```
110+
///
111+
83112
### Signals and Callables together
84113

85114
Kotlin and Java already have ways to pass functions around as References.
@@ -143,6 +172,33 @@ public class AnotherObject extends Object {
143172
```
144173
///
145174

175+
/// tab | Scala
176+
```scala
177+
@RegisterClass
178+
class SomeObject extends Object {
179+
@RegisterFunction
180+
def onReverseChanged(boolean reverse): Unit = {
181+
println(s"Value of reverse has changed: $reverse")
182+
}
183+
}
184+
185+
@RegisterClass
186+
class AnotherObject extends Object {
187+
@RegisterSignal("reverse")
188+
val mySignal: Signal1[Boolean] = Signal1.create(this, "mySignal")
189+
190+
private val targetObject = new SomeObject()
191+
192+
public AnotherObject() {
193+
// Here are 3 different ways to connect a signal to a registered method. The method reference syntax is not implemented for Scala.
194+
mySignal.connect(Callable.create(targetObject, StringNames.toGodotName("onReverseChanged"))) // The recommanded way.
195+
mySignal.connect(Callable.create(targetObject, "on_reverse_changed")) // Unsafe, try to use snake_case in your code as least as possible.
196+
connect("my_signal", Callable.create(targetObject, "on_reverse_changed")) // Really, don't do that.
197+
}
198+
}
199+
```
200+
///
201+
146202
You can also use Kotlin lambdas directly to subscribe to signals
147203

148204
```kt

harness/tests/FreeformRegistrationFileTestClass.gdj

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33

44
registeredName = FreeformRegistrationFileTestClass
55
fqName = godot.tests.freeform.FreeformRegistrationFileTestClass
6-
relativeSourcePath = src/main/kotlin/godot/tests/freeform/FreeformRegistrationFileTestClass.kt
76
baseType = Node
87
supertypes = [
98
godot.api.Node,
109
godot.api.Object,
1110
godot.core.KtObject,
1211
godot.common.interop.NativeWrapper,
13-
godot.common.interop.NativePointer,
14-
kotlin.Any
12+
godot.common.interop.NativePointer
1513
]
1614
signals = [
1715

harness/tests/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ godot {
3737

3838
// uncomment to test ios
3939
// isIOSExportEnabled.set(true)
40+
4041
}
4142

4243
dependencies {

harness/tests/project.godot

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ config_version=5
1212

1313
config/name="Godot Kotlin Tests"
1414
run/main_scene="res://test/GutTests.tscn"
15-
config/features=PackedStringArray("4.4")
15+
config/features=PackedStringArray("4.3")
1616
config/icon="res://icon.png"
1717

1818
[debug]

harness/tests/scala_test_scene.tscn

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[gd_scene load_steps=2 format=3 uid="uid://c27pq6lr7xver"]
2+
3+
[ext_resource type="Script" path="res://scripts/godot/tests/ScalaTestClass.gdj" id="1_ls5ji"]
4+
5+
[node name="ScalaTestScene" type="Node3D"]
6+
script = ExtResource("1_ls5ji")
7+
8+
[node name="Button" type="Button" parent="."]
9+
offset_right = 8.0
10+
offset_bottom = 8.0

harness/tests/scripts/CopyModificationCheckTestClass.gdj

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,14 @@
33

44
registeredName = CopyModificationCheckTestClass
55
fqName = CopyModificationCheckTestClass
6-
relativeSourcePath = otherSourceDir/CopyModificationCheckTestClass.kt
76
baseType = Node3D
87
supertypes = [
98
godot.api.Node3D,
109
godot.api.Node,
1110
godot.api.Object,
1211
godot.core.KtObject,
1312
godot.common.interop.NativeWrapper,
14-
godot.common.interop.NativePointer,
15-
kotlin.Any
13+
godot.common.interop.NativePointer
1614
]
1715
signals = [
1816

harness/tests/scripts/CoreTypePropertyChecks.gdj

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33

44
registeredName = CoreTypePropertyChecks
55
fqName = CoreTypePropertyChecks
6-
relativeSourcePath = otherSourceDir/CoreTypePropertyChecks.kt
76
baseType = Node
87
supertypes = [
98
godot.api.Node,
109
godot.api.Object,
1110
godot.core.KtObject,
1211
godot.common.interop.NativeWrapper,
13-
godot.common.interop.NativePointer,
14-
kotlin.Any
12+
godot.common.interop.NativePointer
1513
]
1614
signals = [
1715

harness/tests/scripts/ScriptInOtherSourceDir.gdj

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33

44
registeredName = ScriptInOtherSourceDir
55
fqName = ScriptInOtherSourceDir
6-
relativeSourcePath = otherSourceDir/ScriptInOtherSourceDir.kt
76
baseType = Node
87
supertypes = [
98
godot.api.Node,
109
godot.api.Object,
1110
godot.core.KtObject,
1211
godot.common.interop.NativeWrapper,
13-
godot.common.interop.NativePointer,
14-
kotlin.Any
12+
godot.common.interop.NativePointer
1513
]
1614
signals = [
1715

harness/tests/scripts/dependencies/flattened-library-tests/FLSimple.gdj

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,14 @@
33

44
registeredName = FLSimple
55
fqName = godot.tests.library.flattened.Simple
6-
relativeSourcePath = src/main/kotlin/godot/tests/library/flattened/Simple.kt
76
baseType = Node3D
87
supertypes = [
98
godot.api.Node3D,
109
godot.api.Node,
1110
godot.api.Object,
1211
godot.core.KtObject,
1312
godot.common.interop.NativeWrapper,
14-
godot.common.interop.NativePointer,
15-
kotlin.Any
13+
godot.common.interop.NativePointer
1614
]
1715
signals = [
1816

harness/tests/scripts/dependencies/fqname-library-tests/godot/tests/library/fqname/godot_tests_library_fqname_FQNLSimple.gdj

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,14 @@
33

44
registeredName = godot_tests_library_fqname_FQNLSimple
55
fqName = godot.tests.library.fqname.Simple
6-
relativeSourcePath = src/main/kotlin/godot/tests/library/fqname/Simple.kt
76
baseType = Node3D
87
supertypes = [
98
godot.api.Node3D,
109
godot.api.Node,
1110
godot.api.Object,
1211
godot.core.KtObject,
1312
godot.common.interop.NativeWrapper,
14-
godot.common.interop.NativePointer,
15-
kotlin.Any
13+
godot.common.interop.NativePointer
1614
]
1715
signals = [
1816

harness/tests/scripts/dependencies/fqname-library-tests/godot/tests/library/fqname/godot_tests_library_fqname_FQNLSimpleChild.gdj

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
registeredName = godot_tests_library_fqname_FQNLSimpleChild
55
fqName = godot.tests.library.fqname.SimpleChild
6-
relativeSourcePath = src/main/kotlin/godot/tests/library/fqname/SimpleChild.kt
76
baseType = Node3D
87
supertypes = [
98
godot.tests.library.fqname.Simple,
@@ -12,8 +11,7 @@ supertypes = [
1211
godot.api.Object,
1312
godot.core.KtObject,
1413
godot.common.interop.NativeWrapper,
15-
godot.common.interop.NativePointer,
16-
kotlin.Any
14+
godot.common.interop.NativePointer
1715
]
1816
signals = [
1917

harness/tests/scripts/dependencies/hierarchical-library-tests/godot/tests/library/hierarchical/HLSimple.gdj

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,14 @@
33

44
registeredName = HLSimple
55
fqName = godot.tests.library.hierarchical.Simple
6-
relativeSourcePath = src/main/kotlin/godot/tests/library/hierarchical/Simple.kt
76
baseType = Node3D
87
supertypes = [
98
godot.api.Node3D,
109
godot.api.Node,
1110
godot.api.Object,
1211
godot.core.KtObject,
1312
godot.common.interop.NativeWrapper,
14-
godot.common.interop.NativePointer,
15-
kotlin.Any
13+
godot.common.interop.NativePointer
1614
]
1715
signals = [
1816

0 commit comments

Comments
 (0)