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

Refine registration annotation handling #727

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 2 additions & 1 deletion .github/workflows/trigger_dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,5 @@ jobs:
- build-linux
with:
godot-version: ${{ needs.setup-build-variables.outputs['godot-version'] }}
jvm-version: ${{ needs.setup-build-variables.outputs['jvm-version'] }}
jvm-version: ${{ needs.setup-build-variables.outputs['jvm-version'] }}
build-version: ${{ needs.setup-build-variables.outputs['build-version'] }}
8 changes: 3 additions & 5 deletions docs/src/doc/getting-started/your-first-class.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,19 @@ Let's create a file `src/main/kotlin/com/yourcompany/game/Simple.kt` with the fo
package com.yourcompany.game

import godot.Node3D
import godot.annotation.RegisterClass
import godot.annotation.RegisterFunction
import godot.annotation.GodotScript
import godot.global.GD

@RegisterClass
@GodotScript
class Simple: Node3D() {

@RegisterFunction
override fun _ready() {
GD.print("Hello world!")
}
}
```

The [classes](../user-guide/classes.md) section covers in details what we did here, but for now `@RegisterClass` will register the class to Godot. Now we can trigger a build.
The [classes](../user-guide/classes.md) section covers in details what we did here, but for now `@GodotScript` will register the class to Godot. Now we can trigger a build.

```shell
./gradlew build
Expand Down
2 changes: 1 addition & 1 deletion docs/src/doc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Also consider the [API Differences](user-guide/api-differences.md) section for g
and limitations which will not be or cannot be adressed in the near forseable future or ever.

- Each registered constructor must have a unique number of arguments, constructor overloading is not yet supported.
- No tool mode (you can set it already in the `@RegisterClass` annotation but it has no effect yet).
- No tool mode (you can already use the `@Tool` annotation, but it has no effect yet).
- 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).
- Web is currently not supported. See [Supported platforms](#supported-platforms) to see what platforms we currently support

Expand Down
17 changes: 6 additions & 11 deletions docs/src/doc/user-guide/advanced/abstract-classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ You can define a abstract class and register it's members the same way as you do
Under the hood, we only register your normal classes, and let them register all members your abstract class defines.

!!! info
For this reason, the `@RegisterClass` annotation is optional for abstract classes.
For this reason, the `@GodotScript` annotation is optional for abstract classes.

!!! warning
As in Kotlin, you cannot instantiate abstract classes directly from any other scripting language like GDScript! In fact, godot does not even know (or care) that your abstract class exists.
Expand All @@ -17,37 +17,32 @@ Under the hood, we only register your normal classes, and let them register all
Abstract class definition:

```kotlin
// register class annotation is optional for abstract classes
// @GodotScript annotation is optional for abstract classes
abstract class AbstractClassInheritanceParent: Node() {

@Export
@RegisterProperty
var registeredExportedPropertyInAbstractClass = false

@RegisterSignal
val signalInAbstractClass by signal<String>("blubb")

@RegisterFunction
@GodotMember
fun functionInAbstractClassWithDefaultImplementation() {
// some implementation
}

@RegisterFunction
@GodotMember
abstract fun abstractFunction()
}
```

Child class definition:

```kotlin
@RegisterClass
@GodotScript
class AbstractClassInheritanceChild: AbstractClassInheritanceParent() {
@RegisterFunction
// registered automatically as the abstract class already defines the annotation
override fun abstractFunction() {
// some implementation
}
}
```

!!! warning "Registration of overridden members"
As you can see in the example; you need to explicitly register any member in the child class which you override from the abstract parent class. Otherwise they will not be registered and thus are not known to godot.
3 changes: 1 addition & 2 deletions docs/src/doc/user-guide/api-differences.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,11 @@ You can implement [_notification](https://docs.godotengine.org/en/stable/classes
and have class hierarchy notification call without using `super` call, as in GDScript and C++. However, the syntax is a bit different:

```kotlin
@RegisterFunction
override fun _notification() = godotNotification {
// ...
}
```
Currently this feature except abstract classes.
Currently, this feature does not work for abstract classes.

## StringName and NodePath

Expand Down
26 changes: 12 additions & 14 deletions docs/src/doc/user-guide/classes.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
To expose a class written in Kotlin it needs to extend `godot.Object` (or any of its subtype) and must be annotated with `@RegisterClass`.
To expose a class written in Kotlin it needs to extend `godot.Object` (or any of its subtype) and must be annotated with `@GodotScript`.

```kt
@RegisterClass
@GodotScript
class RotatingCube: Node3D() {
// ...
}
Expand All @@ -13,7 +13,7 @@ Each registered classes will generate its own .gdj files. For more information,

Classes need to be registered with a unique name as Godot does not support namespaces (or packages in this case) for script classes.

By default, we register your classes with the name you give them. While beign a simple approach and enough in most cases,
By default, we register your classes with the name you give them. While being a simple approach and enough in most cases,
this can lead to naming conflicts if you have classes in different packages with the same name. For example:

- `com.package.a.MyClass`
Expand All @@ -25,7 +25,7 @@ So you are responsible for making sure that classes have a unique name.
We do however provide you with some assistance:

- We have compile time checks in place which should let the *build fail* if classes would end up having the same name.
- The `@RegisterClass` annotation lets you define a custom registration name: `@RegisterClass("CustomRegistrationName")`.
- The `@GodotScript` annotation lets you define a custom registration name: `@GodotScript("CustomRegistrationName")`.
- Register the class names with the fully qualified name: `com.mygame.MyClass` will be registered as: `com_mygame_MyClass`. This can be configured with:
```kotlin
godot {
Expand Down Expand Up @@ -93,7 +93,7 @@ If you want to be notified when initialization and destruction of your class' in
and override the `_onDestroy` function respectively.

```kt
@RegisterClass
@GodotScript
class RotatingCube: Node3D() {
init {
println("Initializing RotatingCube!")
Expand All @@ -110,7 +110,6 @@ class RotatingCube: Node3D() {
Checking if an object is an instance of a particular type can be done via the `is` operator.

```kt
@RegisterFunction
override fun _ready() {
val parent = getParent()
if (parent is CollisionShape) {
Expand All @@ -128,7 +127,6 @@ This also works for any type you define.
If you are sure that an object is always an instance of some type, then you can take advantage of Kotlin's [contracts](https://kotlinlang.org/docs/reference/whatsnew13.html#contracts) feature. This allows you to avoid having nested `if`s.

```kt
@RegisterFunction
override fun _ready() {
val parent = getParent()
require(parent is CollisionShape)
Expand All @@ -140,15 +138,15 @@ This also works for any type you define.
## Constructors

Godot requires you to have a default constructor on your classes.
You can define additional constructors but you have to register them by annothing them with `@RegisterConstructor`.
You can define additional constructors, but you have to register them by annotating them with `@GodotMember`.
Default constructors, on the other hand, are always registered automatically.

Constructors can also have **a maximum of 8 arguments** and must have a unique argument count as constructor overloading is not yet supported.
This limitation is only for registered constructors.

### Instantiate Kotlin script classes in GDScript

From GDScript it is possible to create an instance of a Kotlin class using the default constructor:
From GDScript, it is possible to create an instance of a Kotlin class using the default constructor:

```kt
var instance := YourKotlinClass.new()
Expand All @@ -161,24 +159,24 @@ var instance := load("res://gdj/YourClass.gdj").new(oneArg, anotherArg)
```

!!! info
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.
The limitation of max 8 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.


## Customization

You can customize to some extent how your class should be registered in Godot.

The `@RegisterClass` annotation takes only one argument:
The `@GodotScript` annotation takes only one argument:

- **className**: If set, the class will be registered with the provided name.
- **customName**: If set, the class will be registered with the provided name.

!!! warning "Unique class names"
If you specify the `className` in the annotation, you have to make sure that this name is unique! We implemented compilation checks to make sure the compilation fails if more than two classes are registered with the same name, but we cannot check class names from other scripting languages like GDScript or C#! It is also recommended installing our intellij plugin as it shows duplicated registered class names in the editor as an error.
If you specify the `customName` in the annotation, you have to make sure that this name is unique! We implemented compilation checks to make sure the compilation fails if more than two classes are registered with the same name, but we cannot check class names from other scripting languages like GDScript or C#! It is also recommended installing our intellij plugin as it shows duplicated registered class names in the editor as an error.


## Tool Mode

Annotate your class with `@Tool` to make it a tool class (note that `@RegisterClass` is required for this annotation to take effect).
Annotate your class with `@Tool` to make it a tool class.

!!! Caution
This is currently not implemented.
20 changes: 7 additions & 13 deletions docs/src/doc/user-guide/functions.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
Any Kotlin function can be registered as long as its parameters and return type can be converted to a `Variant`.
To register a function annotate it with `@RegisterFunction`.
To register a function annotate it with `@GodotMember`.

```kotlin
@RegisterClass
@GodotScript
class RotatingCube: Node3D() {
@RegisterFunction
override fun _ready() {
println("I am ready!")
@GodotMember
fun myFunction() {
println("Hello")
}
}
```
Expand All @@ -21,17 +21,11 @@ Therefore, a function called `doSomething()` in Kotlin is usable in GDScript as

Virtual functions (like `_ready`, `_process` and `_physics_process`) are declared as overridable functions.
The default implementation throws a `NotImplementedException`, so you have to override it if you plan to expose
a virtual function to Godot. Remember, just overriding is not enough to use that function - you have to explicitly
register it as well with `@RegisterFunction`.
a virtual function to Godot. Virtual functions are registered automatically and do not require an explicit `@GodotMember`
annotation.

## Arguments count

Godot limits the allowed argument count of functions to `16`. Thus, this binding also has this limitation.
If you want to pass more than 16 parameters in a function, you need to wrap them in a container
(like a custom container class or a `VariantArray` or `Dictionary`).

## Customization

You can customize to some extent how your functions should be registered in Godot. The `@RegisterFunction` annotation takes one argument:

- **rpcMode**: Default: `RPCMode.DISABLED`
18 changes: 8 additions & 10 deletions docs/src/doc/user-guide/properties.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
Any property of a registered class can be registered as long as it is public, mutable and can be converted to a `Variant`.
To register a property annotate it with `@RegisterProperty`.
To register a property annotate it with `@GodotMember`.

```kotlin
@RegisterClass
@GodotScript
class RotatingCube: Node3D() {
@RegisterProperty
@GodotMember
var someString: String = "Hello there :-)"

@RegisterProperty
@GodotMember
var propertyWithDefaultValue: Float = 2f
}
```
Expand All @@ -19,32 +19,30 @@ your properties are actually registered as `snake_case`. So a property `someFlag

## Core type specifics

Godot core type always need to have a value. Hence you cannot register properties of core types (like `Vector3`) with lateinit.
Godot core type always need to have a value. Hence, you cannot register properties of core types (like `Vector3`) with lateinit.

## Exporting properties

A registered property can be exported (a.k.a make it visible in the Godot editor) by annotating it with `@Export`.
A property can be exported (a.k.a. make it visible in the Godot editor) by annotating it with `@Export`.
A property can be exported if it is a core type, a primitive or inherits from `godot.RefCounted`.

```kotlin
@RegisterClass
@GodotMember
class RotatingCube: Node3D() {
@Export
@RegisterProperty
var speed: Float = 2f
}
```

Exported properties can have default values (`2f` in the example above) which will be used as a default value by the `inspector`.
A default value can **only** contain compile time constants and only references to compile time constants.

!!! danger
If you set a default value in code and a different value in the `inspector` the value of the latter will override the value in code after `init` and before `_enter_tree`.

## Type hint registration

This module provides a plethora of annotations for defining property type hints.
These annotations controls how Godot display the property in the inspector.
These annotations control how Godot display the property in the inspector.
Each property hint annotation can only be added to certain types of properties.
Using the wrong annotation will make the compilation fail. These will only take effect if the property is exported.

Expand Down
21 changes: 8 additions & 13 deletions docs/src/doc/user-guide/signals_and_callables.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,19 @@ In both case, you have to provide the name of the signal parameters as strings f

/// tab | Kotlin
```kotlin
@RegisterClass
@GodotScript
class MyScript: Node() {
@RegisterSignal
val mySignal by signal1<Boolean>("reverse")

@RegisterSignal
val mySignal = Signal1<Boolean>("mySignal", "reverse")
}
```
///

/// tab | Java
```java
@RegisterClass
@GodotScript
public MyScript extends Node {
@RegisterSignal
public Signal1<Boolean> mySignal = Signal1.create(this, "mySignal", "reverse"); // Only one way to do it in Java.
}
```
Expand Down Expand Up @@ -90,17 +87,16 @@ Note that the connected method has to be a registered to Godot.

/// tab | Kotlin
```kotlin
@RegisterClass
@GodotScript
class SomeObject: Object() {
@RegisterFunction
@GodotMember
fun onReverseChanged(reverse: Boolean) {
println("Value of reverse has changed: $reverse")
}
}

@RegisterClass
@GodotScript
class AnotherObject: Object() {
@RegisterSignal
val mySignal by signal1<Boolean>("reverse")

private val targetObject = SomeObject()
Expand All @@ -118,17 +114,16 @@ class AnotherObject: Object() {

/// tab | Java
```java
@RegisterClass
@GodotScript
public class SomeObject extends Object {
@RegisterFunction
@GodotMember
public void onReverseChanged(boolean reverse) {
System.out.println("Value of reverse has changed: " + reverse);
}
}

@RegisterClass
@GodotScript
public class AnotherObject extends Object {
@RegisterSignal
public Signal1<Boolean> mySignal = Signal1.create(this, "mySignal", "reverse");

private SomeObject targetObject = new SomeObject();
Expand Down
Loading
Loading