Skip to content

Commit

Permalink
Add C# resource export.
Browse files Browse the repository at this point in the history
  • Loading branch information
willnationsdev committed Aug 19, 2022
1 parent 0ba1046 commit ff475a1
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 4 deletions.
89 changes: 86 additions & 3 deletions modules/mono/csharp_script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,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 @@ -548,6 +549,37 @@ String CSharpLanguage::_get_indentation() const {
return "\t";
}

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

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, "CSharpScript");
if (script.is_valid()) {
String name = script->get_script_class_name();
if (name.length()) {
if (r_base_type) {
StringName base_name = script->get_script_class_base();
if (base_name != StringName()) {
*r_base_type = base_name;
} else {
*r_base_type = script->get_instance_base_type();
}
}
if (r_icon_path) {
*r_icon_path = script->get_script_class_icon_path();
}
return name;
}
}

if (r_base_type)
*r_base_type = String();
if (r_icon_path)
*r_icon_path = String();
return String();
}

String CSharpLanguage::debug_get_error() const {
return _debug_error;
}
Expand Down Expand Up @@ -1076,6 +1108,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
to_reload_state.push_back(script);
}

#ifdef TOOLS_ENABLED
EditorFileSystem *efs = EditorFileSystem::get_singleton();
#endif
for (Ref<CSharpScript> &script : to_reload_state) {
for (const ObjectID &obj_id : script->pending_reload_instances) {
Object *obj = ObjectDB::get_instance(obj_id);
Expand Down Expand Up @@ -1137,6 +1172,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
}
}

#ifdef TOOLS_ENABLED
efs->update_file(script->get_path());
#endif
script->pending_reload_instances.clear();
}

Expand Down Expand Up @@ -2790,7 +2828,9 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
}

#ifdef TOOLS_ENABLED
int hint_res = _try_get_member_export_hint(p_member, type, variant_type, /* allow_generics: */ true, hint, hint_string);
PropertyHint given_hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr));
String given_hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr);
int hint_res = _try_get_member_export_hint(p_member, type, variant_type, given_hint, given_hint_string, /* allow_generics: */ true, hint, hint_string);

ERR_FAIL_COND_V_MSG(hint_res == -1, false,
"Error while trying to determine information about the exported member: '" +
Expand Down Expand Up @@ -2818,7 +2858,7 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
}

#ifdef TOOLS_ENABLED
int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string) {
int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, PropertyHint p_given_hint, String p_given_hint_string, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string) {
if (p_variant_type == Variant::NIL) {
// System.Object (Variant)
return 1;
Expand Down Expand Up @@ -2886,6 +2926,16 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage

r_hint = PROPERTY_HINT_RESOURCE_TYPE;
r_hint_string = String(NATIVE_GDMONOCLASS_NAME(field_native_class));

if (p_type.type_class->has_attribute(CACHED_CLASS(GlobalAttribute))) {
MonoObject *attr = p_type.type_class->get_attribute(CACHED_CLASS(GlobalAttribute));
StringName script_class_name = CACHED_FIELD(GlobalAttribute, name)->get_string_value(attr);
if (script_class_name != StringName()) {
r_hint_string = script_class_name;
}
} else if (!p_given_hint_string.is_empty()) {
r_hint_string = p_given_hint_string;
}
} else if (p_variant_type == Variant::OBJECT && CACHED_CLASS(Node)->is_assignable_from(p_type.type_class)) {
GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(p_type.type_class);
CRASH_COND(field_native_class == nullptr);
Expand Down Expand Up @@ -2918,7 +2968,7 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
}

if (!preset_hint) {
int hint_res = _try_get_member_export_hint(p_member, elem_type, elem_variant_type, /* allow_generics: */ false, elem_hint, elem_hint_string);
int hint_res = _try_get_member_export_hint(p_member, elem_type, elem_variant_type, p_given_hint, p_given_hint_string, /* allow_generics: */ false, elem_hint, elem_hint_string);

ERR_FAIL_COND_V_MSG(hint_res == -1, -1, "Error while trying to determine information about the array element type.");

Expand Down Expand Up @@ -3406,6 +3456,8 @@ Error CSharpScript::reload(bool p_keep_state) {
update_script_class_info(this);

_update_exports();

_update_global_script_class_settings();
}

return OK;
Expand Down Expand Up @@ -3564,6 +3616,37 @@ Error CSharpScript::load_source_code(const String &p_path) {
return OK;
}

void CSharpScript::_update_global_script_class_settings() {
// Evaluate script's use of engine "Script Class" system.
if (script_class->has_attribute(CACHED_CLASS(GlobalAttribute))) {
MonoObject *attr = script_class->get_attribute(CACHED_CLASS(GlobalAttribute));
script_class_name = CACHED_FIELD(GlobalAttribute, name)->get_string_value(attr);
script_class_icon_path = CACHED_FIELD(GlobalAttribute, iconPath)->get_string_value(attr);
if (script_class_name.is_empty()) {
script_class_name = script_class->get_name();
}
} else {
script_class_name = String();
script_class_icon_path = String();
}

GDMonoClass *parent = script_class->get_parent_class();
while (parent) {
if (parent->has_attribute(CACHED_CLASS(GlobalAttribute))) {
MonoObject *attr = parent->get_attribute(CACHED_CLASS(GlobalAttribute));
script_class_base = CACHED_FIELD(GlobalAttribute, name)->get_string_value(attr);
if (script_class_base.is_empty()) {
script_class_base = script_class->get_name();
}
break;
}
parent = parent->get_parent_class();
}
if (script_class_base.is_empty()) {
script_class_base = get_instance_base_type();
}
}

void CSharpScript::_update_name() {
String path = get_path();

Expand Down
16 changes: 15 additions & 1 deletion modules/mono/csharp_script.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ class CSharpScript : public Script {
String source;
StringName name;

// For engine "Script Class" support, not affiliated with `GDMonoClass *script_class` property.
String script_class_name;
String script_class_base;
String script_class_icon_path;

SelfList<CSharpScript> script_list = this;

HashMap<StringName, Vector<SignalParameter>> _signals;
Expand Down Expand Up @@ -167,7 +172,7 @@ class CSharpScript : public Script {

bool _get_member_export(IMonoClassMember *p_member, bool p_inspect_export, PropertyInfo &r_prop_info, bool &r_exported);
#ifdef TOOLS_ENABLED
static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string);
static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, PropertyHint p_given_hint, String p_given_hint_string, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string);
#endif

CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error);
Expand Down Expand Up @@ -242,6 +247,11 @@ class CSharpScript : public Script {

Error load_source_code(const String &p_path);

String get_script_class_name() const { return script_class_name; }
String get_script_class_base() const { return script_class_base; }
String get_script_class_icon_path() const { return script_class_icon_path; }
void _update_global_script_class_settings();

CSharpScript();
~CSharpScript();
};
Expand Down Expand Up @@ -480,6 +490,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 CLASS FUNCTIONS */
virtual bool handles_global_class_type(const String &p_type) const;
virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL, String *r_icon_path = NULL) const;

/* 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 @@ -21,5 +21,16 @@ public ExportAttribute(PropertyHint hint = PropertyHint.None, string hintString
this.hint = hint;
this.hintString = hintString;
}

/// <summary>
/// Constructs a new ExportAttribute Instance. Allows properties to expose their data type as a non-C# Resource class in Godot.
/// </summary>
/// <param name="className">The name of a class by which to export the targeted property.</param>
public ExportAttribute(string className)
{
// Because `ScriptServer` is not exposed to the scripting API, there is no proper way of validating the passed in className.
this.hint = PropertyHint.ResourceType;
this.hintString = className;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;

namespace Godot
{
/// <summary>
/// Exposes the target class as a global script class to Godot Engine.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class GlobalAttribute : Attribute
{
private string name;
private string iconPath;

/// <summary>
/// Constructs a new GlobalAttribute Instance.
/// </summary>
/// <param name="name">The name under which to register the targeted class. Uses its typename by default.</param>
/// <param name="iconPath">An optional file path to a custom icon for representing this class in the Godot Editor.</param>
public GlobalAttribute(string name = "", string iconPath = "")
{
this.name = name ?? "";
this.iconPath = iconPath ?? "";
}
}
}
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 @@ -17,6 +17,7 @@
<Compile Include="Core\Attributes\AssemblyHasScriptsAttribute.cs" />
<Compile Include="Core\Attributes\DisableGodotGeneratorsAttribute.cs" />
<Compile Include="Core\Attributes\ExportAttribute.cs" />
<Compile Include="Core\Attributes\GlobalAttribute.cs" />
<Compile Include="Core\Attributes\GodotMethodAttribute.cs" />
<Compile Include="Core\Attributes\RPCAttribute.cs" />
<Compile Include="Core\Attributes\ScriptPathAttribute.cs" />
Expand Down
6 changes: 6 additions & 0 deletions modules/mono/mono_gd/gd_mono_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ void CachedData::clear_godot_api_cache() {
class_ExportAttribute = nullptr;
field_ExportAttribute_hint = nullptr;
field_ExportAttribute_hintString = nullptr;
class_GlobalAttribute = nullptr;
field_GlobalAttribute_name = nullptr;
field_GlobalAttribute_iconPath = nullptr;
class_SignalAttribute = nullptr;
class_ToolAttribute = nullptr;
class_RPCAttribute = nullptr;
Expand Down Expand Up @@ -275,6 +278,9 @@ void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(ExportAttribute, GODOT_API_CLASS(ExportAttribute));
CACHE_FIELD_AND_CHECK(ExportAttribute, hint, CACHED_CLASS(ExportAttribute)->get_field("hint"));
CACHE_FIELD_AND_CHECK(ExportAttribute, hintString, CACHED_CLASS(ExportAttribute)->get_field("hintString"));
CACHE_CLASS_AND_CHECK(GlobalAttribute, GODOT_API_CLASS(GlobalAttribute));
CACHE_FIELD_AND_CHECK(GlobalAttribute, name, CACHED_CLASS(GlobalAttribute)->get_field("name"));
CACHE_FIELD_AND_CHECK(GlobalAttribute, iconPath, CACHED_CLASS(GlobalAttribute)->get_field("iconPath"));
CACHE_CLASS_AND_CHECK(SignalAttribute, GODOT_API_CLASS(SignalAttribute));
CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute));
CACHE_CLASS_AND_CHECK(RPCAttribute, GODOT_API_CLASS(RPCAttribute));
Expand Down
3 changes: 3 additions & 0 deletions modules/mono/mono_gd/gd_mono_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ struct CachedData {
GDMonoClass *class_ExportAttribute = nullptr;
GDMonoField *field_ExportAttribute_hint = nullptr;
GDMonoField *field_ExportAttribute_hintString = nullptr;
GDMonoClass *class_GlobalAttribute = nullptr;
GDMonoField *field_GlobalAttribute_name = nullptr;
GDMonoField *field_GlobalAttribute_iconPath = nullptr;
GDMonoClass *class_SignalAttribute = nullptr;
GDMonoClass *class_ToolAttribute = nullptr;
GDMonoClass *class_RPCAttribute = nullptr;
Expand Down

0 comments on commit ff475a1

Please sign in to comment.