Skip to content

Commit

Permalink
Ported godot pr: [3.x] Allow exporting custom resources from/to any s…
Browse files Browse the repository at this point in the history
…cripting language (GDScript, VisualScript, C#, NativeScript, PluginScript)

- willnationsdev
godotengine/godot#44879
Using the rebased version from Atlinx/godot@02d1f70 by Atlinx
  • Loading branch information
Relintai committed Sep 5, 2023
1 parent 2a7a431 commit 0a9c9ca
Show file tree
Hide file tree
Showing 26 changed files with 876 additions and 228 deletions.
85 changes: 85 additions & 0 deletions core/bind/core_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "core/io/marshalls.h"
#include "core/math/geometry.h"
#include "core/object/method_bind_ext.gen.inc"
#include "core/object/script_language.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"

Expand Down Expand Up @@ -3127,6 +3128,90 @@ _ClassDB::_ClassDB() {
}
_ClassDB::~_ClassDB() {
}

///////////////////////////////

bool _ScriptServer::_set(const StringName &p_name, const Variant &p_value) {
return false;
}

bool _ScriptServer::_get(const StringName &p_name, Variant &r_ret) const {
if (ScriptServer::is_global_class(p_name)) {
r_ret = ResourceLoader::load(ScriptServer::get_global_class_path(p_name), "Script");
return true;
}
return false;
}

void _ScriptServer::_get_property_list(List<PropertyInfo> *p_list) const {
ERR_FAIL_COND(!p_list);
List<StringName> names;
ScriptServer::get_global_class_list(&names);
for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
StringName n = E->get();
String class_name = String(n).get_file().get_extension();
p_list->push_back(PropertyInfo(Variant::OBJECT, class_name, PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_NETWORK, ResourceLoader::get_resource_type(ScriptServer::get_global_class_path(n))));
}
}

bool _ScriptServer::is_global_class(const StringName &p_class) const {
return ScriptServer::is_global_class(p_class);
}

String _ScriptServer::get_global_class_path(const String &p_class) const {
return ScriptServer::get_global_class_path(p_class);
}

StringName _ScriptServer::get_global_class_base(const String &p_class) const {
return ScriptServer::get_global_class_base(p_class);
}

StringName _ScriptServer::get_global_class_native_base(const String &p_class) const {
return ScriptServer::get_global_class_native_base(p_class);
}

StringName _ScriptServer::get_global_class_name(const String &p_path) const {
return ScriptServer::get_global_class_name(p_path);
}

Ref<Script> _ScriptServer::get_global_class_script(const StringName &p_class) const {
return ScriptServer::get_global_class_script(p_class);
}

Variant _ScriptServer::instantiate_global_class(const StringName &p_class) const {
return ScriptServer::instantiate_global_class(p_class);
}

Array _ScriptServer::get_global_class_list() const {
Array ret;
List<StringName> lst;
ScriptServer::get_global_class_list(&lst);
for (List<StringName>::Element *E = lst.front(); E; E = E->next()) {
ret.push_back(E->get());
}
return ret;
}

void _ScriptServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_global_class", "class"), &_ScriptServer::is_global_class);
ClassDB::bind_method(D_METHOD("get_global_class_path", "class"), &_ScriptServer::get_global_class_path);
ClassDB::bind_method(D_METHOD("get_global_class_base", "class"), &_ScriptServer::get_global_class_base);
ClassDB::bind_method(D_METHOD("get_global_class_native_base", "class"), &_ScriptServer::get_global_class_native_base);
ClassDB::bind_method(D_METHOD("get_global_class_name", "path"), &_ScriptServer::get_global_class_name);
ClassDB::bind_method(D_METHOD("get_global_class_script", "class"), &_ScriptServer::get_global_class_script);
ClassDB::bind_method(D_METHOD("instantiate_global_class", "class"), &_ScriptServer::instantiate_global_class);
ClassDB::bind_method(D_METHOD("get_global_class_list"), &_ScriptServer::get_global_class_list);
}

_ScriptServer::_ScriptServer() {
singleton = this;
}

_ScriptServer::~_ScriptServer() {
}

_ScriptServer *_ScriptServer::singleton = nullptr;

///////////////////////////////

void _Engine::set_physics_ticks_per_second(int p_ips) {
Expand Down
27 changes: 27 additions & 0 deletions core/bind/core_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,33 @@ class _ClassDB : public Object {
~_ClassDB();
};

class _ScriptServer : public Object {
GDCLASS(_ScriptServer, Object);

protected:
static void _bind_methods();
static _ScriptServer *singleton;

bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;

public:
static _ScriptServer *get_singleton() { return singleton; }

bool is_global_class(const StringName &p_class) const;
String get_global_class_path(const String &p_class) const;
StringName get_global_class_base(const String &p_class) const;
StringName get_global_class_native_base(const String &p_class) const;
StringName get_global_class_name(const String &p_path) const;
Ref<Script> get_global_class_script(const StringName &p_class) const;
Variant instantiate_global_class(const StringName &p_class) const;
Array get_global_class_list() const;

_ScriptServer();
~_ScriptServer();
};

class _Engine : public Object {
GDCLASS(_Engine, Object);

Expand Down
67 changes: 62 additions & 5 deletions core/object/script_language.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

#include "core/config/project_settings.h"
#include "core/core_string_names.h"
#include "core/io/resource_loader.h"

ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES];
int ScriptServer::_language_count = 0;
Expand Down Expand Up @@ -200,20 +201,28 @@ void ScriptServer::thread_exit() {
}

HashMap<StringName, ScriptServer::GlobalScriptClass> ScriptServer::global_classes;
HashMap<String, StringName> ScriptServer::global_class_paths;

void ScriptServer::global_classes_clear() {
global_classes.clear();
global_class_paths.clear();
}

void ScriptServer::add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path) {
ERR_FAIL_COND_MSG(p_class == p_base || (global_classes.has(p_base) && get_global_class_native_base(p_base) == p_class), "Cyclic inheritance in script class.");
ERR_FAIL_COND_MSG(p_class == StringName(), vformat("Attempted to register global script class at path '%s' without a class name.", p_path));
ERR_FAIL_COND_MSG(p_base == StringName(), vformat("Attempted to register global script class at path '%s' without a base name.", p_path));
ERR_FAIL_COND_MSG(p_language == StringName(), vformat("Attempted to register global script class at path '%s' without a language name.", p_path));
ERR_FAIL_COND_MSG(p_path.empty(), vformat("Attempted to register global script class named '%s' with an empty path.", p_class));
GlobalScriptClass g;
g.language = p_language;
g.path = p_path;
g.base = p_base;
global_classes[p_class] = g;
global_class_paths[p_path] = p_class;
}
void ScriptServer::remove_global_class(const StringName &p_class) {
global_class_paths.erase(global_classes[p_class].path);
global_classes.erase(p_class);
}
bool ScriptServer::is_global_class(const StringName &p_class) {
Expand All @@ -223,23 +232,71 @@ StringName ScriptServer::get_global_class_language(const StringName &p_class) {
ERR_FAIL_COND_V(!global_classes.has(p_class), StringName());
return global_classes[p_class].language;
}
String ScriptServer::get_global_class_path(const String &p_class) {
String ScriptServer::get_global_class_path(const StringName &p_class) {
ERR_FAIL_COND_V(!global_classes.has(p_class), String());
return global_classes[p_class].path;
}

StringName ScriptServer::get_global_class_base(const String &p_class) {
ERR_FAIL_COND_V(!global_classes.has(p_class), String());
StringName ScriptServer::get_global_class_name(const String &p_path) {
if (global_class_paths.has(p_path)) {
return global_class_paths[p_path];
}
return StringName();
}

StringName ScriptServer::get_global_class_base(const StringName &p_class) {
ERR_FAIL_COND_V(!global_classes.has(p_class), StringName());
return global_classes[p_class].base;
}
StringName ScriptServer::get_global_class_native_base(const String &p_class) {
ERR_FAIL_COND_V(!global_classes.has(p_class), String());
StringName ScriptServer::get_global_class_native_base(const StringName &p_class) {
ERR_FAIL_COND_V(!global_classes.has(p_class), StringName());
String base = global_classes[p_class].base;
while (global_classes.has(base)) {
base = global_classes[base].base;
}
return base;
}

Ref<Script> ScriptServer::get_global_class_script(const StringName &p_class) {
ERR_FAIL_COND_V_MSG(!ScriptServer::is_global_class(p_class), Ref<Script>(), vformat("Class to load '%s' is not a script class.", p_class));
if (!ScriptServer::is_global_class(p_class)) {
return Ref<Script>();
}

String path = ScriptServer::get_global_class_path(p_class);
return ResourceLoader::load(path, "Script");
}

Variant ScriptServer::instantiate_global_class(const StringName &p_class) {
ERR_FAIL_COND_V_MSG(!global_classes.has(p_class), Variant(), vformat("Class to instantiate '%s' is not a script class.", p_class));
String native = get_global_class_native_base(p_class);
Object *o = ClassDB::instance(native);
ERR_FAIL_COND_V_MSG(!o, Variant(), vformat("Could not instantiate global script class '%s'. It extends native class '%s' which is not instantiable.", p_class, native));

REF ref;
Reference *r = Object::cast_to<Reference>(o);
if (r) {
ref = REF(r);
}

Variant ret;
if (ref.is_valid()) {
ret = ref;
} else {
ret = o;
}

Ref<Script> s = get_global_class_script(p_class);
ERR_FAIL_COND_V_MSG(s.is_null(), Variant(), vformat("Failed to load global script class '%s'.", p_class));

o->set_script(s.get_ref_ptr());

ScriptInstance *si = o->get_script_instance();
ERR_FAIL_COND_V_MSG(!si, Variant(), vformat("Failed to create script instance for global script class '%s'.", p_class));

return ret;
}

void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) {
const StringName *K = nullptr;
List<StringName> classes;
Expand Down
12 changes: 9 additions & 3 deletions core/object/script_language.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "core/object/resource.h"

class ScriptLanguage;
class Script;

typedef void (*ScriptEditRequestFunction)(const String &p_path);

Expand All @@ -58,6 +59,7 @@ class ScriptServer {
};

static HashMap<StringName, GlobalScriptClass> global_classes;
static HashMap<String, StringName> global_class_paths;

public:
static ScriptEditRequestFunction edit_request_func;
Expand All @@ -80,9 +82,12 @@ class ScriptServer {
static void remove_global_class(const StringName &p_class);
static bool is_global_class(const StringName &p_class);
static StringName get_global_class_language(const StringName &p_class);
static String get_global_class_path(const String &p_class);
static StringName get_global_class_base(const String &p_class);
static StringName get_global_class_native_base(const String &p_class);
static String get_global_class_path(const StringName &p_class);
static StringName get_global_class_name(const String &p_path);
static StringName get_global_class_base(const StringName &p_class);
static StringName get_global_class_native_base(const StringName &p_class);
static Ref<Script> get_global_class_script(const StringName &p_class);
static Variant instantiate_global_class(const StringName &p_class);
static void get_global_class_list(List<StringName> *r_global_classes);
static void save_global_classes();

Expand Down Expand Up @@ -282,6 +287,7 @@ class ScriptLanguage {
virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const = 0;
virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { return ERR_UNAVAILABLE; }
virtual bool overrides_external_editor() { return false; }
virtual bool has_delayed_script_class_metadata() const { return false; }

virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_force, String &r_call_hint) { return ERR_UNAVAILABLE; }

Expand Down
3 changes: 3 additions & 0 deletions doc/classes/@GlobalScope.xml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@
<member name="Time" type="Time" setter="" getter="">
The [Time] singleton.
</member>
<member name="ScriptServer" type="ScriptServer" setter="" getter="">
The [ScriptServer] singleton.
</member>
<member name="TranslationServer" type="TranslationServer" setter="" getter="">
The [TranslationServer] singleton.
</member>
Expand Down
18 changes: 18 additions & 0 deletions doc/classes/EditorInterface.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@
[b]Warning:[/b] Removing and freeing this node will render the editor useless and may cause a crash.
</description>
</method>
<method name="get_class_icon">
<return type="Texture" />
<argument index="0" name="class" type="String" />
<argument index="1" name="fallback" type="String" />
<description>
Returns the editor icon bound to a class name or the [code]fallback[/code] class's icon if not found. The [code]fallback[/code] defaults to "Object". If still not found, returns [code]null[/code].
[b]Node:[/b] This includes icons from custom types (see [method EditorPlugin.add_custom_type]) and global script classes from [ScriptServer].
</description>
</method>
<method name="get_current_path" qualifiers="const">
<return type="String" />
<description>
Expand Down Expand Up @@ -88,6 +97,15 @@
[b]Warning:[/b] Removing and freeing this node will render a part of the editor useless and may cause a crash.
</description>
</method>
<method name="get_object_icon">
<return type="Texture" />
<argument index="0" name="object" type="Object" />
<argument index="1" name="fallback" type="String" />
<description>
Returns the editor icon bound to [Object] [code]object[/code] or the class [code]fallback[/code] if a type cannot be determined. If [code]object[/code] extends [Script], then return the editor icon bound to the scripted class, not the actual script. If still not found, return [code]null[/code].
[b]Note:[/b] if you need the editor icon for a script such as [GDScript], use [method get_class_icon].
</description>
</method>
<method name="get_open_scenes" qualifiers="const">
<return type="Array" />
<description>
Expand Down
Loading

0 comments on commit 0a9c9ca

Please sign in to comment.