Skip to content

Commit

Permalink
🐛 Fix casting to script types
Browse files Browse the repository at this point in the history
Casting to script types requires [this PR](godotengine/godot#80040), which will make this extension require Godot 4.2 when it is merged.
  • Loading branch information
fuzzybinary committed Jul 30, 2023
1 parent 0a0cf3c commit cbad9e8
Show file tree
Hide file tree
Showing 14 changed files with 110 additions and 16 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ And I want to use it in Godot.

# Current State

```
⚠ NOTE -- Current master of this extension currently requires [this PR](https://github.com/godotengine/godot/pull/80040). Once that
PR is merged, this extension will only be Godot 4.2 compatible
```

Here's a list of planned features and work still to be done ( ✅ - Seems to be
working, 🟨 - Partially working, ❌ - Not working)

Expand Down Expand Up @@ -44,7 +49,7 @@ I have only tested this on Windows. I know for certain it will only work on 'flo

* A clone of this repo.
* Dart (2.19 Current Stable, not tested with 3.0)
* Godot 4.1.x or a custom build compatible with 4.1.x
* Godot 4.2.x - Note above the pending pull request.
* A build of the [Dart Shared
Library](https://github.com/fuzzybinary/dart_shared_libray). Windows x64 .dlls
for Dart 2.19 are provided in `src/dart_dll/bin/win64`.
Expand Down
3 changes: 2 additions & 1 deletion godot-headers/godot/gdextension_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -2114,10 +2114,11 @@ typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate)
* Get the script instance data attached to this object.
*
* @param p_object A pointer to the Object
* @param p_language A pointer to the language expected for this script instance
*
* @return A GDExtensionScriptInstanceDataPtr that was attached to this object as part of script_instance_create
*/
typedef GDExtensionScriptInstanceDataPtr (*GDExtensionInterfaceObjectGetScriptInstance)(GDExtensionConstObjectPtr p_object);
typedef GDExtensionScriptInstanceDataPtr (*GDExtensionInterfaceObjectGetScriptInstance)(GDExtensionConstObjectPtr p_object, GDExtensionObjectPtr p_language);

/* INTERFACE: ClassDB */

Expand Down
15 changes: 15 additions & 0 deletions src/cpp/dart_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ bool GodotDartBindings::initialize(const char *script_path, const char *package_
};
DART_CHECK_RET(result, Dart_Invoke(godot_dart_library, Dart_NewStringFromCString("_registerGodot"), 2, args),
false, "Error calling '_registerGodot'");

// Get the language pointer from the result:
_dart_language = get_object_address(result);
}

// And call the main function from the user supplied library
Expand Down Expand Up @@ -987,6 +990,18 @@ GDE_EXPORT void *create_script_instance(Dart_Handle type, Dart_Handle script, vo
return godot_script_instance;
}

GDE_EXPORT Dart_Handle object_from_script_instance(DartScriptInstance *script_instance) {
if (!script_instance) {
return Dart_Null();
}

Dart_PersistentHandle persistent = script_instance->get_dart_object();
DART_CHECK_RET(dart_object, Dart_HandleFromPersistent(persistent), Dart_Null(),
"Failed to get object from persistent handle");

return dart_object;
}

GDE_EXPORT void perform_frame_maintenance() {
Dart_EnterScope();

Expand Down
5 changes: 5 additions & 0 deletions src/cpp/dart_bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ class GodotDartBindings {
bool initialize(const char *script_path, const char *package_config);
void shutdown();

GDExtensionScriptLanguagePtr get_language() {
return _dart_language;
}

void bind_method(const TypeInfo &bind_type, const char *method_name, const TypeInfo &ret_type_info,
Dart_Handle args_list, MethodFlags method_flags);
void add_property(const TypeInfo &bind_type, Dart_Handle dart_prop_info);
Expand Down Expand Up @@ -84,6 +88,7 @@ class GodotDartBindings {
Dart_PersistentHandle _native_library;

// Some things we need often
GDExtensionScriptLanguagePtr _dart_language;
Dart_PersistentHandle _void_pointer_type;
Dart_PersistentHandle _void_pointer_optional_type;
Dart_PersistentHandle _void_pointer_pointer_type;
Expand Down
3 changes: 1 addition & 2 deletions src/cpp/dart_script_instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,8 +425,7 @@ bool DartScriptInstance::get_fallback(const GDStringName &p_name, GDExtensionVar
}

GDExtensionScriptLanguagePtr DartScriptInstance::get_language() {

return nullptr;
return GodotDartBindings::instance()->get_language();
}

// * Static Callback Functions for Godot */
Expand Down
4 changes: 4 additions & 0 deletions src/cpp/dart_script_instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ class DartScriptInstance {

GDExtensionScriptLanguagePtr get_language();

Dart_PersistentHandle get_dart_object() {
return _dart_object;
}

static const GDExtensionScriptInstanceInfo* get_script_instance_info();

private:
Expand Down
10 changes: 10 additions & 0 deletions src/cpp/gde_c_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,15 @@ GDExtensionScriptInstancePtr gde_script_instance_create(const GDExtensionScriptI
return nullptr;
}

static GDExtensionInterfaceObjectGetScriptInstance _object_get_script_instance_func = nullptr;
GDExtensionScriptInstanceDataPtr gde_object_get_script_instance(GDExtensionConstObjectPtr p_object,
GDExtensionObjectPtr p_language) {
if (_object_get_script_instance_func) {
return _object_get_script_instance_func(p_object, p_language);
}
return nullptr;
}

#define LOAD_METHOD(method, type) _##method##_func = (type)get_proc_address(#method)

void gde_init_c_interface(GDExtensionInterfaceGetProcAddress get_proc_address) {
Expand Down Expand Up @@ -395,6 +404,7 @@ void gde_init_c_interface(GDExtensionInterfaceGetProcAddress get_proc_address) {
LOAD_METHOD(ref_get_object, GDExtensionInterfaceRefGetObject);
LOAD_METHOD(ref_set_object, GDExtensionInterfaceRefSetObject);
LOAD_METHOD(script_instance_create, GDExtensionInterfaceScriptInstanceCreate);
LOAD_METHOD(object_get_script_instance, GDExtensionInterfaceObjectGetScriptInstance);
}

}
2 changes: 2 additions & 0 deletions src/cpp/gde_c_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ GDE_EXPORT void gde_ref_set_object(GDExtensionRefPtr p_ref, GDExtensionObjectPtr

GDE_EXPORT GDExtensionScriptInstancePtr gde_script_instance_create(const GDExtensionScriptInstanceInfo *p_info,
GDExtensionScriptInstanceDataPtr p_instance_data);
GDE_EXPORT GDExtensionScriptInstanceDataPtr gde_object_get_script_instance(GDExtensionConstObjectPtr p_object,
GDExtensionObjectPtr p_language);

#ifdef __cplusplus
}
Expand Down
17 changes: 17 additions & 0 deletions src/cpp/gde_dart_converters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@
#include "gde_wrapper.h"
#include "godot_string_wrappers.h"

void *get_object_address(Dart_Handle engine_handle) {
Dart_Handle native_ptr = Dart_GetField(engine_handle, Dart_NewStringFromCString("nativePtr"));
if (Dart_IsError(native_ptr)) {
GD_PRINT_ERROR(Dart_GetError(native_ptr));
return nullptr;
}
Dart_Handle address = Dart_GetField(native_ptr, Dart_NewStringFromCString("address"));
if (Dart_IsError(address)) {
GD_PRINT_ERROR(Dart_GetError(address));
return nullptr;
}
uint64_t object_ptr = 0;
Dart_IntegerToUint64(address, &object_ptr);

return reinterpret_cast<void *>(object_ptr);
}

void *get_opaque_address(Dart_Handle variant_handle) {
// TODO: Look for a better way convert the variant.
Dart_Handle opaque = Dart_GetField(variant_handle, Dart_NewStringFromCString("_opaque"));
Expand Down
1 change: 1 addition & 0 deletions src/cpp/gde_dart_converters.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ struct TypeInfo {
// Actually defined in dart_bindings.cpp
void type_info_from_dart(TypeInfo *type_info, Dart_Handle dart_type_info);

void *get_object_address(Dart_Handle variant_handle);
void *get_opaque_address(Dart_Handle variant_handle);

void gde_method_info_from_dart(Dart_Handle dart_method_info, GDExtensionMethodInfo *method_info);
Expand Down
4 changes: 3 additions & 1 deletion src/dart/godot_dart/lib/godot_dart.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ late GodotDart _globalExtension;
DartScriptLanguage? _dartScriptLanguage;

@pragma('vm:entry-point')
void _registerGodot(int libraryAddress, int bindingCallbacks) {
DartScriptLanguage _registerGodot(int libraryAddress, int bindingCallbacks) {
final godotDart = GodotDartNativeBindings.openLibrary('godot_dart');
final ffiInterface = GDExtensionFFI(godotDart);

Expand Down Expand Up @@ -57,6 +57,8 @@ void _registerGodot(int libraryAddress, int bindingCallbacks) {
.addResourceFormatSaver(Ref(DartResourceFormatSaver()), false);

print('Everything loaded a-ok!');

return _dartScriptLanguage!;
}

@pragma('vm:entry-point')
Expand Down
31 changes: 20 additions & 11 deletions src/dart/godot_dart/lib/src/core/gdextension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -257,19 +257,28 @@ class GodotDart {
if (casted == nullptr) {
return null;
}
} else {
casted = from.nativePtr;
}

if (typeInfo.bindingToken != null) {
final persistent = gde.ffiBindings.gde_object_get_instance_binding(
casted,
typeInfo.bindingToken!,
gde.engineBindingCallbacks,
);
final dartObject = dartBindings.fromPersistentHandle(persistent);
if (typeInfo.bindingToken != null) {
final persistent = gde.ffiBindings.gde_object_get_instance_binding(
casted,
typeInfo.bindingToken!,
gde.engineBindingCallbacks,
);
final dartObject = dartBindings.fromPersistentHandle(persistent);

return dartObject as T;
return dartObject as T;
}
// TODO: What case is there where there is a class tag but no binding token?
} else {
// Try getting the script instance, and casting from that
final scriptInstance = gde.ffiBindings.gde_object_get_script_instance(
from.nativePtr, DartScriptLanguage.singleton.nativePtr);
if (scriptInstance != nullptr) {
final o = gde.dartBindings.objectFromScriptInstance(scriptInstance);
if (o is T) {
return o;
}
}
}

return null;
Expand Down
19 changes: 19 additions & 0 deletions src/dart/godot_dart/lib/src/core/gdextension_ffi_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,25 @@ class GDExtensionFFI {
GDExtensionScriptInstancePtr Function(
ffi.Pointer<GDExtensionScriptInstanceInfo>,
GDExtensionScriptInstanceDataPtr)>();

GDExtensionScriptInstanceDataPtr gde_object_get_script_instance(
GDExtensionConstObjectPtr p_object,
GDExtensionObjectPtr p_language,
) {
return _gde_object_get_script_instance(
p_object,
p_language,
);
}

late final _gde_object_get_script_instancePtr = _lookup<
ffi.NativeFunction<
GDExtensionScriptInstanceDataPtr Function(GDExtensionConstObjectPtr,
GDExtensionObjectPtr)>>('gde_object_get_script_instance');
late final _gde_object_get_script_instance =
_gde_object_get_script_instancePtr.asFunction<
GDExtensionScriptInstanceDataPtr Function(
GDExtensionConstObjectPtr, GDExtensionObjectPtr)>();
}

typedef va_list = ffi.Pointer<ffi.Char>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ class GodotDartNativeBindings {
Bool)>>('create_script_instance')
.asFunction<
Pointer<Void> Function(Type, DartScript, Pointer<Void>, bool)>();
late final objectFromScriptInstance = godotDartDylib
.lookup<NativeFunction<Handle Function(Pointer<Void>)>>(
'object_from_script_instance')
.asFunction<Object? Function(Pointer<Void>)>();

late final _safeNewPersistentHandle = godotDartDylib
.lookup<NativeFunction<Pointer<Void> Function(Handle)>>(
'safe_new_persistent_handle')
Expand Down

0 comments on commit cbad9e8

Please sign in to comment.