godot.core.Variant.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of godot-library-debug Show documentation
Show all versions of godot-library-debug Show documentation
Contains godot api as kotlin classes and jvm cpp interaction code.
package godot.core
import godot.Object
import godot.core.memory.MemoryManager
import godot.util.nullptr
import godot.util.toRealT
import java.nio.ByteBuffer
private var ByteBuffer.bool: Boolean
get() = int == 1
set(value) {
putInt(if (value) 1 else 0)
}
private var ByteBuffer.vector2: Vector2
get() = Vector2(float.toRealT(), float.toRealT())
set(value) {
putFloat(value.x.toFloat())
putFloat(value.y.toFloat())
}
private var ByteBuffer.vector2i: Vector2i
get() = Vector2i(int, int)
set(value) {
putInt(value.x)
putInt(value.y)
}
private var ByteBuffer.vector3: Vector3
get() = Vector3(float.toRealT(), float.toRealT(), float.toRealT())
set(value) {
putFloat(value.x.toFloat())
putFloat(value.y.toFloat())
putFloat(value.z.toFloat())
}
private var ByteBuffer.vector3i: Vector3i
get() = Vector3i(int, int, int)
set(value) {
putInt(value.x)
putInt(value.y)
putInt(value.z)
}
private var ByteBuffer.vector4: Vector4
get() = Vector4(float.toRealT(), float.toRealT(), float.toRealT(), float.toRealT())
set(value) {
putFloat(value.x.toFloat())
putFloat(value.y.toFloat())
putFloat(value.z.toFloat())
putFloat(value.w.toFloat())
}
private var ByteBuffer.vector4i: Vector4i
get() = Vector4i(int, int, int, int)
set(value) {
putInt(value.x)
putInt(value.y)
putInt(value.z)
putInt(value.w)
}
private var ByteBuffer.basis: Basis
get() = Basis().also {
it._x = vector3
it._y = vector3
it._z = vector3
}
set(value) {
vector3 = value._x
vector3 = value._y
vector3 = value._z
}
private var ByteBuffer.stringName: Any
get() {
val ptr = long
return StringName(ptr)
}
set(value) {
toGodotNativeCoreType(this, value)
}
private var ByteBuffer.obj: KtObject?
get() {
val constructorIndex = int
val ptr = long
val id = long
if (ptr == nullptr) {
return null
}
return MemoryManager.getInstanceOrCreate(ptr, id, constructorIndex)
}
set(value) {
putLong(value?.rawPtr ?: nullptr)
}
private var ByteBuffer.variantType: Int
get() = int
set(value) {
putInt(value)
}
sealed interface VariantConverter {
val id: Int
fun toKotlin(buffer: ByteBuffer): Any?
fun toGodot(buffer: ByteBuffer, any: Any?)
}
enum class VariantParser(override val id: Int) : VariantConverter {
NIL(0) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = Unit
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {}
},
// atomic types
BOOL(1) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = buffer.bool
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Boolean)
buffer.bool = any
}
},
LONG(2) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = buffer.long
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Long)
buffer.putLong(any)
}
},
DOUBLE(3) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = buffer.double
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Double)
buffer.putDouble(any)
}
},
STRING(4) {
override fun toUnsafeKotlin(buffer: ByteBuffer): String {
val isLong = buffer.bool
return if (isLong) {
LongStringQueue.pollString()
} else {
/**
* A CString is read from the buffer, they all end with a 0 character except if the String is empty
* "" has a size of 0, but "a" has a size of 2.
* We only read the buffer if the size is superior to 0.
* When it's the case, we create a string without the last 0 character.
*/
val stringSize = buffer.int
if (stringSize == 0) {
String()
} else {
val charArray = ByteArray(stringSize)
buffer.get(charArray, 0, stringSize)
String(charArray, 0, stringSize - 1, Charsets.UTF_8)
}
}
}
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is String)
val stringBytes = any.encodeToByteArray()
if (stringBytes.size > LongStringQueue.stringMaxSize) {
buffer.bool = true
LongStringQueue.sendStringToCPP(any)
} else {
buffer.bool = false
buffer.putInt(stringBytes.size)
buffer.put(stringBytes)
}
}
},
// math types
VECTOR2(5) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = buffer.vector2
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Vector2)
buffer.vector2 = any
}
},
VECTOR2I(6) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = buffer.vector2i
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Vector2i)
buffer.vector2i = any
}
},
RECT2(7) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = Rect2(
buffer.vector2,
buffer.vector2
)
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Rect2)
buffer.vector2 = any._position
buffer.vector2 = any._size
}
},
RECT2I(8) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = Rect2i(
buffer.vector2i,
buffer.vector2i
)
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Rect2i)
buffer.vector2i = any._position
buffer.vector2i = any._size
}
},
VECTOR3(9) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = buffer.vector3
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Vector3)
buffer.vector3 = any
}
},
VECTOR3I(10) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = buffer.vector3i
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Vector3i)
buffer.vector3i = any
}
},
TRANSFORM2D(11) {
override fun toUnsafeKotlin(buffer: ByteBuffer): Transform2D {
val x = buffer.vector2
val y = buffer.vector2
val origin = buffer.vector2
return Transform2D(x, y, origin)
}
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Transform2D)
buffer.vector2 = any._x
buffer.vector2 = any._y
buffer.vector2 = any.origin
}
},
VECTOR4(12) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = buffer.vector4
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Vector4)
buffer.vector4 = any
}
},
VECTOR4I(13) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = buffer.vector4i
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Vector4i)
buffer.vector4i = any
}
},
PLANE(14) {
override fun toUnsafeKotlin(buffer: ByteBuffer): Plane {
val normal = buffer.vector3
val d = buffer.float.toRealT()
return Plane(normal, d)
}
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Plane)
buffer.vector3 = any._normal
buffer.putFloat(any.d.toFloat())
}
},
QUATERNION(15) {
override fun toUnsafeKotlin(buffer: ByteBuffer): Quaternion {
val x = buffer.float.toRealT()
val y = buffer.float.toRealT()
val z = buffer.float.toRealT()
val w = buffer.float.toRealT()
return Quaternion(x, y, z, w)
}
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Quaternion)
buffer.putFloat(any.x.toFloat())
buffer.putFloat(any.y.toFloat())
buffer.putFloat(any.z.toFloat())
buffer.putFloat(any.w.toFloat())
}
},
AABB(16) {
override fun toUnsafeKotlin(buffer: ByteBuffer): godot.core.AABB {
val position = buffer.vector3
val size = buffer.vector3
return AABB(position, size)
}
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is godot.core.AABB)
buffer.vector3 = any._position
buffer.vector3 = any._size
}
},
BASIS(17) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = buffer.basis
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Basis)
buffer.basis = any
}
},
TRANSFORM3D(18) {
override fun toUnsafeKotlin(buffer: ByteBuffer): Transform3D {
val basis = buffer.basis
val origin = buffer.vector3
return Transform3D(basis, origin)
}
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Transform3D)
buffer.basis = any._basis
buffer.vector3 = any._origin
}
},
PROJECTION(19) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = Projection(
buffer.vector4,
buffer.vector4,
buffer.vector4,
buffer.vector4
)
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Projection)
buffer.vector4 = any._x
buffer.vector4 = any._y
buffer.vector4 = any._z
buffer.vector4 = any._w
}
},
// misc types
COLOR(20) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = Color(buffer.float, buffer.float, buffer.float, buffer.float)
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Color)
buffer.putFloat(any.r.toFloat())
buffer.putFloat(any.g.toFloat())
buffer.putFloat(any.b.toFloat())
buffer.putFloat(any.a.toFloat())
}
},
STRING_NAME(21) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = buffer.stringName
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is StringName)
buffer.stringName = any
}
},
NODE_PATH(22) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = NodePath(buffer.long)
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) = toGodotNativeCoreType(buffer, any)
},
_RID(23) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = RID(buffer.long)
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is RID)
buffer.putLong(any.id)
}
},
OBJECT(24) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = buffer.obj
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is KtObject?)
buffer.obj = any
}
},
CALLABLE(25) {
override fun toUnsafeKotlin(buffer: ByteBuffer): Callable {
val ptr = buffer.long
return NativeCallable(ptr)
}
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
if (any is NativeCallable) {
buffer.bool = false
buffer.putLong(any._handle)
} else {
require(any is LambdaCallable<*>)
buffer.bool = true
buffer.putLong(any.wrapInCustomCallable())
}
}
},
SIGNAL(26) {
override fun toUnsafeKotlin(buffer: ByteBuffer): Signal {
val obj = buffer.obj
val name = buffer.stringName
require(obj is Object)
require(name is StringName)
return Signal(obj, name)
}
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) {
require(any is Signal)
buffer.obj = any.godotObject
buffer.stringName = any.name
}
},
DICTIONARY(27) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = Dictionary(buffer.long)
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) =
toGodotNativeCoreType>(buffer, any)
},
ARRAY(28) {
override fun toUnsafeKotlin(buffer: ByteBuffer) : VariantArray<*>{
val ptr = buffer.long
val type = buffer.long
// TODO: Use the type to create the correct VariantArray type. For now, we just use the less efficient but flexible Any type.
return VariantArray(ptr)
}
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) =
toGodotNativeCoreType>(buffer, any)
},
// PackedArray
PACKED_BYTE_ARRAY(29) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = PackedByteArray(buffer.long)
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) = toGodotNativeCoreType(buffer, any)
},
PACKED_INT_32_ARRAY(30) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = PackedInt32Array(buffer.long)
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) = toGodotNativeCoreType(buffer, any)
},
PACKED_INT_64_ARRAY(31) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = PackedInt64Array(buffer.long)
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) = toGodotNativeCoreType(buffer, any)
},
PACKED_FLOAT_32_ARRAY(32) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = PackedFloat32Array(buffer.long)
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) =
toGodotNativeCoreType(buffer, any)
},
PACKED_FLOAT_64_ARRAY(33) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = PackedFloat64Array(buffer.long)
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) =
toGodotNativeCoreType(buffer, any)
},
PACKED_STRING_ARRAY(34) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = PackedStringArray(buffer.long)
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) =
toGodotNativeCoreType(buffer, any)
},
PACKED_VECTOR2_ARRAY(35) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = PackedVector2Array(buffer.long)
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) =
toGodotNativeCoreType(buffer, any)
},
PACKED_VECTOR3_ARRAY(36) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = PackedVector3Array(buffer.long)
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) =
toGodotNativeCoreType(buffer, any)
},
PACKED_COLOR_ARRAY(37) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = PackedColorArray(buffer.long)
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) = toGodotNativeCoreType(buffer, any)
},
PACKED_VECTOR4_ARRAY(38) {
override fun toUnsafeKotlin(buffer: ByteBuffer) = PackedVector4Array(buffer.long)
override fun toUnsafeGodot(buffer: ByteBuffer, any: Any?) =
toGodotNativeCoreType(buffer, any)
};
override fun toKotlin(buffer: ByteBuffer): Any? {
val idInBuffer = buffer.variantType
if (idInBuffer == id) {
return toUnsafeKotlin(buffer)
} else if(id == OBJECT.id && idInBuffer == NIL.id) {
// Godot can sometimes send null pointer as NIL variant, so we need to test for that case.
return null
}
throw TypeCastException(
"Shared Buffer Error: JVM expected a ${this::class.simpleName} but received a ${
VariantParser.from(
idInBuffer.toLong()
)
}."
)
}
override fun toGodot(buffer: ByteBuffer, any: Any?) {
buffer.variantType = id
toUnsafeGodot(buffer, any)
}
abstract fun toUnsafeKotlin(buffer: ByteBuffer): Any?
abstract fun toUnsafeGodot(buffer: ByteBuffer, any: Any?)
companion object {
fun from(value: Long) = entries[value.toInt()]
}
}
//TODO: Unify VariantCaster with Meta in the API gen + use it in entry gen. Or maybe get rid of it and just have both with their own solution.
sealed class VariantCaster(val coreVariant: VariantParser) : VariantConverter {
override val id by coreVariant::id
sealed class VariantSimpleCaster(coreVariant: VariantParser) : VariantCaster(coreVariant) {
override fun toKotlin(buffer: ByteBuffer) = toKotlinCast(coreVariant.toKotlin(buffer))
override fun toGodot(buffer: ByteBuffer, any: Any?) = coreVariant.toGodot(buffer, toGodotCast(any))
abstract fun toKotlinCast(any: Any?): Any?
abstract fun toGodotCast(any: Any?): Any?
}
data object BYTE : VariantSimpleCaster(VariantParser.LONG) {
override fun toKotlinCast(any: Any?) = (any as Long).toByte()
override fun toGodotCast(any: Any?) = (any as Byte).toLong()
}
data object INT : VariantSimpleCaster(VariantParser.LONG) {
override fun toKotlinCast(any: Any?) = (any as Long).toInt()
override fun toGodotCast(any: Any?) = (any as Int).toLong()
}
data object FLOAT : VariantSimpleCaster(VariantParser.DOUBLE) {
override fun toKotlinCast(any: Any?) = (any as Double).toFloat()
override fun toGodotCast(any: Any?) = (any as Float).toDouble()
}
// It can seem weird for ANY to have a NIL id, which is the opposite concept. But that's how Godot works.
// Each parameter, property, or return type of Variant type actually uses a PropertyUsageFlag named NIL_IS_VARIANT.
// https://docs.godotengine.org/en/stable/classes/class_%40globalscope.html#enum-globalscope-propertyusageflags
data object ANY : VariantCaster(VariantParser.NIL) {
override fun toKotlin(buffer: ByteBuffer): Any? {
val expectedType = buffer.variantType
return VariantParser.entries[expectedType].toUnsafeKotlin(buffer)
}
override fun toGodot(buffer: ByteBuffer, any: Any?) {
if (any === null) {
VariantParser.NIL.toGodot(buffer, null)
} else {
val type = variantMapper[any::class]
?: throw UnsupportedOperationException("Can't convert type ${any::class} to Variant")
type.toGodot(buffer, any)
}
}
}
}
private inline fun toGodotNativeCoreType(buffer: ByteBuffer, any: Any?) {
require(any is T)
buffer.putLong(any._handle)
}