Merge pull request #87890 from raulsntos/dotnet/generics
Improve handling of generic C# types
This commit is contained in:
commit
efcb23fb6b
10 changed files with 422 additions and 141 deletions
|
@ -558,42 +558,9 @@ bool CSharpLanguage::handles_global_class_type(const String &p_type) const {
|
|||
}
|
||||
|
||||
String CSharpLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
|
||||
Ref<CSharpScript> scr = ResourceLoader::load(p_path, get_type());
|
||||
// Always assign r_base_type and r_icon_path, even if the script
|
||||
// is not a global one. In the case that it is not a global script,
|
||||
// return an empty string AFTER assigning the return parameters.
|
||||
// See GDScriptLanguage::get_global_class_name() in modules/gdscript/gdscript.cpp
|
||||
|
||||
if (!scr.is_valid() || !scr->valid) {
|
||||
// Invalid script.
|
||||
return String();
|
||||
}
|
||||
|
||||
if (r_icon_path) {
|
||||
if (scr->icon_path.is_empty() || scr->icon_path.is_absolute_path()) {
|
||||
*r_icon_path = scr->icon_path.simplify_path();
|
||||
} else if (scr->icon_path.is_relative_path()) {
|
||||
*r_icon_path = p_path.get_base_dir().path_join(scr->icon_path).simplify_path();
|
||||
}
|
||||
}
|
||||
if (r_base_type) {
|
||||
bool found_global_base_script = false;
|
||||
const CSharpScript *top = scr->base_script.ptr();
|
||||
while (top != nullptr) {
|
||||
if (top->global_class) {
|
||||
*r_base_type = top->class_name;
|
||||
found_global_base_script = true;
|
||||
break;
|
||||
}
|
||||
|
||||
top = top->base_script.ptr();
|
||||
}
|
||||
if (!found_global_base_script) {
|
||||
*r_base_type = scr->get_instance_base_type();
|
||||
}
|
||||
}
|
||||
|
||||
return scr->global_class ? scr->class_name : String();
|
||||
String class_name;
|
||||
GDMonoCache::managed_callbacks.ScriptManagerBridge_GetGlobalClassName(&p_path, r_base_type, r_icon_path, &class_name);
|
||||
return class_name;
|
||||
}
|
||||
|
||||
String CSharpLanguage::debug_get_error() const {
|
||||
|
@ -697,25 +664,19 @@ struct CSharpScriptDepSort {
|
|||
// Shouldn't happen but just in case...
|
||||
return false;
|
||||
}
|
||||
const CSharpScript *I = get_base_script(B.ptr()).ptr();
|
||||
const Script *I = B->get_base_script().ptr();
|
||||
while (I) {
|
||||
if (I == A.ptr()) {
|
||||
// A is a base of B
|
||||
return true;
|
||||
}
|
||||
|
||||
I = get_base_script(I).ptr();
|
||||
I = I->get_base_script().ptr();
|
||||
}
|
||||
|
||||
// A isn't a base of B
|
||||
return false;
|
||||
}
|
||||
|
||||
// Special fix for constructed generic types.
|
||||
Ref<CSharpScript> get_base_script(const CSharpScript *p_script) const {
|
||||
Ref<CSharpScript> base_script = p_script->base_script;
|
||||
return base_script.is_valid() && !base_script->class_name.is_empty() ? base_script : nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
void CSharpLanguage::reload_all_scripts() {
|
||||
|
@ -937,7 +898,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
obj->set_script(Ref<RefCounted>()); // Remove script and existing script instances (placeholder are not removed before domain reload)
|
||||
}
|
||||
|
||||
scr->was_tool_before_reload = scr->tool;
|
||||
scr->was_tool_before_reload = scr->type_info.is_tool;
|
||||
scr->_clear();
|
||||
}
|
||||
|
||||
|
@ -997,7 +958,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
scr->exports_invalidated = true;
|
||||
#endif
|
||||
|
||||
if (!scr->get_path().is_empty()) {
|
||||
if (!scr->get_path().is_empty() && !scr->get_path().begins_with("csharp://")) {
|
||||
scr->reload(p_soft_reload);
|
||||
|
||||
if (!scr->valid) {
|
||||
|
@ -1839,6 +1800,7 @@ bool CSharpInstance::_internal_new_managed() {
|
|||
|
||||
ERR_FAIL_NULL_V(owner, false);
|
||||
ERR_FAIL_COND_V(script.is_null(), false);
|
||||
ERR_FAIL_COND_V(!script->can_instantiate(), false);
|
||||
|
||||
bool ok = GDMonoCache::managed_callbacks.ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance(
|
||||
script.ptr(), owner, nullptr, 0);
|
||||
|
@ -2161,7 +2123,7 @@ void GD_CLR_STDCALL CSharpScript::_add_property_info_list_callback(CSharpScript
|
|||
|
||||
#ifdef TOOLS_ENABLED
|
||||
p_script->exported_members_cache.push_back(PropertyInfo(
|
||||
Variant::NIL, *p_current_class_name, PROPERTY_HINT_NONE,
|
||||
Variant::NIL, p_script->type_info.class_name, PROPERTY_HINT_NONE,
|
||||
p_script->get_path(), PROPERTY_USAGE_CATEGORY));
|
||||
#endif
|
||||
|
||||
|
@ -2334,9 +2296,7 @@ void CSharpScript::reload_registered_script(Ref<CSharpScript> p_script) {
|
|||
|
||||
// Extract information about the script using the mono class.
|
||||
void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
|
||||
bool tool = false;
|
||||
bool global_class = false;
|
||||
bool abstract_class = false;
|
||||
TypeInfo type_info;
|
||||
|
||||
// TODO: Use GDExtension godot_dictionary
|
||||
Array methods_array;
|
||||
|
@ -2346,18 +2306,12 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
|
|||
Dictionary signals_dict;
|
||||
signals_dict.~Dictionary();
|
||||
|
||||
String class_name;
|
||||
String icon_path;
|
||||
Ref<CSharpScript> base_script;
|
||||
GDMonoCache::managed_callbacks.ScriptManagerBridge_UpdateScriptClassInfo(
|
||||
p_script.ptr(), &class_name, &tool, &global_class, &abstract_class, &icon_path,
|
||||
p_script.ptr(), &type_info,
|
||||
&methods_array, &rpc_functions_dict, &signals_dict, &base_script);
|
||||
|
||||
p_script->class_name = class_name;
|
||||
p_script->tool = tool;
|
||||
p_script->global_class = global_class;
|
||||
p_script->abstract_class = abstract_class;
|
||||
p_script->icon_path = icon_path;
|
||||
p_script->type_info = type_info;
|
||||
|
||||
p_script->rpc_config.clear();
|
||||
p_script->rpc_config = rpc_functions_dict;
|
||||
|
@ -2436,7 +2390,7 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
|
|||
|
||||
bool CSharpScript::can_instantiate() const {
|
||||
#ifdef TOOLS_ENABLED
|
||||
bool extra_cond = tool || ScriptServer::is_scripting_enabled();
|
||||
bool extra_cond = type_info.is_tool || ScriptServer::is_scripting_enabled();
|
||||
#else
|
||||
bool extra_cond = true;
|
||||
#endif
|
||||
|
@ -2445,10 +2399,10 @@ bool CSharpScript::can_instantiate() const {
|
|||
// For tool scripts, this will never fire if the class is not found. That's because we
|
||||
// don't know if it's a tool script if we can't find the class to access the attributes.
|
||||
if (extra_cond && !valid) {
|
||||
ERR_FAIL_V_MSG(false, "Cannot instance script because the associated class could not be found. Script: '" + get_path() + "'. Make sure the script exists and contains a class definition with a name that matches the filename of the script exactly (it's case-sensitive).");
|
||||
ERR_FAIL_V_MSG(false, "Cannot instantiate C# script because the associated class could not be found. Script: '" + get_path() + "'. Make sure the script exists and contains a class definition with a name that matches the filename of the script exactly (it's case-sensitive).");
|
||||
}
|
||||
|
||||
return valid && !abstract_class && extra_cond;
|
||||
return valid && type_info.can_instantiate() && extra_cond;
|
||||
}
|
||||
|
||||
StringName CSharpScript::get_instance_base_type() const {
|
||||
|
@ -2458,6 +2412,8 @@ StringName CSharpScript::get_instance_base_type() const {
|
|||
}
|
||||
|
||||
CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error) {
|
||||
ERR_FAIL_COND_V_MSG(!type_info.can_instantiate(), nullptr, "Cannot instantiate C# script. Script: '" + get_path() + "'.");
|
||||
|
||||
/* STEP 1, CREATE */
|
||||
|
||||
Ref<RefCounted> ref;
|
||||
|
@ -2772,11 +2728,11 @@ bool CSharpScript::inherits_script(const Ref<Script> &p_script) const {
|
|||
}
|
||||
|
||||
Ref<Script> CSharpScript::get_base_script() const {
|
||||
return base_script.is_valid() && !base_script->get_path().is_empty() ? base_script : nullptr;
|
||||
return base_script;
|
||||
}
|
||||
|
||||
StringName CSharpScript::get_global_name() const {
|
||||
return global_class ? StringName(class_name) : StringName();
|
||||
return type_info.is_global_class ? StringName(type_info.class_name) : StringName();
|
||||
}
|
||||
|
||||
void CSharpScript::get_script_property_list(List<PropertyInfo> *r_list) const {
|
||||
|
@ -2833,7 +2789,7 @@ Error CSharpScript::load_source_code(const String &p_path) {
|
|||
}
|
||||
|
||||
void CSharpScript::_clear() {
|
||||
tool = false;
|
||||
type_info = TypeInfo();
|
||||
valid = false;
|
||||
reload_invalidated = true;
|
||||
}
|
||||
|
@ -2881,17 +2837,25 @@ Ref<Resource> ResourceFormatLoaderCSharpScript::load(const String &p_path, const
|
|||
|
||||
// TODO ignore anything inside bin/ and obj/ in tools builds?
|
||||
|
||||
String real_path = p_path;
|
||||
if (p_path.begins_with("csharp://")) {
|
||||
// This is a virtual path used by generic types, extract the real path.
|
||||
real_path = "res://" + p_path.trim_prefix("csharp://");
|
||||
real_path = real_path.substr(0, real_path.rfind(":"));
|
||||
}
|
||||
|
||||
Ref<CSharpScript> scr;
|
||||
|
||||
if (GDMonoCache::godot_api_cache_updated) {
|
||||
GDMonoCache::managed_callbacks.ScriptManagerBridge_GetOrCreateScriptBridgeForPath(&p_path, &scr);
|
||||
ERR_FAIL_NULL_V_MSG(scr, Ref<Resource>(), "Could not create C# script '" + real_path + "'.");
|
||||
} else {
|
||||
scr = Ref<CSharpScript>(memnew(CSharpScript));
|
||||
}
|
||||
|
||||
#if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED)
|
||||
Error err = scr->load_source_code(p_path);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Cannot load C# script file '" + p_path + "'.");
|
||||
Error err = scr->load_source_code(real_path);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Cannot load C# script file '" + real_path + "'.");
|
||||
#endif
|
||||
|
||||
// Only one instance of a C# script is allowed to exist.
|
||||
|
|
|
@ -60,14 +60,88 @@ class CSharpScript : public Script {
|
|||
|
||||
friend class CSharpInstance;
|
||||
friend class CSharpLanguage;
|
||||
friend struct CSharpScriptDepSort;
|
||||
|
||||
bool tool = false;
|
||||
bool global_class = false;
|
||||
bool abstract_class = false;
|
||||
public:
|
||||
struct TypeInfo {
|
||||
/**
|
||||
* Name of the C# class.
|
||||
*/
|
||||
String class_name;
|
||||
|
||||
/**
|
||||
* Path to the icon that will be used for this class by the editor.
|
||||
*/
|
||||
String icon_path;
|
||||
|
||||
/**
|
||||
* Script is marked as tool and runs in the editor.
|
||||
*/
|
||||
bool is_tool = false;
|
||||
|
||||
/**
|
||||
* Script is marked as global class and will be registered in the editor.
|
||||
* Registered classes can be created using certain editor dialogs and
|
||||
* can be referenced by name from other languages that support the feature.
|
||||
*/
|
||||
bool is_global_class = false;
|
||||
|
||||
/**
|
||||
* Script is declared abstract.
|
||||
*/
|
||||
bool is_abstract = false;
|
||||
|
||||
/**
|
||||
* The C# type that corresponds to this script is a constructed generic type.
|
||||
* E.g.: `Dictionary<int, string>`
|
||||
*/
|
||||
bool is_constructed_generic_type = false;
|
||||
|
||||
/**
|
||||
* The C# type that corresponds to this script is a generic type definition.
|
||||
* E.g.: `Dictionary<,>`
|
||||
*/
|
||||
bool is_generic_type_definition = false;
|
||||
|
||||
/**
|
||||
* The C# type that corresponds to this script contains generic type parameters,
|
||||
* regardless of whether the type parameters are bound or not.
|
||||
*/
|
||||
bool is_generic() const {
|
||||
return is_constructed_generic_type || is_generic_type_definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the script can be instantiated.
|
||||
* C# types can't be instantiated if they are abstract or contain generic
|
||||
* type parameters, but a CSharpScript is still created for them.
|
||||
*/
|
||||
bool can_instantiate() const {
|
||||
return !is_abstract && !is_generic_type_definition;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
/**
|
||||
* Contains the C# type information for this script.
|
||||
*/
|
||||
TypeInfo type_info;
|
||||
|
||||
/**
|
||||
* Scripts are valid when the corresponding C# class is found and used
|
||||
* to extract the script info using the [update_script_class_info] method.
|
||||
*/
|
||||
bool valid = false;
|
||||
/**
|
||||
* Scripts extract info from the C# class in the reload methods but,
|
||||
* if the reload is not invalidated, then the current extracted info
|
||||
* is still valid and there's no need to reload again.
|
||||
*/
|
||||
bool reload_invalidated = false;
|
||||
|
||||
/**
|
||||
* Base script that this script derives from, or null if it derives from a
|
||||
* native Godot class.
|
||||
*/
|
||||
Ref<CSharpScript> base_script;
|
||||
|
||||
HashSet<Object *> instances;
|
||||
|
@ -88,9 +162,10 @@ class CSharpScript : public Script {
|
|||
HashSet<ObjectID> pending_replace_placeholders;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Script source code.
|
||||
*/
|
||||
String source;
|
||||
String class_name;
|
||||
String icon_path;
|
||||
|
||||
SelfList<CSharpScript> script_list = this;
|
||||
|
||||
|
@ -167,7 +242,7 @@ public:
|
|||
return docs;
|
||||
}
|
||||
virtual String get_class_icon_path() const override {
|
||||
return icon_path;
|
||||
return type_info.icon_path;
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
|
@ -185,13 +260,13 @@ public:
|
|||
void get_members(HashSet<StringName> *p_members) override;
|
||||
|
||||
bool is_tool() const override {
|
||||
return tool;
|
||||
return type_info.is_tool;
|
||||
}
|
||||
bool is_valid() const override {
|
||||
return valid;
|
||||
}
|
||||
bool is_abstract() const override {
|
||||
return abstract_class;
|
||||
return type_info.is_abstract;
|
||||
}
|
||||
|
||||
bool inherits_script(const Ref<Script> &p_script) const override;
|
||||
|
|
|
@ -54,9 +54,7 @@ namespace Godot.SourceGenerators
|
|||
)
|
||||
.Where(x =>
|
||||
// Ignore classes whose name is not the same as the file name
|
||||
Path.GetFileNameWithoutExtension(x.cds.SyntaxTree.FilePath) == x.symbol.Name &&
|
||||
// Ignore generic classes
|
||||
!x.symbol.IsGenericType)
|
||||
Path.GetFileNameWithoutExtension(x.cds.SyntaxTree.FilePath) == x.symbol.Name)
|
||||
.GroupBy(x => x.symbol)
|
||||
.ToDictionary(g => g.Key, g => g.Select(x => x.cds));
|
||||
|
||||
|
@ -160,6 +158,8 @@ namespace Godot.SourceGenerators
|
|||
first = false;
|
||||
sourceBuilder.Append("typeof(");
|
||||
sourceBuilder.Append(qualifiedName);
|
||||
if (godotClass.Key.IsGenericType)
|
||||
sourceBuilder.Append($"<{new string(',', godotClass.Key.TypeParameters.Count() - 1)}>");
|
||||
sourceBuilder.Append(")");
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using GodotTools.Build;
|
||||
|
@ -23,9 +24,19 @@ namespace GodotTools.Inspector
|
|||
{
|
||||
foreach (var script in EnumerateScripts(godotObject))
|
||||
{
|
||||
if (script is not CSharpScript) continue;
|
||||
if (script is not CSharpScript)
|
||||
continue;
|
||||
|
||||
if (File.GetLastWriteTime(script.ResourcePath) > BuildManager.LastValidBuildDateTime)
|
||||
string scriptPath = script.ResourcePath;
|
||||
if (scriptPath.StartsWith("csharp://"))
|
||||
{
|
||||
// This is a virtual path used by generic types, extract the real path.
|
||||
var scriptPathSpan = scriptPath.AsSpan("csharp://".Length);
|
||||
scriptPathSpan = scriptPathSpan[..scriptPathSpan.IndexOf(':')];
|
||||
scriptPath = $"res://{scriptPathSpan}";
|
||||
}
|
||||
|
||||
if (File.GetLastWriteTime(scriptPath) > BuildManager.LastValidBuildDateTime)
|
||||
{
|
||||
AddCustomControl(new InspectorOutOfSyncWarning());
|
||||
break;
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace Godot.Bridge
|
|||
public delegate* unmanaged<godot_string_name*, IntPtr, IntPtr> ScriptManagerBridge_CreateManagedForGodotObjectBinding;
|
||||
public delegate* unmanaged<IntPtr, IntPtr, godot_variant**, int, godot_bool> ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance;
|
||||
public delegate* unmanaged<IntPtr, godot_string_name*, void> ScriptManagerBridge_GetScriptNativeName;
|
||||
public delegate* unmanaged<godot_string*, godot_string*, godot_string*, godot_string*, void> ScriptManagerBridge_GetGlobalClassName;
|
||||
public delegate* unmanaged<IntPtr, IntPtr, void> ScriptManagerBridge_SetGodotObjectPtr;
|
||||
public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant**, int, godot_bool*, void> ScriptManagerBridge_RaiseEventSignal;
|
||||
public delegate* unmanaged<IntPtr, IntPtr, godot_bool> ScriptManagerBridge_ScriptIsOrInherits;
|
||||
|
@ -25,7 +26,7 @@ namespace Godot.Bridge
|
|||
public delegate* unmanaged<godot_string*, godot_ref*, void> ScriptManagerBridge_GetOrCreateScriptBridgeForPath;
|
||||
public delegate* unmanaged<IntPtr, void> ScriptManagerBridge_RemoveScriptBridge;
|
||||
public delegate* unmanaged<IntPtr, godot_bool> ScriptManagerBridge_TryReloadRegisteredScriptWithClass;
|
||||
public delegate* unmanaged<IntPtr, godot_string*, godot_bool*, godot_bool*, godot_bool*, godot_string*, godot_array*, godot_dictionary*, godot_dictionary*, godot_ref*, void> ScriptManagerBridge_UpdateScriptClassInfo;
|
||||
public delegate* unmanaged<IntPtr, godot_csharp_type_info*, godot_array*, godot_dictionary*, godot_dictionary*, godot_ref*, void> ScriptManagerBridge_UpdateScriptClassInfo;
|
||||
public delegate* unmanaged<IntPtr, IntPtr*, godot_bool, godot_bool> ScriptManagerBridge_SwapGCHandleForType;
|
||||
public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, godot_string*, void*, int, void>, void> ScriptManagerBridge_GetPropertyInfoList;
|
||||
public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, void*, int, void>, void> ScriptManagerBridge_GetPropertyDefaultValues;
|
||||
|
@ -60,6 +61,7 @@ namespace Godot.Bridge
|
|||
ScriptManagerBridge_CreateManagedForGodotObjectBinding = &ScriptManagerBridge.CreateManagedForGodotObjectBinding,
|
||||
ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance = &ScriptManagerBridge.CreateManagedForGodotObjectScriptInstance,
|
||||
ScriptManagerBridge_GetScriptNativeName = &ScriptManagerBridge.GetScriptNativeName,
|
||||
ScriptManagerBridge_GetGlobalClassName = &ScriptManagerBridge.GetGlobalClassName,
|
||||
ScriptManagerBridge_SetGodotObjectPtr = &ScriptManagerBridge.SetGodotObjectPtr,
|
||||
ScriptManagerBridge_RaiseEventSignal = &ScriptManagerBridge.RaiseEventSignal,
|
||||
ScriptManagerBridge_ScriptIsOrInherits = &ScriptManagerBridge.ScriptIsOrInherits,
|
||||
|
|
|
@ -11,6 +11,7 @@ using System.Runtime.CompilerServices;
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Loader;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
namespace Godot.Bridge
|
||||
|
@ -29,7 +30,7 @@ namespace Godot.Bridge
|
|||
foreach (var type in typesInAlc.Keys)
|
||||
{
|
||||
if (_scriptTypeBiMap.RemoveByScriptType(type, out IntPtr scriptPtr) &&
|
||||
!_pathTypeBiMap.TryGetScriptPath(type, out _))
|
||||
(!_pathTypeBiMap.TryGetScriptPath(type, out string? scriptPath) || scriptPath.StartsWith("csharp://")))
|
||||
{
|
||||
// For scripts without a path, we need to keep the class qualified name for reloading
|
||||
_scriptDataForReload.TryAdd(scriptPtr,
|
||||
|
@ -220,6 +221,71 @@ namespace Godot.Bridge
|
|||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static unsafe void GetGlobalClassName(godot_string* scriptPath, godot_string* outBaseType, godot_string* outIconPath, godot_string* outClassName)
|
||||
{
|
||||
// This method must always return the outBaseType for every script, even if the script is
|
||||
// not a global class. But if the script is not a global class it must return an empty
|
||||
// outClassName string since it should not have a name.
|
||||
string scriptPathStr = Marshaling.ConvertStringToManaged(*scriptPath);
|
||||
Debug.Assert(!string.IsNullOrEmpty(scriptPathStr), "Script path can't be empty.");
|
||||
|
||||
if (!_pathTypeBiMap.TryGetScriptType(scriptPathStr, out Type? scriptType))
|
||||
{
|
||||
// Script at the given path does not exist, or it's not a C# type.
|
||||
// This is fine, it may be a path to a generic script and those can't be global classes.
|
||||
*outClassName = default;
|
||||
return;
|
||||
}
|
||||
|
||||
if (outIconPath != null)
|
||||
{
|
||||
var iconAttr = scriptType.GetCustomAttributes(inherit: false)
|
||||
.OfType<IconAttribute>()
|
||||
.FirstOrDefault();
|
||||
|
||||
*outIconPath = Marshaling.ConvertStringToNative(iconAttr?.Path);
|
||||
}
|
||||
|
||||
if (outBaseType != null)
|
||||
{
|
||||
bool foundGlobalBaseScript = false;
|
||||
|
||||
Type native = GodotObject.InternalGetClassNativeBase(scriptType);
|
||||
Type? top = scriptType.BaseType;
|
||||
|
||||
while (top != null && top != native)
|
||||
{
|
||||
if (IsGlobalClass(top))
|
||||
{
|
||||
*outBaseType = Marshaling.ConvertStringToNative(top.Name);
|
||||
foundGlobalBaseScript = true;
|
||||
break;
|
||||
}
|
||||
|
||||
top = top.BaseType;
|
||||
}
|
||||
if (!foundGlobalBaseScript)
|
||||
{
|
||||
*outBaseType = Marshaling.ConvertStringToNative(native.Name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsGlobalClass(scriptType))
|
||||
{
|
||||
// Scripts that are not global classes should not have a name.
|
||||
// Return an empty string to prevent the class from being registered
|
||||
// as a global class in the editor.
|
||||
*outClassName = default;
|
||||
return;
|
||||
}
|
||||
|
||||
*outClassName = Marshaling.ConvertStringToNative(scriptType.Name);
|
||||
|
||||
static bool IsGlobalClass(Type scriptType) =>
|
||||
scriptType.IsDefined(typeof(GlobalClassAttribute), inherit: false);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void SetGodotObjectPtr(IntPtr gcHandlePtr, IntPtr newPtr)
|
||||
{
|
||||
|
@ -333,7 +399,7 @@ namespace Godot.Bridge
|
|||
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
if (type.IsNested || type.IsGenericType)
|
||||
if (type.IsNested)
|
||||
continue;
|
||||
|
||||
if (!typeOfGodotObject.IsAssignableFrom(type))
|
||||
|
@ -352,9 +418,6 @@ namespace Godot.Bridge
|
|||
{
|
||||
foreach (var type in scriptTypes)
|
||||
{
|
||||
if (type.IsGenericType)
|
||||
continue;
|
||||
|
||||
LookupScriptForClass(type);
|
||||
}
|
||||
}
|
||||
|
@ -422,20 +485,8 @@ namespace Godot.Bridge
|
|||
{
|
||||
try
|
||||
{
|
||||
lock (_scriptTypeBiMap.ReadWriteLock)
|
||||
{
|
||||
if (!_scriptTypeBiMap.IsScriptRegistered(scriptPtr))
|
||||
{
|
||||
string scriptPathStr = Marshaling.ConvertStringToManaged(*scriptPath);
|
||||
|
||||
if (!_pathTypeBiMap.TryGetScriptType(scriptPathStr, out Type? scriptType))
|
||||
return godot_bool.False;
|
||||
|
||||
_scriptTypeBiMap.Add(scriptPtr, scriptType);
|
||||
}
|
||||
}
|
||||
|
||||
return godot_bool.True;
|
||||
string scriptPathStr = Marshaling.ConvertStringToManaged(*scriptPath);
|
||||
return AddScriptBridgeCore(scriptPtr, scriptPathStr).ToGodotBool();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -444,6 +495,22 @@ namespace Godot.Bridge
|
|||
}
|
||||
}
|
||||
|
||||
private static unsafe bool AddScriptBridgeCore(IntPtr scriptPtr, string scriptPath)
|
||||
{
|
||||
lock (_scriptTypeBiMap.ReadWriteLock)
|
||||
{
|
||||
if (!_scriptTypeBiMap.IsScriptRegistered(scriptPtr))
|
||||
{
|
||||
if (!_pathTypeBiMap.TryGetScriptType(scriptPath, out Type? scriptType))
|
||||
return false;
|
||||
|
||||
_scriptTypeBiMap.Add(scriptPtr, scriptType);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static unsafe void GetOrCreateScriptBridgeForPath(godot_string* scriptPath, godot_ref* outScript)
|
||||
{
|
||||
|
@ -455,6 +522,8 @@ namespace Godot.Bridge
|
|||
return;
|
||||
}
|
||||
|
||||
Debug.Assert(!scriptType.IsGenericTypeDefinition, $"Cannot get or create script for a generic type definition '{scriptType.FullName}'. Path: '{scriptPathStr}'.");
|
||||
|
||||
GetOrCreateScriptBridgeForType(scriptType, outScript);
|
||||
}
|
||||
|
||||
|
@ -494,16 +563,51 @@ namespace Godot.Bridge
|
|||
if (_pathTypeBiMap.TryGetScriptPath(scriptType, out scriptPath))
|
||||
return true;
|
||||
|
||||
if (scriptType.IsConstructedGenericType)
|
||||
{
|
||||
// If the script type is generic, also try looking for the path of the generic type definition
|
||||
// since we can use it to create the script.
|
||||
Type genericTypeDefinition = scriptType.GetGenericTypeDefinition();
|
||||
if (_pathTypeBiMap.TryGetGenericTypeDefinitionPath(genericTypeDefinition, out scriptPath))
|
||||
return true;
|
||||
}
|
||||
|
||||
CreateScriptBridgeForType(scriptType, outScript);
|
||||
scriptPath = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static string GetVirtualConstructedGenericTypeScriptPath(Type scriptType, string scriptPath)
|
||||
{
|
||||
// Constructed generic types all have the same path which is not allowed by Godot
|
||||
// (every Resource must have a unique path). So we create a unique "virtual" path
|
||||
// for each type.
|
||||
|
||||
if (!scriptPath.StartsWith("res://"))
|
||||
{
|
||||
throw new ArgumentException("Script path must start with 'res://'.", nameof(scriptPath));
|
||||
}
|
||||
|
||||
scriptPath = scriptPath.Substring("res://".Length);
|
||||
return $"csharp://{scriptPath}:{scriptType}.cs";
|
||||
}
|
||||
|
||||
if (GetPathOtherwiseGetOrCreateScript(scriptType, outScript, out string? scriptPath))
|
||||
{
|
||||
// This path is slower, but it's only executed for the first instantiation of the type
|
||||
|
||||
if (scriptType.IsConstructedGenericType && !scriptPath.StartsWith("csharp://"))
|
||||
{
|
||||
// If the script type is generic it can't be loaded using the real script path.
|
||||
// Construct a virtual path unique to this constructed generic type and add it
|
||||
// to the path bimap so they can be found later by their virtual path.
|
||||
// IMPORTANT: The virtual path must be added to _pathTypeBiMap before the first
|
||||
// load of the script, otherwise the loaded script won't be added to _scriptTypeBiMap.
|
||||
scriptPath = GetVirtualConstructedGenericTypeScriptPath(scriptType, scriptPath);
|
||||
_pathTypeBiMap.Add(scriptPath, scriptType);
|
||||
}
|
||||
|
||||
// This must be done outside the read-write lock, as the script resource loading can lock it
|
||||
using godot_string scriptPathIn = Marshaling.ConvertStringToNative(scriptPath);
|
||||
if (!NativeFuncs.godotsharp_internal_script_load(scriptPathIn, outScript).ToBool())
|
||||
|
@ -514,11 +618,23 @@ namespace Godot.Bridge
|
|||
// with no path, as we do for types without an associated script file.
|
||||
GetOrCreateScriptBridgeForType(scriptType, outScript);
|
||||
}
|
||||
|
||||
if (scriptType.IsConstructedGenericType)
|
||||
{
|
||||
// When reloading generic scripts they won't be added to the script bimap because their
|
||||
// virtual path won't be in the path bimap yet. The current method executes when a derived type
|
||||
// is trying to get or create the script for their base type. The code above has now added
|
||||
// the virtual path to the path bimap and loading the script with that path should retrieve
|
||||
// any existing script, so now we have a chance to make sure it's added to the script bimap.
|
||||
AddScriptBridgeCore(outScript->Reference, scriptPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe void CreateScriptBridgeForType(Type scriptType, godot_ref* outScript)
|
||||
{
|
||||
Debug.Assert(!scriptType.IsGenericTypeDefinition, $"Script type must be a constructed generic type or not generic at all. Type: {scriptType}.");
|
||||
|
||||
NativeFuncs.godotsharp_internal_new_csharp_script(outScript);
|
||||
IntPtr scriptPtr = outScript->Reference;
|
||||
|
||||
|
@ -605,45 +721,82 @@ namespace Godot.Bridge
|
|||
}
|
||||
}
|
||||
|
||||
private static unsafe void GetScriptTypeInfo(Type scriptType, godot_csharp_type_info* outTypeInfo)
|
||||
{
|
||||
Type native = GodotObject.InternalGetClassNativeBase(scriptType);
|
||||
|
||||
string typeName = scriptType.Name;
|
||||
if (scriptType.IsGenericType)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
AppendTypeName(sb, scriptType);
|
||||
typeName = sb.ToString();
|
||||
}
|
||||
|
||||
godot_string className = Marshaling.ConvertStringToNative(typeName);
|
||||
|
||||
bool isTool = scriptType.IsDefined(typeof(ToolAttribute), inherit: false);
|
||||
|
||||
// If the type is nested and the parent type is a tool script,
|
||||
// consider the nested type a tool script as well.
|
||||
if (!isTool && scriptType.IsNested)
|
||||
{
|
||||
isTool = scriptType.DeclaringType?.IsDefined(typeof(ToolAttribute), inherit: false) ?? false;
|
||||
}
|
||||
|
||||
// Every script in the GodotTools assembly is a tool script.
|
||||
if (!isTool && scriptType.Assembly.GetName().Name == "GodotTools")
|
||||
{
|
||||
isTool = true;
|
||||
}
|
||||
|
||||
bool isGlobalClass = scriptType.IsDefined(typeof(GlobalClassAttribute), inherit: false);
|
||||
|
||||
var iconAttr = scriptType.GetCustomAttributes(inherit: false)
|
||||
.OfType<IconAttribute>()
|
||||
.FirstOrDefault();
|
||||
|
||||
godot_string iconPath = Marshaling.ConvertStringToNative(iconAttr?.Path);
|
||||
|
||||
outTypeInfo->ClassName = className;
|
||||
outTypeInfo->IconPath = iconPath;
|
||||
outTypeInfo->IsTool = isTool.ToGodotBool();
|
||||
outTypeInfo->IsGlobalClass = isGlobalClass.ToGodotBool();
|
||||
outTypeInfo->IsAbstract = scriptType.IsAbstract.ToGodotBool();
|
||||
outTypeInfo->IsGenericTypeDefinition = scriptType.IsGenericTypeDefinition.ToGodotBool();
|
||||
outTypeInfo->IsConstructedGenericType = scriptType.IsConstructedGenericType.ToGodotBool();
|
||||
|
||||
static void AppendTypeName(StringBuilder sb, Type type)
|
||||
{
|
||||
sb.Append(type.Name);
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
sb.Append('<');
|
||||
for (int i = 0; i < type.GenericTypeArguments.Length; i++)
|
||||
{
|
||||
Type typeArg = type.GenericTypeArguments[i];
|
||||
AppendTypeName(sb, typeArg);
|
||||
if (i != type.GenericTypeArguments.Length - 1)
|
||||
{
|
||||
sb.Append(", ");
|
||||
}
|
||||
}
|
||||
sb.Append('>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_string* outClassName,
|
||||
godot_bool* outTool, godot_bool* outGlobal, godot_bool* outAbstract, godot_string* outIconPath,
|
||||
godot_array* outMethodsDest, godot_dictionary* outRpcFunctionsDest,
|
||||
godot_dictionary* outEventSignalsDest, godot_ref* outBaseScript)
|
||||
internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_csharp_type_info* outTypeInfo,
|
||||
godot_array* outMethodsDest, godot_dictionary* outRpcFunctionsDest, godot_dictionary* outEventSignalsDest, godot_ref* outBaseScript)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Performance is not critical here as this will be replaced with source generators.
|
||||
var scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr);
|
||||
Debug.Assert(!scriptType.IsGenericTypeDefinition, $"Script type must be a constructed generic type or not generic at all. Type: {scriptType}.");
|
||||
|
||||
*outClassName = Marshaling.ConvertStringToNative(scriptType.Name);
|
||||
|
||||
*outTool = scriptType.GetCustomAttributes(inherit: false)
|
||||
.OfType<ToolAttribute>()
|
||||
.Any().ToGodotBool();
|
||||
|
||||
if (!(*outTool).ToBool() && scriptType.IsNested)
|
||||
{
|
||||
*outTool = (scriptType.DeclaringType?.GetCustomAttributes(inherit: false)
|
||||
.OfType<ToolAttribute>()
|
||||
.Any() ?? false).ToGodotBool();
|
||||
}
|
||||
|
||||
if (!(*outTool).ToBool() && scriptType.Assembly.GetName().Name == "GodotTools")
|
||||
*outTool = godot_bool.True;
|
||||
|
||||
var globalAttr = scriptType.GetCustomAttributes(inherit: false)
|
||||
.OfType<GlobalClassAttribute>()
|
||||
.FirstOrDefault();
|
||||
|
||||
*outGlobal = (globalAttr != null).ToGodotBool();
|
||||
|
||||
var iconAttr = scriptType.GetCustomAttributes(inherit: false)
|
||||
.OfType<IconAttribute>()
|
||||
.FirstOrDefault();
|
||||
*outIconPath = Marshaling.ConvertStringToNative(iconAttr?.Path);
|
||||
|
||||
*outAbstract = scriptType.IsAbstract.ToGodotBool();
|
||||
GetScriptTypeInfo(scriptType, outTypeInfo);
|
||||
|
||||
// Methods
|
||||
|
||||
|
@ -820,11 +973,7 @@ namespace Godot.Bridge
|
|||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
*outClassName = default;
|
||||
*outTool = godot_bool.False;
|
||||
*outGlobal = godot_bool.False;
|
||||
*outAbstract = godot_bool.False;
|
||||
*outIconPath = default;
|
||||
*outTypeInfo = default;
|
||||
*outMethodsDest = NativeFuncs.godotsharp_array_new();
|
||||
*outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new();
|
||||
*outEventSignalsDest = NativeFuncs.godotsharp_dictionary_new();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
@ -19,6 +20,8 @@ public static partial class ScriptManagerBridge
|
|||
{
|
||||
// TODO: What if this is called while unloading a load context, but after we already did cleanup in preparation for unloading?
|
||||
|
||||
Debug.Assert(!scriptType.IsGenericTypeDefinition, $"A generic type definition must never be added to the script type map. Type: {scriptType}.");
|
||||
|
||||
_scriptTypeMap.Add(scriptPtr, scriptType);
|
||||
_typeScriptMap.Add(scriptType, scriptPtr);
|
||||
|
||||
|
@ -85,10 +88,29 @@ public static partial class ScriptManagerBridge
|
|||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetScriptType(string scriptPath, [MaybeNullWhen(false)] out Type scriptType) =>
|
||||
_pathTypeMap.TryGetValue(scriptPath, out scriptType);
|
||||
// This must never return true for a generic type definition, we only consider script types
|
||||
// the types that can be attached to a Node/Resource (non-generic or constructed generic types).
|
||||
_pathTypeMap.TryGetValue(scriptPath, out scriptType) && !scriptType.IsGenericTypeDefinition;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetScriptPath(Type scriptType, [MaybeNullWhen(false)] out string scriptPath) =>
|
||||
_typePathMap.TryGetValue(scriptType, out scriptPath);
|
||||
public bool TryGetScriptPath(Type scriptType, [MaybeNullWhen(false)] out string scriptPath)
|
||||
{
|
||||
if (scriptType.IsGenericTypeDefinition)
|
||||
{
|
||||
// This must never return true for a generic type definition, we only consider script types
|
||||
// the types that can be attached to a Node/Resource (non-generic or constructed generic types).
|
||||
scriptPath = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return _typePathMap.TryGetValue(scriptType, out scriptPath);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetGenericTypeDefinitionPath(Type genericTypeDefinition, [MaybeNullWhen(false)] out string scriptPath)
|
||||
{
|
||||
Debug.Assert(genericTypeDefinition.IsGenericTypeDefinition);
|
||||
return _typePathMap.TryGetValue(genericTypeDefinition, out scriptPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,6 +105,61 @@ namespace Godot.NativeInterop
|
|||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public ref struct godot_csharp_type_info
|
||||
{
|
||||
private godot_string _className;
|
||||
private godot_string _iconPath;
|
||||
private godot_bool _isTool;
|
||||
private godot_bool _isGlobalClass;
|
||||
private godot_bool _isAbstract;
|
||||
private godot_bool _isConstructedGenericType;
|
||||
private godot_bool _isGenericTypeDefinition;
|
||||
|
||||
public godot_string ClassName
|
||||
{
|
||||
readonly get => _className;
|
||||
set => _className = value;
|
||||
}
|
||||
|
||||
public godot_string IconPath
|
||||
{
|
||||
readonly get => _iconPath;
|
||||
set => _iconPath = value;
|
||||
}
|
||||
|
||||
public godot_bool IsTool
|
||||
{
|
||||
readonly get => _isTool;
|
||||
set => _isTool = value;
|
||||
}
|
||||
|
||||
public godot_bool IsGlobalClass
|
||||
{
|
||||
readonly get => _isGlobalClass;
|
||||
set => _isGlobalClass = value;
|
||||
}
|
||||
|
||||
public godot_bool IsAbstract
|
||||
{
|
||||
readonly get => _isAbstract;
|
||||
set => _isAbstract = value;
|
||||
}
|
||||
|
||||
public godot_bool IsConstructedGenericType
|
||||
{
|
||||
readonly get => _isConstructedGenericType;
|
||||
set => _isConstructedGenericType = value;
|
||||
}
|
||||
|
||||
public godot_bool IsGenericTypeDefinition
|
||||
{
|
||||
readonly get => _isGenericTypeDefinition;
|
||||
set => _isGenericTypeDefinition = value;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public ref struct godot_variant
|
||||
|
|
|
@ -59,6 +59,7 @@ void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks) {
|
|||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, CreateManagedForGodotObjectBinding);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, CreateManagedForGodotObjectScriptInstance);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetScriptNativeName);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetGlobalClassName);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, SetGodotObjectPtr);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, RaiseEventSignal);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, ScriptIsOrInherits);
|
||||
|
|
|
@ -84,6 +84,7 @@ struct ManagedCallbacks {
|
|||
using FuncScriptManagerBridge_CreateManagedForGodotObjectBinding = GCHandleIntPtr(GD_CLR_STDCALL *)(const StringName *, Object *);
|
||||
using FuncScriptManagerBridge_CreateManagedForGodotObjectScriptInstance = bool(GD_CLR_STDCALL *)(const CSharpScript *, Object *, const Variant **, int32_t);
|
||||
using FuncScriptManagerBridge_GetScriptNativeName = void(GD_CLR_STDCALL *)(const CSharpScript *, StringName *);
|
||||
using FuncScriptManagerBridge_GetGlobalClassName = void(GD_CLR_STDCALL *)(const String *, String *, String *, String *);
|
||||
using FuncScriptManagerBridge_SetGodotObjectPtr = void(GD_CLR_STDCALL *)(GCHandleIntPtr, Object *);
|
||||
using FuncScriptManagerBridge_RaiseEventSignal = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, const Variant **, int32_t, bool *);
|
||||
using FuncScriptManagerBridge_ScriptIsOrInherits = bool(GD_CLR_STDCALL *)(const CSharpScript *, const CSharpScript *);
|
||||
|
@ -91,7 +92,7 @@ struct ManagedCallbacks {
|
|||
using FuncScriptManagerBridge_GetOrCreateScriptBridgeForPath = void(GD_CLR_STDCALL *)(const String *, Ref<CSharpScript> *);
|
||||
using FuncScriptManagerBridge_RemoveScriptBridge = void(GD_CLR_STDCALL *)(const CSharpScript *);
|
||||
using FuncScriptManagerBridge_TryReloadRegisteredScriptWithClass = bool(GD_CLR_STDCALL *)(const CSharpScript *);
|
||||
using FuncScriptManagerBridge_UpdateScriptClassInfo = void(GD_CLR_STDCALL *)(const CSharpScript *, String *, bool *, bool *, bool *, String *, Array *, Dictionary *, Dictionary *, Ref<CSharpScript> *);
|
||||
using FuncScriptManagerBridge_UpdateScriptClassInfo = void(GD_CLR_STDCALL *)(const CSharpScript *, CSharpScript::TypeInfo *, Array *, Dictionary *, Dictionary *, Ref<CSharpScript> *);
|
||||
using FuncScriptManagerBridge_SwapGCHandleForType = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr *, bool);
|
||||
using FuncScriptManagerBridge_GetPropertyInfoList = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyInfoList_Add);
|
||||
using FuncScriptManagerBridge_GetPropertyDefaultValues = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyDefaultValues_Add);
|
||||
|
@ -120,6 +121,7 @@ struct ManagedCallbacks {
|
|||
FuncScriptManagerBridge_CreateManagedForGodotObjectBinding ScriptManagerBridge_CreateManagedForGodotObjectBinding;
|
||||
FuncScriptManagerBridge_CreateManagedForGodotObjectScriptInstance ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance;
|
||||
FuncScriptManagerBridge_GetScriptNativeName ScriptManagerBridge_GetScriptNativeName;
|
||||
FuncScriptManagerBridge_GetGlobalClassName ScriptManagerBridge_GetGlobalClassName;
|
||||
FuncScriptManagerBridge_SetGodotObjectPtr ScriptManagerBridge_SetGodotObjectPtr;
|
||||
FuncScriptManagerBridge_RaiseEventSignal ScriptManagerBridge_RaiseEventSignal;
|
||||
FuncScriptManagerBridge_ScriptIsOrInherits ScriptManagerBridge_ScriptIsOrInherits;
|
||||
|
|
Loading…
Reference in a new issue