Skip to content

Commit

Permalink
Add C# resource export.
Browse files Browse the repository at this point in the history
Co-authored-by: willnationsdev <willnationsdev@gmail.com>
  • Loading branch information
raulsntos and willnationsdev committed Feb 9, 2023
1 parent b12bf7f commit 3dd8725
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 12 deletions.
72 changes: 70 additions & 2 deletions modules/mono/csharp_script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#ifdef TOOLS_ENABLED
#include "core/os/keyboard.h"
#include "editor/bindings_generator.h"
#include "editor/editor_file_system.h"
#include "editor/editor_internal_calls.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
Expand Down Expand Up @@ -542,6 +543,48 @@ String CSharpLanguage::_get_indentation() const {
return "\t";
}

bool CSharpLanguage::handles_global_class_type(const String &p_type) const {
return p_type == get_type();
}

String CSharpLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
Ref<CSharpScript> script = ResourceLoader::load(p_path, get_type());
if (!script->valid || !script->global_class) {
// Invalid script or the script is not a global class.
return String();
}

String name = script->class_name;
if (unlikely(name.is_empty())) {
return String();
}

if (r_icon_path) {
if (script->icon_path.is_empty() || script->icon_path.is_absolute_path()) {
*r_icon_path = script->icon_path.simplify_path();
} else if (script->icon_path.is_relative_path()) {
*r_icon_path = p_path.get_base_dir().path_join(script->icon_path).simplify_path();
}
}
if (r_base_type) {
bool found_global_base_script = false;
const CSharpScript *top = script->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 = script->get_instance_base_type();
}
}
return name;
}

String CSharpLanguage::debug_get_error() const {
return _debug_error;
}
Expand Down Expand Up @@ -2207,11 +2250,21 @@ void CSharpScript::reload_registered_script(Ref<CSharpScript> p_script) {
update_script_class_info(p_script);

p_script->_update_exports();

#if TOOLS_ENABLED
// If the EditorFileSystem singleton is available, update the file;
// otherwise, the file will be updated when the singleton becomes available.
EditorFileSystem *efs = EditorFileSystem::get_singleton();
if (efs) {
efs->update_file(p_script->get_path());
}
#endif
}

// 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;

// TODO: Use GDExtension godot_dictionary
Array methods_array;
Expand All @@ -2221,11 +2274,17 @@ 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(), &tool, &methods_array, &rpc_functions_dict, &signals_dict, &base_script);
p_script.ptr(), &class_name, &tool, &global_class, &icon_path,
&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->icon_path = icon_path;

p_script->rpc_config.clear();
p_script->rpc_config = rpc_functions_dict;
Expand Down Expand Up @@ -2523,6 +2582,15 @@ Error CSharpScript::reload(bool p_keep_state) {
update_script_class_info(this);

_update_exports();

#if TOOLS_ENABLED
// If the EditorFileSystem singleton is available, update the file;
// otherwise, the file will be updated when the singleton becomes available.
EditorFileSystem *efs = EditorFileSystem::get_singleton();
if (efs) {
efs->update_file(script_path);
}
#endif
}

return OK;
Expand Down Expand Up @@ -2613,7 +2681,7 @@ Ref<Script> CSharpScript::get_base_script() const {
}

StringName CSharpScript::get_global_name() const {
return StringName();
return global_class ? StringName(class_name) : StringName();
}

void CSharpScript::get_script_property_list(List<PropertyInfo> *r_list) const {
Expand Down
7 changes: 7 additions & 0 deletions modules/mono/csharp_script.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class CSharpScript : public Script {
friend class CSharpLanguage;

bool tool = false;
bool global_class = false;
bool valid = false;
bool reload_invalidated = false;

Expand All @@ -86,6 +87,8 @@ class CSharpScript : public Script {
#endif

String source;
String class_name;
String icon_path;

SelfList<CSharpScript> script_list = this;

Expand Down Expand Up @@ -443,6 +446,10 @@ class CSharpLanguage : public ScriptLanguage {
/* TODO? */ void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const override {}
/* TODO */ void add_global_constant(const StringName &p_variable, const Variant &p_value) override {}

/* SCRIPT GLOBAL CLASS FUNCTIONS */
virtual bool handles_global_class_type(const String &p_type) const override;
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const override;

/* DEBUGGER FUNCTIONS */
String debug_get_error() const override;
int debug_get_stack_level_count() const override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ public static bool IsGodotMustBeVariantAttribute(this INamedTypeSymbol symbol)
public static bool IsGodotClassNameAttribute(this INamedTypeSymbol symbol)
=> symbol.ToString() == GodotClasses.GodotClassNameAttr;

public static bool IsGodotGlobalClassAttribute(this INamedTypeSymbol symbol)
=> symbol.ToString() == GodotClasses.GlobalClassAttr;

public static bool IsSystemFlagsAttribute(this INamedTypeSymbol symbol)
=> symbol.ToString() == GodotClasses.SystemFlagsAttr;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public static class GodotClasses
public const string SignalAttr = "Godot.SignalAttribute";
public const string MustBeVariantAttr = "Godot.MustBeVariantAttribute";
public const string GodotClassNameAttr = "Godot.GodotClassNameAttribute";
public const string GlobalClassAttr = "Godot.GlobalClassAttribute";
public const string SystemFlagsAttr = "System.FlagsAttribute";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -547,25 +547,32 @@ private static bool TryGetMemberExportHint(
{
if (memberNamedType.InheritsFrom("GodotSharp", "Godot.Resource"))
{
string nativeTypeName = memberNamedType.GetGodotScriptNativeClassName()!;

hint = PropertyHint.ResourceType;
hintString = nativeTypeName;
hintString = GetTypeName(memberNamedType);

return true;
}

if (memberNamedType.InheritsFrom("GodotSharp", "Godot.Node"))
{
string nativeTypeName = memberNamedType.GetGodotScriptNativeClassName()!;

hint = PropertyHint.NodeType;
hintString = nativeTypeName;
hintString = GetTypeName(memberNamedType);

return true;
}
}

static string GetTypeName(INamedTypeSymbol memberSymbol)
{
if (memberSymbol.GetAttributes()
.Any(a => a.AttributeClass?.IsGodotGlobalClassAttribute() ?? false))
{
return memberSymbol.Name;
}

return memberSymbol.GetGodotScriptNativeClassName()!;
}

static bool GetStringArrayEnumHint(VariantType elementVariantType,
AttributeData exportAttr, out string? hintString)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;

#nullable enable

namespace Godot
{
/// <summary>
/// Exposes the target class as a global script class to Godot Engine.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class GlobalClassAttribute : Attribute
{
/// <summary>
/// Optional file path to a custom icon for representing this class in the Godot Editor.
/// </summary>
public string? IconPath { get; }

/// <summary>
/// Constructs a new GlobalClassAttribute Instance.
/// </summary>
/// <param name="iconPath">
/// An optional file path to a custom icon for representing this class in the Godot Editor.
/// </param>
public GlobalClassAttribute(string iconPath = "")
{
IconPath = iconPath;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public unsafe struct ManagedCallbacks
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_bool*, godot_array*, godot_dictionary*, godot_dictionary*, godot_ref*, void> ScriptManagerBridge_UpdateScriptClassInfo;
public delegate* unmanaged<IntPtr, godot_string*, godot_bool*, godot_bool*, godot_string*, 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,12 @@ static void LookupScriptForClass(Type type)

_pathTypeBiMap.Add(scriptPathAttr.Path, type);

if (Engine.IsEditorHint())
{
using godot_string scriptPath = Marshaling.ConvertStringToNative(scriptPathAttr.Path);
NativeFuncs.godotsharp_internal_editor_file_system_update_file(scriptPath);
}

if (AlcReloadCfg.IsAlcReloadingEnabled)
{
AddTypeForAlcReloading(type);
Expand Down Expand Up @@ -584,7 +590,8 @@ internal static godot_bool TryReloadRegisteredScriptWithClass(IntPtr scriptPtr)
}

[UnmanagedCallersOnly]
internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool* outTool,
internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_string* outClassName,
godot_bool* outTool, godot_bool* outGlobal, godot_string* outIconPath,
godot_array* outMethodsDest, godot_dictionary* outRpcFunctionsDest,
godot_dictionary* outEventSignalsDest, godot_ref* outBaseScript)
{
Expand All @@ -593,6 +600,8 @@ internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool*
// Performance is not critical here as this will be replaced with source generators.
var scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr);

*outClassName = Marshaling.ConvertStringToNative(scriptType.Name);

*outTool = scriptType.GetCustomAttributes(inherit: false)
.OfType<ToolAttribute>()
.Any().ToGodotBool();
Expand All @@ -607,6 +616,13 @@ internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool*
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();
*outIconPath = Marshaling.ConvertStringToNative(globalAttr?.IconPath);

// Methods

// Performance is not critical here as this will be replaced with source generators.
Expand Down Expand Up @@ -693,7 +709,7 @@ internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool*
}

*outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy(
(godot_dictionary)(rpcFunctions).NativeValue);
(godot_dictionary)rpcFunctions.NativeValue);

// Event signals

Expand Down Expand Up @@ -755,7 +771,10 @@ internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool*
catch (Exception e)
{
ExceptionUtils.LogException(e);
*outClassName = default;
*outTool = godot_bool.False;
*outGlobal = godot_bool.False;
*outIconPath = default;
*outMethodsDest = NativeFuncs.godotsharp_array_new();
*outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new();
*outEventSignalsDest = NativeFuncs.godotsharp_dictionary_new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ internal static partial Error godotsharp_stack_info_vector_resize(
internal static partial void godotsharp_stack_info_vector_destroy(
ref DebuggingUtils.godot_stack_info_vector p_stack_info_vector);

internal static partial void godotsharp_internal_editor_file_system_update_file(in godot_string p_script_path);

internal static partial void godotsharp_internal_script_debugger_send_error(in godot_string p_func,
in godot_string p_file, int p_line, in godot_string p_err, in godot_string p_descr,
godot_bool p_warning, in DebuggingUtils.godot_stack_info_vector p_stack_info_vector);
Expand Down
1 change: 1 addition & 0 deletions modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
<Compile Include="Core\Attributes\ExportCategoryAttribute.cs" />
<Compile Include="Core\Attributes\ExportGroupAttribute.cs" />
<Compile Include="Core\Attributes\ExportSubgroupAttribute.cs" />
<Compile Include="Core\Attributes\GlobalClassAttribute.cs" />
<Compile Include="Core\Attributes\GodotClassNameAttribute.cs" />
<Compile Include="Core\Attributes\MustBeVariantAttribute.cs" />
<Compile Include="Core\Attributes\RpcAttribute.cs" />
Expand Down
18 changes: 18 additions & 0 deletions modules/mono/glue/runtime_interop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
#include "core/os/os.h"
#include "core/string/string_name.h"

#ifdef TOOLS_ENABLED
#include "editor/editor_file_system.h"
#endif

#include "../interop_types.h"

#include "modules/mono/csharp_script.h"
Expand Down Expand Up @@ -304,6 +308,19 @@ void godotsharp_internal_new_csharp_script(Ref<CSharpScript> *r_dest) {
memnew_placement(r_dest, Ref<CSharpScript>(memnew(CSharpScript)));
}

void godotsharp_internal_editor_file_system_update_file(const String *p_script_path) {
#if TOOLS_ENABLED
// If the EditorFileSystem singleton is available, update the file;
// otherwise, the file will be updated when the singleton becomes available.
EditorFileSystem *efs = EditorFileSystem::get_singleton();
if (efs) {
efs->update_file(*p_script_path);
}
#else
CRASH_NOW_MSG("EditorFileSystem is only available when running in the Godot editor.");
#endif
}

bool godotsharp_internal_script_load(const String *p_path, Ref<CSharpScript> *r_dest) {
Ref<Resource> res = ResourceLoader::load(*p_path);
if (res.is_valid()) {
Expand Down Expand Up @@ -1396,6 +1413,7 @@ static const void *unmanaged_callbacks[]{
(void *)godotsharp_engine_get_singleton,
(void *)godotsharp_stack_info_vector_resize,
(void *)godotsharp_stack_info_vector_destroy,
(void *)godotsharp_internal_editor_file_system_update_file,
(void *)godotsharp_internal_script_debugger_send_error,
(void *)godotsharp_internal_script_debugger_is_active,
(void *)godotsharp_internal_object_get_associated_gchandle,
Expand Down
2 changes: 1 addition & 1 deletion modules/mono/mono_gd/gd_mono_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,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 *, bool *, Array *, Dictionary *, Dictionary *, Ref<CSharpScript> *);
using FuncScriptManagerBridge_UpdateScriptClassInfo = void(GD_CLR_STDCALL *)(const CSharpScript *, String *, bool *, bool *, String *, 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);
Expand Down

0 comments on commit 3dd8725

Please sign in to comment.