Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x] Allow exporting custom resources from/to any scripting language (GDScript, VisualScript, C#, NativeScript, PluginScript) #44879

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions core/bind/core_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "core/project_settings.h"
#include "core/script_language.h"

/**
* Time constants borrowed from loc_time.h
Expand Down Expand Up @@ -3005,6 +3006,89 @@ _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_iterations_per_second(int p_ips) {
Engine::get_singleton()->set_iterations_per_second(p_ips);
}
Expand Down
8 changes: 7 additions & 1 deletion core/io/resource_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,14 @@ void ResourceFormatLoader::get_recognized_extensions_for_type(const String &p_ty
}

void ResourceLoader::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) {
StringName native = ScriptServer::is_global_class(p_type) ? ScriptServer::get_global_class_native_base(p_type) : StringName(p_type);
for (int i = 0; i < loader_count; i++) {
loader[i]->get_recognized_extensions_for_type(p_type, p_extensions);
Ref<ResourceFormatLoader> current = loader[i];
if (!current->get_script().is_null()) {
current->get_recognized_extensions_for_type(p_type, p_extensions);
} else {
current->get_recognized_extensions_for_type(native, p_extensions);
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions core/register_core_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
#include "core/packed_data_container.h"
#include "core/path_remap.h"
#include "core/project_settings.h"
#include "core/script_language.h"
#include "core/translation.h"
#include "core/undo_redo.h"

Expand All @@ -87,6 +88,7 @@ static _Engine *_engine = nullptr;
static _ClassDB *_classdb = nullptr;
static _Marshalls *_marshalls = nullptr;
static _JSON *_json = nullptr;
static _ScriptServer *_script_server = nullptr;

static IP *ip = nullptr;

Expand Down Expand Up @@ -223,6 +225,7 @@ void register_core_types() {
_classdb = memnew(_ClassDB);
_marshalls = memnew(_Marshalls);
_json = memnew(_JSON);
_script_server = memnew(_ScriptServer);
}

void register_core_settings() {
Expand Down Expand Up @@ -252,6 +255,7 @@ void register_core_singletons() {
ClassDB::register_class<_JSON>();
ClassDB::register_class<Expression>();
ClassDB::register_class<Time>();
ClassDB::register_class<_ScriptServer>();

Engine::get_singleton()->add_singleton(Engine::Singleton("ProjectSettings", ProjectSettings::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("IP", IP::get_singleton()));
Expand All @@ -267,6 +271,7 @@ void register_core_singletons() {
Engine::get_singleton()->add_singleton(Engine::Singleton("InputMap", InputMap::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("JSON", _JSON::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("Time", Time::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("ScriptServer", _ScriptServer::get_singleton()));
}

void unregister_core_types() {
Expand All @@ -277,6 +282,7 @@ void unregister_core_types() {
memdelete(_classdb);
memdelete(_marshalls);
memdelete(_json);
memdelete(_script_server);

memdelete(_geometry);

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

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

ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES];
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_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 String &p_class) {
ERR_FAIL_COND_V(!global_classes.has(p_class), String());
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
Loading