Skip to content

Commit

Permalink
feat: Remove need for .cast
Browse files Browse the repository at this point in the history
By adding the script instance check to `gd_object_to_dart_object`, that method can do all casting for us properly.

Minor: fix generator to not have RPC method class be private (preventing a warning from the analyzer).
  • Loading branch information
fuzzybinary committed Dec 23, 2024
1 parent d4d8d61 commit 945a905
Show file tree
Hide file tree
Showing 7 changed files with 28 additions and 51 deletions.
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class SimpleScript extends Sprite2D {
// Any method that needs to be seen by a signal needs to be exported
@GodotExport()
void onSignal() {
// To call and RPC as an RPC you use the $rpc variable
// To call an RPC as an RPC you use the $rpc variable
$rpc.rpcMessage('message');
}
}
Expand Down Expand Up @@ -133,8 +133,7 @@ class Simple extends Sprite2D {
StringName.fromString('Simple'),
parentClass: StringName.fromString('Sprite2D'),
// a vTable getter is required for classes that will be used from extensions.
// If you are not adding any virtual functions, just return the base class's vTable.
// If the class is only used from scripts, this is likely not necessary.
// If you are not adding any virtual functions, just return the base class's vTable.
vTable: Sprite2D.sTypeInfo.vTable;
);
// An override of [typeInfo] is required. This is how
Expand Down Expand Up @@ -184,9 +183,9 @@ void main() {

### Casting

When you are working with a Godot object, do not use `is` or `as` to perform downcasting. This will
always fail because of how Godot extension works. Instead, use `.cast<T>`, which will return `null`
if the cast fails.
Early versions of Godot Dart required using `.cast<T>` to perform downcasting. This is no longer
necessary. Dart's built in `is` and `as` operators should now work to perform downcasting. `cast<T>`
has also been removed and replaced with `.as<T>`, which is an implementation of `as?` or `dynamic_cast`.

### Virtual functions

Expand Down
2 changes: 1 addition & 1 deletion example/2d_tutorial/src/lib/game_logic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class GameLogic extends Node {

@GodotExport()
void onMobTimerTimeout() {
final mob = mobScene?.instantiate()?.cast<Mob>();
final mob = mobScene?.instantiate()?.as<Mob>();
if (mob != null) {
var mobSpawnLocation =
getNodeT<PathFollow2D>('MobPath/MobSpawnLocation')!;
Expand Down
24 changes: 16 additions & 8 deletions src/cpp/dart_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -397,18 +397,18 @@ void GodotDartBindings::bind_method(const TypeInfo &bind_type, const char *metho
Dart_Handle GodotDartBindings::find_dart_type(Dart_Handle type_name) {
DartBlockScope scope;

uint8_t* c_type_name = nullptr;
uint8_t *c_type_name = nullptr;
intptr_t length = 0;
Dart_StringToUTF8(type_name, &c_type_name, &length);
if (0 == strncmp(reinterpret_cast<const char*>(c_type_name), "Object", length)) {
if (0 == strncmp(reinterpret_cast<const char *>(c_type_name), "Object", length)) {
type_name = Dart_NewStringFromCString("GodotObject");
}

DART_CHECK_RET(engine_classes_library, Dart_HandleFromPersistent(_engine_classes_library), Dart_Null(),
"Error getting class class library.")
"Error getting class class library.")

DART_CHECK_RET(type, Dart_GetNonNullableType(engine_classes_library, type_name, 0, nullptr), Dart_Null(),
"Could not find type in the engine_classes_library.");
"Could not find type in the engine_classes_library.");

return type;
}
Expand Down Expand Up @@ -815,9 +815,17 @@ void gd_object_to_dart_object(Dart_NativeArguments args) {
}

GDEWrapper *gde = GDEWrapper::instance();
GDExtensionScriptInstanceDataPtr script_instance = gde_object_get_script_instance(
reinterpret_cast<GDExtensionObjectPtr>(object_ptr), DartScriptLanguage::instance()->_owner);
if (script_instance) {
Dart_Handle obj = reinterpret_cast<DartScriptInstance *>(script_instance)->get_dart_object();
Dart_SetReturnValue(args, obj);
return;
}

DartGodotInstanceBinding *binding = (DartGodotInstanceBinding *)gde_object_get_instance_binding(
reinterpret_cast<GDExtensionObjectPtr>(object_ptr), bindings, &DartGodotInstanceBinding::engine_binding_callbacks);
reinterpret_cast<GDExtensionObjectPtr>(object_ptr), bindings,
&DartGodotInstanceBinding::engine_binding_callbacks);
if (binding == nullptr) {
Dart_SetReturnValue(args, Dart_Null());
} else {
Expand Down Expand Up @@ -905,7 +913,7 @@ void type_info_from_dart(TypeInfo *type_info, Dart_Handle dart_type_info) {

Dart_Handle class_name = Dart_GetField(dart_type_info, Dart_NewStringFromCString("className"));
Dart_Handle parent_type = Dart_GetField(dart_type_info, Dart_NewStringFromCString("parentType"));
Dart_Handle variant_type = Dart_GetField(dart_type_info, Dart_NewStringFromCString("variantType"));
Dart_Handle variant_type = Dart_GetField(dart_type_info, Dart_NewStringFromCString("variantType"));

type_info->type_name = get_object_address(class_name);
type_info->parent_type = parent_type;
Expand Down Expand Up @@ -938,8 +946,8 @@ GDE_EXPORT void tie_dart_to_native(Dart_Handle dart_object, GDExtensionObjectPtr
type_info_from_dart(&class_type_info, d_class_type_info);

const GDExtensionInstanceBindingCallbacks *callbacks = &DartGodotInstanceBinding::engine_binding_callbacks;
DartGodotInstanceBinding *binding = (DartGodotInstanceBinding *)gde_object_get_instance_binding(
godot_object, bindings, callbacks);
DartGodotInstanceBinding *binding =
(DartGodotInstanceBinding *)gde_object_get_instance_binding(godot_object, bindings, callbacks);
if (!binding->is_initialized()) {
binding->initialize(dart_object, is_refcounted);
}
Expand Down
35 changes: 3 additions & 32 deletions src/dart/godot_dart/lib/src/core/gdextension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -212,37 +212,8 @@ class GodotDart {
}

extension GodotObjectCast on GodotObject {
// TODO: We might not need this if gdObjectToDartObject checks
// if something has a script instance, than casts it directly.
T? cast<T>() {
var typeInfo = gde.dartBindings.getGodotTypeInfo(T);
final classTag = gde.getClassTag(typeInfo.className);
Pointer<Void> casted;
if (classTag != nullptr) {
casted = gde.ffiBindings.gde_object_cast_to(nativePtr, classTag);
if (casted == nullptr) {
return null;
}

final persistent = gde.ffiBindings.gde_object_get_instance_binding(
casted,
gde.extensionToken,
gde.engineBindingCallbacks,
);
final dartObject = gde.dartBindings.objectFromInstanceBinding(persistent);

return dartObject as T;
} else {
// Try getting the script instance, and casting from that
final scriptInstance = gde.dartBindings.getScriptInstance(nativePtr);
if (scriptInstance != nullptr) {
final o = gde.dartBindings.objectFromScriptInstance(scriptInstance);
if (o is T) {
return o;
}
}
}

return null;
// Implementation of as? or dynamic_cast
T? as<T>() {
return this is T ? this as T : null;
}
}
2 changes: 1 addition & 1 deletion src/dart/godot_dart/lib/src/core/type_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class TypeInfo {
this.size = 0,
this.vTable = const {},
this.scriptInfo,
}) {}
});

static late Map<Type?, TypeInfo> _typeMapping;
static void initTypeMappings() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ extension TNode on Node {
name = GDString.fromStringName(typeInfo.className);
}
var node = getNode(NodePath.fromGDString(name));
return node?.cast<T>();
return node?.as<T>();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ class GodotScriptAnnotationGenerator
buffer.writeln(' name: \'$exportName\',');
buffer.writeln(' dartMethodName: \'${element.name}\',');
} else if (element.hasOverride) {
// TODO - I might change the naming scheme here
final godotMethodName = _convertVirtualMethodName(element.name);
buffer.writeln(' name: \'$godotMethodName\',');
buffer.writeln(' dartMethodName: \'${element.name}\',');
Expand Down Expand Up @@ -311,7 +310,7 @@ class GodotScriptAnnotationGenerator
StringBuffer buffer = StringBuffer();

final className = element.name;
final rpcMethodsClass = '_\$${className}RpcMethods';
final rpcMethodsClass = '\$${className}RpcMethods';
buffer.writeln('class $rpcMethodsClass {');
buffer.writeln(' $className self;');
buffer.writeln(' $rpcMethodsClass(this.self);');
Expand Down

0 comments on commit 945a905

Please sign in to comment.