This commit is contained in:
Will Nations 2021-11-11 09:28:33 +01:00 committed by GitHub
commit 8a2fb52f01
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
56 changed files with 1243 additions and 245 deletions

View file

@ -38,6 +38,7 @@
#include "core/io/marshalls.h"
#include "core/math/geometry_2d.h"
#include "core/math/geometry_3d.h"
#include "core/object/script_language.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
@ -2133,6 +2134,82 @@ void ClassDB::_bind_methods() {
} // namespace special
////// _ScriptServer //////
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 StringName &p_class) const {
return ::ScriptServer::get_global_class_path(p_class);
}
StringName ScriptServer::get_global_class_base(const StringName &p_class) const {
return ::ScriptServer::get_global_class_base(p_class);
}
StringName ScriptServer::get_global_class_native_base(const StringName &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 = NULL;
////// Engine //////
void Engine::set_physics_ticks_per_second(int p_ips) {

View file

@ -607,6 +607,33 @@ public:
} // namespace special
class ScriptServer : public Object {
GDCLASS(ScriptServer, Object);
static ScriptServer *singleton;
protected:
static void _bind_methods();
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 StringName &p_class) const;
StringName get_global_class_base(const StringName &p_class) const;
StringName get_global_class_native_base(const StringName &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() { singleton = this; }
};
class Engine : public Object {
GDCLASS(Engine, Object);

View file

@ -102,8 +102,11 @@ 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];
String type = !current->get_script().is_null() ? p_type : String(native);
current->get_recognized_extensions_for_type(type, p_extensions);
}
}

View file

@ -34,6 +34,7 @@
#include "core/core_string_names.h"
#include "core/debugger/engine_debugger.h"
#include "core/debugger/script_debugger.h"
#include "core/io/resource_loader.h"
#include <stdint.h>
@ -204,21 +205,29 @@ 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.is_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);
}
@ -231,18 +240,24 @@ StringName ScriptServer::get_global_class_language(const StringName &p_class) {
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;
@ -250,6 +265,46 @@ StringName ScriptServer::get_global_class_native_base(const String &p_class) {
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::instantiate(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;
RefCounted *r = Object::cast_to<RefCounted>(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);
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;

View file

@ -38,6 +38,7 @@
#include "core/templates/pair.h"
class ScriptLanguage;
class Script;
typedef void (*ScriptEditRequestFunction)(const String &p_path);
@ -55,10 +56,11 @@ class ScriptServer {
struct GlobalScriptClass {
StringName language;
String path;
String base;
StringName base;
};
static HashMap<StringName, GlobalScriptClass> global_classes;
static HashMap<String, StringName> global_class_paths;
public:
static ScriptEditRequestFunction edit_request_func;
@ -81,9 +83,12 @@ public:
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_base(const StringName &p_class);
static StringName get_global_class_native_base(const StringName &p_class);
static StringName get_global_class_name(const String &p_path);
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();
@ -293,6 +298,7 @@ public:
virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &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; }
@ -375,8 +381,8 @@ public:
virtual void frame();
virtual bool handles_global_class_type(const String &p_type) const { return false; }
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const { return String(); }
virtual bool handles_global_class_type(const StringName &p_type) const { return false; }
virtual StringName get_global_class_name(const String &p_path, StringName *r_base_type = nullptr, String *r_icon_path = nullptr) const { return StringName(); }
virtual ~ScriptLanguage() {}
};

View file

@ -71,6 +71,7 @@
#include "core/multiplayer/multiplayer_peer.h"
#include "core/multiplayer/multiplayer_replicator.h"
#include "core/object/class_db.h"
#include "core/object/script_language.h"
#include "core/object/undo_redo.h"
#include "core/os/main_loop.h"
#include "core/os/time.h"
@ -92,6 +93,7 @@ static core_bind::OS *_os = nullptr;
static core_bind::Engine *_engine = nullptr;
static core_bind::special::ClassDB *_classdb = nullptr;
static core_bind::Marshalls *_marshalls = nullptr;
static core_bind::ScriptServer *_script_server = nullptr;
static core_bind::EngineDebugger *_engine_debugger = nullptr;
static IP *ip = nullptr;
@ -257,6 +259,7 @@ void register_core_types() {
_engine = memnew(core_bind::Engine);
_classdb = memnew(core_bind::special::ClassDB);
_marshalls = memnew(core_bind::Marshalls);
_script_server = memnew(core_bind::ScriptServer);
_engine_debugger = memnew(core_bind::EngineDebugger);
}
@ -282,6 +285,7 @@ void register_core_singletons() {
GDREGISTER_CLASS(core_bind::Engine);
GDREGISTER_CLASS(core_bind::special::ClassDB);
GDREGISTER_CLASS(core_bind::Marshalls);
GDREGISTER_CLASS(core_bind::ScriptServer);
GDREGISTER_CLASS(TranslationServer);
GDREGISTER_VIRTUAL_CLASS(Input);
GDREGISTER_CLASS(InputMap);
@ -299,6 +303,7 @@ void register_core_singletons() {
Engine::get_singleton()->add_singleton(Engine::Singleton("Engine", core_bind::Engine::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("ClassDB", _classdb));
Engine::get_singleton()->add_singleton(Engine::Singleton("Marshalls", core_bind::Marshalls::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("ScriptServer", core_bind::ScriptServer::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("TranslationServer", TranslationServer::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("Input", Input::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("InputMap", InputMap::get_singleton()));
@ -327,6 +332,7 @@ void unregister_core_types() {
memdelete(_engine);
memdelete(_classdb);
memdelete(_marshalls);
memdelete(_script_server);
memdelete(_engine_debugger);
memdelete(_geometry_2d);

View file

@ -1149,6 +1149,9 @@
</member>
<member name="ResourceUID" type="ResourceUID" setter="" getter="">
</member>
<member name="ScriptServer" type="ScriptServer" setter="" getter="">
The [ScriptServer] singleton.
</member>
<member name="TextServerManager" type="TextServerManager" setter="" getter="">
The [TextServerManager] singleton.
</member>

View file

@ -50,20 +50,6 @@
Returns the path to the file at index [code]idx[/code].
</description>
</method>
<method name="get_file_script_class_extends" qualifiers="const">
<return type="String" />
<argument index="0" name="idx" type="int" />
<description>
Returns the base class of the script class defined in the file at index [code]idx[/code]. If the file doesn't define a script class using the [code]class_name[/code] syntax, this will return an empty string.
</description>
</method>
<method name="get_file_script_class_name" qualifiers="const">
<return type="String" />
<argument index="0" name="idx" type="int" />
<description>
Returns the name of the script class defined in the file at index [code]idx[/code]. If the file doesn't define a script class using the [code]class_name[/code] syntax, this will return an empty string.
</description>
</method>
<method name="get_file_type" qualifiers="const">
<return type="StringName" />
<argument index="0" name="idx" type="int" />

View file

@ -31,6 +31,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" qualifiers="const">
<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_command_palette" qualifiers="const">
<return type="EditorCommandPalette" />
<description>
@ -90,6 +99,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" qualifiers="const">
<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>

View file

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ScriptServer" inherits="Object" version="4.0">
<brief_description>
Global script class management singleton.
</brief_description>
<description>
ScriptServer manages all information related to global script classes in Godot projects, similar to [ClassDB] for engine classes. Scripts independently opt-in to become global classes. With it, you can check if a script has a global name or icon, what its base classes are, or even instantiate them directly.
[b]Note:[/b] This class shouldn't be instantiated directly. Instead, access the singleton through a global variable.
</description>
<tutorials>
<link>https://docs.godotengine.org/en/stable/getting_started/scripting/gdscript/gdscript_basics.html#classes</link>
</tutorials>
<methods>
<method name="get_global_class_base" qualifiers="const">
<return type="StringName" />
<argument index="0" name="class" type="StringName" />
<description>
Returns the class name that the script named [code]class[/code] directly extends. This may be an engine class or another global script class.
</description>
</method>
<method name="get_global_class_list" qualifiers="const">
<return type="Array" />
<description>
Returns the [StringName] names of all global script class names known by the ScriptServer.
</description>
</method>
<method name="get_global_class_name" qualifiers="const">
<return type="StringName" />
<argument index="0" name="path" type="String" />
<description>
Returns the global class name bound to the [Script] at [code]path[/code].
</description>
</method>
<method name="get_global_class_native_base" qualifiers="const">
<return type="StringName" />
<argument index="0" name="class" type="StringName" />
<description>
Returns the native engine class that the script named [code]class[/code] eventually extends.
</description>
</method>
<method name="get_global_class_path" qualifiers="const">
<return type="String" />
<argument index="0" name="class" type="StringName" />
<description>
Returns the file path to the script resource named [code]class[/code].
</description>
</method>
<method name="get_global_class_script" qualifiers="const">
<return type="Script" />
<argument index="0" name="class" type="StringName" />
<description>
</description>
</method>
<method name="instantiate_global_class" qualifiers="const">
<return type="Variant" />
<argument index="0" name="class" type="StringName" />
<description>
</description>
</method>
<method name="is_global_class" qualifiers="const">
<return type="bool" />
<argument index="0" name="class" type="StringName" />
<description>
</description>
</method>
</methods>
<constants>
</constants>
</class>

View file

@ -153,8 +153,9 @@ bool CreateDialog::_should_hide_type(const String &p_type) const {
String script_path = ScriptServer::get_global_class_path(p_type);
if (script_path.begins_with("res://addons/")) {
if (!EditorNode::get_singleton()->is_addon_plugin_enabled(script_path.get_slicec('/', 3))) {
return true; // Plugin is not enabled.
String cfg_path = script_path.plus_file("plugin.cfg");
if (FileAccess::exists(cfg_path) && !EditorNode::get_singleton()->is_addon_plugin_enabled(script_path.get_slicec('/', 3))) {
return true; // Plugin is not enabled. Addons are only "plugins" if they have a plugin.cfg.
}
}
}
@ -229,7 +230,11 @@ void CreateDialog::_configure_search_option_item(TreeItem *r_item, const String
r_item->set_text(0, p_type);
} else if (script_type) {
r_item->set_metadata(0, p_type);
r_item->set_text(0, p_type + " (" + ScriptServer::get_global_class_path(p_type).get_file() + ")");
String text = p_type;
if (!EDITOR_GET("interface/editors/create_dialog_hide_script_class_filepath")) {
text += vformat(" (%s)", ScriptServer::get_global_class_path(p_type).get_file());
}
r_item->set_text(0, text);
} else {
r_item->set_metadata(0, custom_type_parents[p_type]);
r_item->set_text(0, p_type);

View file

@ -889,12 +889,33 @@ void EditorData::get_plugin_window_layout(Ref<ConfigFile> p_layout) {
}
}
bool EditorData::script_class_is_parent(const String &p_class, const String &p_inherits) {
bool EditorData::class_equals_or_inherits(const StringName &p_class, const StringName &p_inherits) const {
if (p_class == p_inherits) {
return true;
}
if (ScriptServer::is_global_class(p_class)) {
return script_class_is_parent(p_class, p_inherits);
}
if (custom_types.has(p_inherits)) {
const Vector<EditorData::CustomType> &v = custom_types[p_inherits];
for (int i = 0; i < v.size(); i++) {
if (v[i].name == p_class) {
return true;
}
}
}
if (ClassDB::class_exists(p_class)) {
return ClassDB::is_parent_class(p_class, p_inherits);
}
return false;
}
bool EditorData::script_class_is_parent(const StringName &p_class, const StringName &p_inherits) const {
if (!ScriptServer::is_global_class(p_class)) {
return false;
}
String base = script_class_get_base(p_class);
Ref<Script> script = script_class_load_script(p_class);
Ref<Script> script = ScriptServer::get_global_class_script(p_class);
Ref<Script> base_script = script->get_base_script();
while (p_inherits != base) {
@ -911,25 +932,29 @@ bool EditorData::script_class_is_parent(const String &p_class, const String &p_i
return true;
}
StringName EditorData::script_class_get_base(const String &p_class) const {
Ref<Script> script = script_class_load_script(p_class);
StringName EditorData::script_class_get_base(const StringName &p_class) const {
if (!ScriptServer::is_global_class(p_class)) {
return StringName();
}
Ref<Script> script = ScriptServer::get_global_class_script(p_class);
if (script.is_null()) {
return StringName();
}
Ref<Script> base_script = script->get_base_script();
if (base_script.is_null()) {
return ScriptServer::get_global_class_base(p_class);
return ScriptServer::get_global_class_native_base(p_class);
}
return script->get_language()->get_global_class_name(base_script->get_path());
return ScriptServer::get_global_class_name(base_script->get_path());
}
Variant EditorData::script_class_instance(const String &p_class) {
Variant EditorData::script_class_instance(const StringName &p_class) const {
if (ScriptServer::is_global_class(p_class)) {
Variant obj = ClassDB::instantiate(ScriptServer::get_global_class_native_base(p_class));
if (obj) {
Ref<Script> script = script_class_load_script(p_class);
Ref<Script> script = ScriptServer::get_global_class_script(p_class);
if (script.is_valid()) {
((Object *)obj)->set_script(script);
}
@ -939,25 +964,16 @@ Variant EditorData::script_class_instance(const String &p_class) {
return Variant();
}
Ref<Script> EditorData::script_class_load_script(const String &p_class) const {
if (!ScriptServer::is_global_class(p_class)) {
return Ref<Script>();
}
String path = ScriptServer::get_global_class_path(p_class);
return ResourceLoader::load(path, "Script");
}
void EditorData::script_class_set_icon_path(const String &p_class, const String &p_icon_path) {
void EditorData::script_class_set_icon_path(const StringName &p_class, const String &p_icon_path) {
_script_class_icon_paths[p_class] = p_icon_path;
}
String EditorData::script_class_get_icon_path(const String &p_class) const {
String EditorData::script_class_get_icon_path(const StringName &p_class) const {
if (!ScriptServer::is_global_class(p_class)) {
return String();
}
String current = p_class;
StringName current = p_class;
String ret = _script_class_icon_paths[current];
while (ret.is_empty()) {
current = script_class_get_base(current);
@ -970,12 +986,25 @@ String EditorData::script_class_get_icon_path(const String &p_class) const {
return ret;
}
StringName EditorData::script_class_get_name(const String &p_path) const {
return _script_class_file_to_path.has(p_path) ? _script_class_file_to_path[p_path] : StringName();
}
void EditorData::script_class_set_name(const String &p_path, const StringName &p_class) {
_script_class_file_to_path[p_path] = p_class;
Ref<Script> EditorData::script_class_get_base_from_anonymous_path(const String &p_path) const {
StringName name = ScriptServer::get_global_class_name(p_path);
if (name != StringName()) {
return NULL;
}
Ref<Script> script = ResourceLoader::load(p_path, "Script");
if (script.is_null()) {
return NULL;
}
do {
if (ScriptServer::get_global_class_name(script->get_path()) != StringName()) {
return script;
}
if (script->get_path().find("::") != -1) {
WARN_PRINT_ONCE("If you remove a built-in script that derives a script class, inheritance cannot be determined. The entire script is removed.");
}
script = script->get_base_script();
} while (script.is_valid());
return NULL;
}
void EditorData::script_class_save_icon_paths() {
@ -1018,9 +1047,6 @@ void EditorData::script_class_load_icon_paths() {
for (const Variant &E : keys) {
String name = E.operator String();
_script_class_icon_paths[name] = d[name];
String path = ScriptServer::get_global_class_path(name);
script_class_set_name(path, name);
}
}
}

View file

@ -141,7 +141,6 @@ private:
bool _find_updated_instances(Node *p_root, Node *p_node, Set<String> &checked_paths);
HashMap<StringName, String> _script_class_icon_paths;
HashMap<String, StringName> _script_class_file_to_path;
public:
EditorPlugin *get_editor(Object *p_object);
@ -215,17 +214,15 @@ public:
void notify_edited_scene_changed();
void notify_resource_saved(const Ref<Resource> &p_resource);
bool script_class_is_parent(const String &p_class, const String &p_inherits);
StringName script_class_get_base(const String &p_class) const;
Variant script_class_instance(const String &p_class);
bool class_equals_or_inherits(const StringName &p_class, const StringName &p_inherits) const;
bool script_class_is_parent(const StringName &p_class, const StringName &p_inherits) const;
StringName script_class_get_base(const StringName &p_class) const;
Variant script_class_instance(const StringName &p_class) const;
Ref<Script> script_class_load_script(const String &p_class) const;
Ref<Script> script_class_get_base_from_anonymous_path(const String &p_path) const;
StringName script_class_get_name(const String &p_path) const;
void script_class_set_name(const String &p_path, const StringName &p_class);
String script_class_get_icon_path(const String &p_class) const;
void script_class_set_icon_path(const String &p_class, const String &p_icon_path);
String script_class_get_icon_path(const StringName &p_class) const;
void script_class_set_icon_path(const StringName &p_class, const String &p_icon_path);
void script_class_clear_icon_paths() { _script_class_icon_paths.clear(); }
void script_class_save_icon_paths();
void script_class_load_icon_paths();

View file

@ -149,18 +149,6 @@ uint64_t EditorFileSystemDirectory::get_file_modified_time(int p_idx) const {
return files[p_idx]->modified_time;
}
String EditorFileSystemDirectory::get_file_script_class_name(int p_idx) const {
return files[p_idx]->script_class_name;
}
String EditorFileSystemDirectory::get_file_script_class_extends(int p_idx) const {
return files[p_idx]->script_class_extends;
}
String EditorFileSystemDirectory::get_file_script_class_icon_path(int p_idx) const {
return files[p_idx]->script_class_icon_path;
}
StringName EditorFileSystemDirectory::get_file_type(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, files.size(), "");
return files[p_idx]->type;
@ -181,8 +169,6 @@ void EditorFileSystemDirectory::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_file", "idx"), &EditorFileSystemDirectory::get_file);
ClassDB::bind_method(D_METHOD("get_file_path", "idx"), &EditorFileSystemDirectory::get_file_path);
ClassDB::bind_method(D_METHOD("get_file_type", "idx"), &EditorFileSystemDirectory::get_file_type);
ClassDB::bind_method(D_METHOD("get_file_script_class_name", "idx"), &EditorFileSystemDirectory::get_file_script_class_name);
ClassDB::bind_method(D_METHOD("get_file_script_class_extends", "idx"), &EditorFileSystemDirectory::get_file_script_class_extends);
ClassDB::bind_method(D_METHOD("get_file_import_is_valid", "idx"), &EditorFileSystemDirectory::get_file_import_is_valid);
ClassDB::bind_method(D_METHOD("get_name"), &EditorFileSystemDirectory::get_name);
ClassDB::bind_method(D_METHOD("get_path"), &EditorFileSystemDirectory::get_path);
@ -347,6 +333,7 @@ void EditorFileSystem::_save_filesystem_cache() {
void EditorFileSystem::_thread_func(void *_userdata) {
EditorFileSystem *sd = (EditorFileSystem *)_userdata;
sd->init_compiled_lang_script_class_file_cache();
sd->_scan_filesystem();
}
@ -1443,29 +1430,38 @@ Vector<String> EditorFileSystem::_get_dependencies(const String &p_path) {
return ret;
}
String EditorFileSystem::_get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const {
StringName EditorFileSystem::_get_global_script_class(const String &p_type, const String &p_path, StringName *r_extends, String *r_icon_path) const {
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
if (ScriptServer::get_language(i)->handles_global_class_type(p_type)) {
String global_name;
String extends;
String icon_path;
global_name = ScriptServer::get_language(i)->get_global_class_name(p_path, &extends, &icon_path);
*r_extends = extends;
*r_icon_path = icon_path;
return global_name;
ScriptLanguage *lang = ScriptServer::get_language(i);
if (lang->handles_global_class_type(p_type)) {
if (lang->has_delayed_script_class_metadata() && compiled_lang_script_class_file_cache.has(p_path)) {
Dictionary d = compiled_lang_script_class_file_cache[p_path];
if (r_extends) {
*r_extends = d["base"].operator StringName();
}
if (r_icon_path) {
*r_icon_path = d.has("icon_path") ? d["icon_path"] : "";
}
return d["class"].operator StringName();
} else {
return lang->get_global_class_name(p_path, r_extends, r_icon_path);
}
}
}
*r_extends = String();
*r_icon_path = String();
return String();
if (r_extends) {
*r_extends = StringName();
}
if (r_icon_path) {
*r_icon_path = "";
}
return StringName();
}
void EditorFileSystem::_scan_script_classes(EditorFileSystemDirectory *p_dir) {
int filecount = p_dir->files.size();
const EditorFileSystemDirectory::FileInfo *const *files = p_dir->files.ptr();
for (int i = 0; i < filecount; i++) {
if (files[i]->script_class_name == String()) {
if (files[i]->script_class_name == StringName()) {
continue;
}
@ -1477,7 +1473,6 @@ void EditorFileSystem::_scan_script_classes(EditorFileSystemDirectory *p_dir) {
}
ScriptServer::add_global_class(files[i]->script_class_name, files[i]->script_class_extends, lang, p_dir->get_file_path(i));
EditorNode::get_editor_data().script_class_set_icon_path(files[i]->script_class_name, files[i]->script_class_icon_path);
EditorNode::get_editor_data().script_class_set_name(files[i]->file, files[i]->script_class_name);
}
for (int i = 0; i < p_dir->get_subdir_count(); i++) {
_scan_script_classes(p_dir->get_subdir(i));
@ -1507,6 +1502,59 @@ void EditorFileSystem::update_script_classes() {
ResourceSaver::add_custom_savers();
}
void EditorFileSystem::update_file_script_class_metadata(const String &p_path, const StringName &p_name, const StringName &p_base, const StringName &p_language, const String &p_icon_path) {
EditorFileSystemDirectory *fs = NULL;
int cpos = -1;
if (!_find_file(p_path, &fs, cpos)) {
if (!fs)
return;
}
EditorFileSystemDirectory::FileInfo *fi = fs->files[cpos];
fi->script_class_name = p_name;
fi->script_class_extends = p_base;
fi->script_class_icon_path = p_icon_path;
if (p_name != StringName() && !ScriptServer::is_global_class(p_name)) {
ScriptServer::add_global_class(p_name, p_base, p_language, p_path);
EditorData &ed = EditorNode::get_editor_data();
ed.script_class_set_icon_path(p_name, p_icon_path);
}
}
void EditorFileSystem::init_compiled_lang_script_class_file_cache() {
if (compiled_lang_script_class_file_cache.is_empty() && ProjectSettings::get_singleton()->has_setting("_global_script_classes")) {
Array script_classes = ProjectSettings::get_singleton()->get_setting("_global_script_classes");
Dictionary script_class_icons = ProjectSettings::get_singleton()->get_setting("_global_script_class_icons");
Set<StringName> compiled_language_names;
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
ScriptLanguage *lang = ScriptServer::get_language(i);
if (lang->has_delayed_script_class_metadata()) {
String n = lang->get_name();
compiled_language_names.insert(n);
}
}
for (int i = 0; i < script_classes.size(); i++) {
Dictionary d = script_classes[i];
StringName lg = d["language"];
if (compiled_language_names.has(lg)) {
StringName c = d["class"];
String p = d["path"];
String ip = script_class_icons[c];
d["icon_path"] = ip;
compiled_lang_script_class_file_cache[p] = d;
}
}
}
}
void EditorFileSystem::remove_compiled_lang_script_class_file_cache(const String &p_file) {
if (compiled_lang_script_class_file_cache.has(p_file)) {
compiled_lang_script_class_file_cache.erase(p_file);
}
}
void EditorFileSystem::_queue_update_script_classes() {
if (update_script_classes_queued.is_set()) {
return;

View file

@ -62,8 +62,8 @@ class EditorFileSystemDirectory : public Object {
String import_group_file;
Vector<String> deps;
bool verified = false; //used for checking changes
String script_class_name;
String script_class_extends;
StringName script_class_name;
StringName script_class_extends;
String script_class_icon_path;
};
@ -94,9 +94,6 @@ public:
Vector<String> get_file_deps(int p_idx) const;
bool get_file_import_is_valid(int p_idx) const;
uint64_t get_file_modified_time(int p_idx) const;
String get_file_script_class_name(int p_idx) const; //used for scripts
String get_file_script_class_extends(int p_idx) const; //used for scripts
String get_file_script_class_icon_path(int p_idx) const; //used for scripts
EditorFileSystemDirectory *get_parent();
@ -153,6 +150,8 @@ class EditorFileSystem : public Node {
void _save_late_updated_files();
HashMap<String, Dictionary> compiled_lang_script_class_file_cache; // keep track of script classes from compiled languages
EditorFileSystemDirectory *filesystem;
static EditorFileSystem *singleton;
@ -166,8 +165,8 @@ class EditorFileSystem : public Node {
Vector<String> deps;
bool import_valid = false;
String import_group_file;
String script_class_name;
String script_class_extends;
StringName script_class_name;
StringName script_class_extends;
String script_class_icon_path;
};
@ -232,7 +231,7 @@ class EditorFileSystem : public Node {
SafeFlag update_script_classes_queued;
void _queue_update_script_classes();
String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const;
StringName _get_global_script_class(const String &p_type, const String &p_path, StringName *r_extends = nullptr, String *r_icon_path = nullptr) const;
static Error _resource_import(const String &p_path);
@ -282,6 +281,9 @@ public:
void reimport_file_with_custom_parameters(const String &p_file, const String &p_importer, const Map<StringName, Variant> &p_custom_params);
void update_script_classes();
void update_file_script_class_metadata(const String &p_path, const StringName &p_name, const StringName &p_base, const StringName &p_language, const String &p_icon_path);
void remove_compiled_lang_script_class_file_cache(const String &p_file);
void init_compiled_lang_script_class_file_cache();
bool is_group_file(const String &p_path) const;
void move_group_file(const String &p_path, const String &p_new_path);

View file

@ -2428,7 +2428,7 @@ void EditorInspector::update_tree() {
base_type = script->get_instance_base_type();
}
while (script.is_valid()) {
StringName name = EditorNode::get_editor_data().script_class_get_name(script->get_path());
StringName name = ScriptServer::get_global_class_name(script->get_path());
String icon_path = EditorNode::get_editor_data().script_class_get_icon_path(name);
if (name != StringName() && icon_path.length()) {
category->icon = ResourceLoader::load(icon_path, "Texture");
@ -3460,7 +3460,7 @@ void EditorInspector::_update_script_class_properties(const Object &p_object, Li
Set<StringName> added;
for (const Ref<Script> &s : classes) {
String path = s->get_path();
String name = EditorNode::get_editor_data().script_class_get_name(path);
String name = ScriptServer::get_global_class_name(path);
if (name.is_empty()) {
if (!s->is_built_in()) {
name = path.get_file();

View file

@ -3911,14 +3911,14 @@ Ref<Script> EditorNode::get_object_custom_type_base(const Object *p_object) cons
if (script.is_valid()) {
// Uncommenting would break things! Consider adding a parameter if you need it.
// StringName name = EditorNode::get_editor_data().script_class_get_name(base_script->get_path());
// StringName name = ScriptServer::get_global_class_name(base_script->get_path());
// if (name != StringName())
// return name;
// should probably be deprecated in 4.x
StringName base = script->get_instance_base_type();
if (base != StringName() && EditorNode::get_editor_data().get_custom_types().has(base)) {
const Vector<EditorData::CustomType> &types = EditorNode::get_editor_data().get_custom_types()[base];
StringName native = script->get_instance_base_type();
if (native != StringName() && EditorNode::get_editor_data().get_custom_types().has(native)) {
const Vector<EditorData::CustomType> &types = EditorNode::get_editor_data().get_custom_types()[native];
Ref<Script> base_script = script;
while (base_script.is_valid()) {
@ -3946,7 +3946,7 @@ StringName EditorNode::get_object_custom_type_name(const Object *p_object) const
if (script.is_valid()) {
Ref<Script> base_script = script;
while (base_script.is_valid()) {
StringName name = EditorNode::get_editor_data().script_class_get_name(base_script->get_path());
StringName name = ScriptServer::get_global_class_name(base_script->get_path());
if (name != StringName()) {
return name;
}
@ -4008,7 +4008,7 @@ Ref<Texture2D> EditorNode::get_object_icon(const Object *p_object, const String
if (script.is_valid()) {
Ref<Script> base_script = script;
while (base_script.is_valid()) {
StringName name = EditorNode::get_editor_data().script_class_get_name(base_script->get_path());
StringName name = ScriptServer::get_global_class_name(base_script->get_path());
String icon_path = EditorNode::get_editor_data().script_class_get_icon_path(name);
Ref<ImageTexture> icon = _load_custom_class_icon(icon_path);
if (icon.is_valid()) {
@ -4048,14 +4048,15 @@ Ref<Texture2D> EditorNode::get_object_icon(const Object *p_object, const String
Ref<Texture2D> EditorNode::get_class_icon(const String &p_class, const String &p_fallback) const {
ERR_FAIL_COND_V_MSG(p_class.is_empty(), nullptr, "Class name cannot be empty.");
EditorData &ed = get_editor_data();
if (ScriptServer::is_global_class(p_class)) {
Ref<ImageTexture> icon;
Ref<Script> script = EditorNode::get_editor_data().script_class_load_script(p_class);
Ref<Script> script = ScriptServer::get_global_class_script(p_class);
StringName name = p_class;
while (script.is_valid()) {
name = EditorNode::get_editor_data().script_class_get_name(script->get_path());
String current_icon_path = EditorNode::get_editor_data().script_class_get_icon_path(name);
name = ScriptServer::get_global_class_name(script->get_path());
String current_icon_path = ed.script_class_get_icon_path(name);
icon = _load_custom_class_icon(current_icon_path);
if (icon.is_valid()) {
return icon;
@ -4068,7 +4069,7 @@ Ref<Texture2D> EditorNode::get_class_icon(const String &p_class, const String &p
}
}
const Map<String, Vector<EditorData::CustomType>> &p_map = EditorNode::get_editor_data().get_custom_types();
const Map<String, Vector<EditorData::CustomType>> &p_map = ed.get_custom_types();
for (const KeyValue<String, Vector<EditorData::CustomType>> &E : p_map) {
const Vector<EditorData::CustomType> &ct = E.value;
for (int i = 0; i < ct.size(); ++i) {

View file

@ -315,6 +315,14 @@ EditorCommandPalette *EditorInterface::get_command_palette() const {
return EditorCommandPalette::get_singleton();
}
Ref<Texture> EditorInterface::get_class_icon(const String &p_class, const String &p_fallback) const {
return EditorNode::get_singleton()->get_class_icon(p_class, p_fallback);
}
Ref<Texture> EditorInterface::get_object_icon(const Object *p_object, const String &p_fallback) const {
return EditorNode::get_singleton()->get_object_icon(p_object, p_fallback);
}
EditorInterface *EditorInterface::singleton = nullptr;
void EditorInterface::_bind_methods() {
@ -359,6 +367,9 @@ void EditorInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_distraction_free_mode", "enter"), &EditorInterface::set_distraction_free_mode);
ClassDB::bind_method(D_METHOD("is_distraction_free_mode_enabled"), &EditorInterface::is_distraction_free_mode_enabled);
ClassDB::bind_method(D_METHOD("get_class_icon", "class", "fallback"), &EditorInterface::get_class_icon);
ClassDB::bind_method(D_METHOD("get_object_icon", "object", "fallback"), &EditorInterface::get_object_icon);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distraction_free_mode"), "set_distraction_free_mode", "is_distraction_free_mode_enabled");
}

View file

@ -122,6 +122,9 @@ public:
void set_distraction_free_mode(bool p_enter);
bool is_distraction_free_mode_enabled() const;
Ref<Texture> get_class_icon(const String &p_name, const String &p_fallback) const;
Ref<Texture> get_object_icon(const Object *p_object, const String &p_fallback) const;
EditorInterface();
};

View file

@ -3532,13 +3532,14 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
EditorPropertyResource *editor = memnew(EditorPropertyResource);
editor->setup(p_object, p_path, p_hint == PROPERTY_HINT_RESOURCE_TYPE ? p_hint_text : "Resource");
EditorData &ed = EditorNode::get_editor_data();
if (p_hint == PROPERTY_HINT_RESOURCE_TYPE) {
String open_in_new = EDITOR_GET("interface/inspector/resources_to_open_in_new_inspector");
for (int i = 0; i < open_in_new.get_slice_count(","); i++) {
String type = open_in_new.get_slicec(',', i).strip_edges();
for (int j = 0; j < p_hint_text.get_slice_count(","); j++) {
String inherits = p_hint_text.get_slicec(',', j);
if (ClassDB::is_parent_class(inherits, type)) {
if (ed.class_equals_or_inherits(inherits, type)) {
editor->set_use_sub_inspector(false);
}
}

View file

@ -58,7 +58,15 @@ void EditorResourcePicker::_update_resource() {
assign_button->set_text(edited_resource->get_path().get_file());
assign_button->set_tooltip(edited_resource->get_path());
} else {
assign_button->set_text(edited_resource->get_class());
String class_name = edited_resource->get_class();
Ref<Script> res_script = edited_resource->get_script();
if (res_script.is_valid()) {
String script_name = ScriptServer::get_global_class_name(res_script->get_path());
if (!script_name.is_empty()) {
class_name = script_name;
}
}
assign_button->set_text(class_name);
}
if (edited_resource->get_path().is_resource_file()) {
@ -116,9 +124,19 @@ void EditorResourcePicker::_file_selected(const String &p_path) {
if (base_type != "") {
bool any_type_matches = false;
StringName res_type = loaded_resource->get_class();
Ref<Script> res_script = loaded_resource->get_script();
if (res_script.is_valid()) {
StringName script_type = ScriptServer::get_global_class_name(res_script->get_path());
if (script_type != StringName()) {
res_type = script_type;
}
}
for (int i = 0; i < base_type.get_slice_count(","); i++) {
String base = base_type.get_slice(",", i);
if (loaded_resource->is_class(base)) {
if (EditorNode::get_editor_data().class_equals_or_inherits(res_type, base)) {
any_type_matches = true;
break;
}
@ -184,7 +202,9 @@ void EditorResourcePicker::_update_menu_items() {
paste_valid = true;
} else {
for (int i = 0; i < base_type.get_slice_count(","); i++) {
if (ClassDB::is_parent_class(cb->get_class(), base_type.get_slice(",", i))) {
StringName script_name = ScriptServer::get_global_class_name(cb->get_path());
StringName class_name = script_name != StringName() ? script_name : StringName(cb->get_class());
if (EditorNode::get_editor_data().class_equals_or_inherits(class_name, base_type.get_slice(",", i))) {
paste_valid = true;
break;
}
@ -231,6 +251,10 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
for (int i = 0; i < base_type.get_slice_count(","); i++) {
String base = base_type.get_slice(",", i);
ResourceLoader::get_recognized_extensions_for_type(base, &extensions);
if (ScriptServer::is_global_class(base)) {
String native = ScriptServer::get_global_class_native_base(base);
ResourceLoader::get_recognized_extensions_for_type(native, &extensions);
}
}
Set<String> valid_extensions;
@ -294,10 +318,20 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
propvalues.push_back(p);
}
String orig_type = edited_resource->get_class();
Object *inst = ClassDB::instantiate(orig_type);
Ref<Resource> unique_resource = Ref<Resource>(Object::cast_to<Resource>(inst));
ERR_FAIL_COND(unique_resource.is_null());
Ref<Resource> inst;
Ref<Script> res_script = edited_resource->get_script();
if (res_script.is_valid()) {
StringName script_name = ScriptServer::get_global_class_name(res_script->get_path());
if (ScriptServer::is_global_class(script_name)) {
inst = ScriptServer::instantiate_global_class(script_name);
}
}
if (inst.is_null()) {
inst = ClassDB::instantiate(edited_resource->get_class());
}
ERR_FAIL_COND_MSG(inst.is_null(), "Failed to instantiate resource during Make Unique.");
Ref<Resource> unique_resource = Ref<Resource>(inst);
ERR_FAIL_COND_MSG(unique_resource.is_null(), "Failed to copy resource reference during Make Unique.");
for (const Pair<String, Variant> &p : propvalues) {
unique_resource->set(p.first, p.second);
@ -357,13 +391,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
Variant obj;
if (ScriptServer::is_global_class(intype)) {
obj = ClassDB::instantiate(ScriptServer::get_global_class_native_base(intype));
if (obj) {
Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(intype));
if (script.is_valid()) {
((Object *)obj)->set_script(script);
}
}
obj = ScriptServer::instantiate_global_class(intype);
} else {
obj = ClassDB::instantiate(intype);
}
@ -496,7 +524,9 @@ void EditorResourcePicker::_get_allowed_types(bool p_with_convert, Set<String> *
List<StringName> allowed_subtypes;
List<StringName> inheriters;
ClassDB::get_inheriters_from_class(base, &inheriters);
if (!ScriptServer::is_global_class(base)) {
ClassDB::get_inheriters_from_class(base, &inheriters);
}
for (const StringName &subtype_name : inheriters) {
p_vector->insert(subtype_name);
allowed_subtypes.push_back(subtype_name);
@ -636,21 +666,25 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_
for (Set<String>::Element *E = allowed_types.front(); E; E = E->next()) {
String at = E->get().strip_edges();
if (at == "StandardMaterial3D" && ClassDB::is_parent_class(dropped_resource->get_class(), "Texture2D")) {
EditorData &ed = EditorNode::get_editor_data();
StringName script_name = ScriptServer::get_global_class_name(dropped_resource->get_path());
String class_name = script_name != StringName() ? script_name : StringName(dropped_resource->get_class());
if (at == "StandardMaterial3D" && ed.class_equals_or_inherits(class_name, "Texture2D")) {
Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D);
mat->set_texture(StandardMaterial3D::TextureParam::TEXTURE_ALBEDO, dropped_resource);
dropped_resource = mat;
break;
}
if (at == "ShaderMaterial" && ClassDB::is_parent_class(dropped_resource->get_class(), "Shader")) {
if (at == "ShaderMaterial" && ClassDB::is_parent_class(class_name, "Shader")) {
Ref<ShaderMaterial> mat = memnew(ShaderMaterial);
mat->set_shader(dropped_resource);
dropped_resource = mat;
break;
}
if (at == "Font" && ClassDB::is_parent_class(dropped_resource->get_class(), "FontData")) {
if (at == "Font" && ClassDB::is_parent_class(class_name, "FontData")) {
Ref<Font> font = memnew(Font);
font->add_data(dropped_resource);
dropped_resource = font;

View file

@ -1958,7 +1958,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
Ref<Script> root_script = nullptr;
if (ScriptServer::is_global_class(root_type)) {
root_script = ResourceLoader::load(ScriptServer::get_global_class_path(root_type));
root_script = ScriptServer::get_global_class_script(root_type);
root_type = ScriptServer::get_global_class_base(root_type);
}

View file

@ -96,18 +96,7 @@ void VersionControlEditorPlugin::_initialize_vcs() {
const int id = set_up_choice->get_selected_id();
String selected_addon = set_up_choice->get_item_text(id);
String path = ScriptServer::get_global_class_path(selected_addon);
Ref<Script> script = ResourceLoader::load(path);
ERR_FAIL_COND_MSG(!script.is_valid(), "VCS Addon path is invalid");
EditorVCSInterface *vcs_interface = memnew(EditorVCSInterface);
ScriptInstance *addon_script_instance = script->instance_create(vcs_interface);
ERR_FAIL_COND_MSG(!addon_script_instance, "Failed to create addon script instance.");
// The addon is attached as a script to the VCS interface as a proxy end-point
vcs_interface->set_script_and_instance(script, addon_script_instance);
EditorVCSInterface *vcs_interface = Object::cast_to<EditorVCSInterface>(ScriptServer::instantiate_global_class(selected_addon));
EditorVCSInterface::set_singleton(vcs_interface);
EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &VersionControlEditorPlugin::_refresh_stage_area));
@ -330,14 +319,12 @@ void VersionControlEditorPlugin::register_editor() {
void VersionControlEditorPlugin::fetch_available_vcs_addon_names() {
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
EditorData &ed = EditorNode::get_editor_data();
StringName base = EditorVCSInterface::get_class_static();
for (int i = 0; i != global_classes.size(); i++) {
String path = ScriptServer::get_global_class_path(global_classes[i]);
Ref<Script> script = ResourceLoader::load(path);
ERR_FAIL_COND(script.is_null());
if (script->get_instance_base_type() == "EditorVCSInterface") {
available_addons.push_back(global_classes[i]);
for (StringName type : global_classes) {
if (ed.script_class_is_parent(type, base)) {
available_addons.push_back(type);
}
}
}

View file

@ -1138,17 +1138,13 @@ void VisualShaderEditor::update_custom_nodes() {
List<StringName> class_list;
ScriptServer::get_global_class_list(&class_list);
Dictionary added;
for (int i = 0; i < class_list.size(); i++) {
if (ScriptServer::get_global_class_native_base(class_list[i]) == "VisualShaderNodeCustom") {
String script_path = ScriptServer::get_global_class_path(class_list[i]);
Ref<Resource> res = ResourceLoader::load(script_path);
ERR_FAIL_COND(res.is_null());
ERR_FAIL_COND(!res->is_class("Script"));
Ref<Script> script = Ref<Script>(res);
Ref<VisualShaderNodeCustom> ref;
ref.instantiate();
ref->set_script(script);
EditorData &ed = EditorNode::get_editor_data();
StringName base = VisualShaderNodeCustom::get_class_static();
for (StringName type : class_list) {
if (ed.script_class_is_parent(type, base)) {
Ref<Script> script = ScriptServer::get_global_class_script(type);
Ref<VisualShaderNodeCustom> ref = ScriptServer::instantiate_global_class(type);
ERR_CONTINUE_MSG(ref.is_null(), vformat("Failed to instantiate %s '%s'. Skipping its inclusion in the editor.", VisualShaderNodeCustom::get_class_static(), type));
String name;
if (ref->has_method("_get_name")) {

View file

@ -576,23 +576,65 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
return;
}
editor_data->get_undo_redo().create_action(TTR("Detach Script"));
editor_data->get_undo_redo().add_do_method(editor, "push_item", (Script *)nullptr);
Array update_array;
EditorData &ed = EditorNode::get_editor_data();
for (int i = 0; i < selection.size(); i++) {
Node *n = Object::cast_to<Node>(selection[i]);
Ref<Script> existing = n->get_script();
Ref<Script> empty = EditorNode::get_singleton()->get_object_custom_type_base(n);
if (existing != empty) {
editor_data->get_undo_redo().add_do_method(n, "set_script", empty);
editor_data->get_undo_redo().add_undo_method(n, "set_script", existing);
Ref<Script> base = NULL;
if (existing.is_valid()) {
StringName script_class_name = ScriptServer::get_global_class_name(existing->get_path());
if (script_class_name != StringName()) {
update_array.clear();
print_error(vformat("Unable to remove script class '%s'. Can only remove anonymous scripts.", script_class_name));
break;
}
base = ed.script_class_get_base_from_anonymous_path(existing->get_path());
if (base.is_null()) {
const Map<String, Vector<EditorData::CustomType>> &ct = EditorNode::get_editor_data().get_custom_types();
if (ct.has(n->get_class())) {
const Vector<EditorData::CustomType> &v = ct[n->get_class()];
String ct_name;
for (int j = 0; j < v.size() && !ct_name.is_empty(); j++) {
if (v[j].script == existing) {
ct_name = v[j].name;
}
}
if (!ct_name.is_empty()) {
update_array.clear();
print_error(vformat("Unable to remove custom type '%s'. Can only remove anonymous scripts.", ct_name));
break;
}
}
base = EditorNode::get_singleton()->get_object_custom_type_base(n);
}
}
if (existing != base) {
Array an_update;
an_update.push_back(n);
an_update.push_back(base);
an_update.push_back(existing);
update_array.push_back(an_update);
}
}
editor_data->get_undo_redo().add_do_method(this, "_update_script_button");
editor_data->get_undo_redo().add_undo_method(this, "_update_script_button");
if (update_array.size()) {
editor_data->get_undo_redo().create_action(TTR("Detach Script"));
editor_data->get_undo_redo().add_do_method(editor, "push_item", (Script *)NULL);
for (int i = 0; i < update_array.size(); i++) {
Array an_update = update_array[i];
Node *n = an_update[0];
Ref<Script> do_script = an_update[1];
Ref<Script> undo_script = an_update[2];
editor_data->get_undo_redo().add_do_method(n, "set_script", do_script);
editor_data->get_undo_redo().add_undo_method(n, "set_script", undo_script);
}
editor_data->get_undo_redo().add_do_method(this, "_update_script_button");
editor_data->get_undo_redo().add_undo_method(this, "_update_script_button");
editor_data->get_undo_redo().commit_action();
editor_data->get_undo_redo().commit_action();
}
} break;
case TOOL_MOVE_UP:
case TOOL_MOVE_DOWN: {
@ -1110,10 +1152,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (TOOL_CREATE_FAVORITE == p_tool) {
String name = selected_favorite_root.get_slicec(' ', 0);
if (ScriptServer::is_global_class(name)) {
new_node = Object::cast_to<Node>(ClassDB::instantiate(ScriptServer::get_global_class_native_base(name)));
Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(name), "Script");
if (new_node && script.is_valid()) {
new_node->set_script(script);
new_node = Object::cast_to<Node>(ScriptServer::instantiate_global_class(name));
if (new_node) {
new_node->set_name(name);
}
} else {
@ -1222,6 +1262,7 @@ void SceneTreeDock::_notification(int p_what) {
button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
button_instance->set_icon(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")));
button_create_script->set_icon(get_theme_icon(SNAME("ScriptCreate"), SNAME("EditorIcons")));
button_extend_script->set_icon(get_theme_icon(SNAME("ScriptExtend"), SNAME("EditorIcons")));
button_detach_script->set_icon(get_theme_icon(SNAME("ScriptRemove"), SNAME("EditorIcons")));
button_tree_menu->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
@ -1303,6 +1344,7 @@ void SceneTreeDock::_notification(int p_what) {
button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
button_instance->set_icon(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")));
button_create_script->set_icon(get_theme_icon(SNAME("ScriptCreate"), SNAME("EditorIcons")));
button_extend_script->set_icon(get_theme_icon(SNAME("ScriptExtend"), SNAME("EditorIcons")));
button_detach_script->set_icon(get_theme_icon(SNAME("ScriptRemove"), SNAME("EditorIcons")));
button_2d->set_icon(get_theme_icon(SNAME("Node2D"), SNAME("EditorIcons")));
button_3d->set_icon(get_theme_icon(SNAME("Node3D"), SNAME("EditorIcons")));
@ -2103,29 +2145,48 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
void SceneTreeDock::_update_script_button() {
if (!profile_allow_script_editing) {
button_create_script->hide();
button_extend_script->hide();
button_detach_script->hide();
} else if (editor_selection->get_selection().size() == 0) {
button_create_script->hide();
button_extend_script->hide();
button_detach_script->hide();
} else if (editor_selection->get_selection().size() == 1) {
Node *n = editor_selection->get_selected_node_list()[0];
if (n->get_script().is_null()) {
button_create_script->show();
button_detach_script->hide();
Ref<Script> s = n->get_script();
if (s.is_valid()) {
if (ScriptServer::get_global_class_name(s->get_path()) != StringName()) {
button_create_script->hide();
button_extend_script->show();
button_detach_script->hide();
} else {
button_create_script->hide();
button_extend_script->hide();
button_detach_script->show();
}
} else {
button_create_script->hide();
button_detach_script->show();
button_create_script->show();
button_extend_script->hide();
button_detach_script->hide();
}
} else {
button_create_script->hide();
Array selection = editor_selection->get_selected_nodes();
for (int i = 0; i < selection.size(); i++) {
Node *n = Object::cast_to<Node>(selection[i]);
if (!n->get_script().is_null()) {
button_detach_script->show();
Ref<Script> s = n->get_script();
if (s.is_valid()) {
if (ScriptServer::get_global_class_name(s->get_path()) != StringName()) {
button_extend_script->show();
button_detach_script->hide();
} else {
button_extend_script->hide();
button_detach_script->show();
}
return;
}
}
button_extend_script->hide();
button_detach_script->hide();
}
}
@ -2301,6 +2362,11 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_prop
List<PropertyInfo> pinfo;
n->get_property_list(&pinfo);
Ref<Script> s = n->get_script();
if (s.is_valid() && ScriptServer::get_global_class_name(s->get_path()) != StringName()) {
n->set_script(Variant());
}
for (const PropertyInfo &E : pinfo) {
if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
@ -2701,7 +2767,8 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
existing_script = selected->get_script();
if (EditorNode::get_singleton()->get_object_custom_type_base(selected) == existing_script) {
if (EditorNode::get_singleton()->get_object_custom_type_base(selected) == existing_script ||
(existing_script.is_valid() && ScriptServer::get_global_class_name(existing_script->get_path()) != StringName())) {
existing_script_removable = false;
}
}
@ -2915,7 +2982,7 @@ void SceneTreeDock::attach_script_to_selected(bool p_extend) {
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
ScriptLanguage *l = ScriptServer::get_language(i);
if (l->get_type() == existing->get_class()) {
String name = l->get_global_class_name(existing->get_path());
String name = ScriptServer::get_global_class_name(existing->get_path());
if (ScriptServer::is_global_class(name) && EDITOR_GET("interface/editors/derive_script_globals_by_name").operator bool()) {
inherits = name;
} else if (l->can_inherit_from_file()) {
@ -3280,6 +3347,14 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
filter_hbc->add_child(button_create_script);
button_create_script->hide();
button_extend_script = memnew(Button);
button_create_script->set_flat(true);
button_extend_script->connect("pressed", callable_mp(this, &SceneTreeDock::_tool_selected), make_binds(TOOL_EXTEND_SCRIPT, false));
button_extend_script->set_tooltip(TTR("Attach a new script extending the selected node's script."));
button_extend_script->set_shortcut(ED_GET_SHORTCUT("scene_tree/extend_script"));
filter_hbc->add_child(button_extend_script);
button_extend_script->hide();
button_detach_script = memnew(Button);
button_detach_script->set_flat(true);
button_detach_script->connect("pressed", callable_mp(this, &SceneTreeDock::_tool_selected), make_binds(TOOL_DETACH_SCRIPT, false));
@ -3421,6 +3496,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
EDITOR_DEF("interface/editors/show_scene_tree_root_selection", true);
EDITOR_DEF("interface/editors/derive_script_globals_by_name", true);
EDITOR_DEF("interface/editors/create_dialog_hide_script_class_filepath", false);
EDITOR_DEF("_use_favorites_root_selection", false);
Resource::_update_configuration_warning = _update_configuration_warning;

View file

@ -115,6 +115,7 @@ class SceneTreeDock : public VBoxContainer {
Button *button_add;
Button *button_instance;
Button *button_create_script;
Button *button_extend_script;
Button *button_detach_script;
Button *button_tree_menu;

View file

@ -199,15 +199,16 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent, bool p_scroll
Color accent = get_theme_color(SNAME("accent_color"), SNAME("Editor"));
Ref<Script> script = p_node->get_script();
if (!script.is_null() && EditorNode::get_singleton()->get_object_custom_type_base(p_node) != script) {
if (!script.is_null() && ScriptServer::get_global_class_name(script->get_path()) == StringName() &&
EditorNode::get_singleton()->get_object_custom_type_base(p_node) != script) {
//has script
item->add_button(0, get_theme_icon(SNAME("Script"), SNAME("EditorIcons")), BUTTON_SCRIPT);
} else {
//has no script (or script is a custom type)
//has no script (or script is a custom type / script class)
item->set_custom_color(0, get_theme_color(SNAME("disabled_font_color"), SNAME("Editor")));
item->set_selectable(0, false);
if (!script.is_null()) { // make sure to mark the script if a custom type
if (!script.is_null()) { // make sure to mark the script if a custom type or script class
item->add_button(0, get_theme_icon(SNAME("Script"), SNAME("EditorIcons")), BUTTON_SCRIPT);
item->set_button_disabled(0, item->get_button_count(0) - 1, true);
}
@ -332,9 +333,10 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent, bool p_scroll
}
Ref<Script> script = p_node->get_script();
if (!script.is_null()) {
if (script.is_valid()) {
item->add_button(0, get_theme_icon(SNAME("Script"), SNAME("EditorIcons")), BUTTON_SCRIPT, false, TTR("Open Script:") + " " + script->get_path());
if (EditorNode::get_singleton()->get_object_custom_type_base(p_node) == script) {
if (EditorNode::get_singleton()->get_object_custom_type_base(p_node) == script ||
ScriptServer::get_global_class_name(script->get_path()) != StringName()) {
item->set_button_color(0, item->get_button_count(0) - 1, Color(1, 1, 1, 0.5));
}
}

View file

@ -2163,9 +2163,8 @@ bool Main::start() {
return false;
}
} else { // Not based on script path.
if (!editor && !ClassDB::class_exists(main_loop_type) && ScriptServer::is_global_class(main_loop_type)) {
String script_path = ScriptServer::get_global_class_path(main_loop_type);
Ref<Script> script_res = ResourceLoader::load(script_path);
if (!editor && ScriptServer::is_global_class(main_loop_type)) {
Ref<Script> script_res = ScriptServer::get_global_class_script(main_loop_type);
StringName script_base = ScriptServer::get_global_class_native_base(main_loop_type);
Object *obj = ClassDB::instantiate(script_base);
MainLoop *script_loop = Object::cast_to<MainLoop>(obj);

View file

@ -43,13 +43,13 @@
</method>
</methods>
<members>
<member name="class_name" type="String" setter="set_class_name" getter="get_class_name" default="&quot;&quot;">
<member name="class_name" type="StringName" setter="set_class_name" getter="get_class_name" default="&amp;&quot;&quot;">
</member>
<member name="library" type="GDNativeLibrary" setter="set_library" getter="get_library">
</member>
<member name="script_class_icon_path" type="String" setter="set_script_class_icon_path" getter="get_script_class_icon_path" default="&quot;&quot;">
</member>
<member name="script_class_name" type="String" setter="set_script_class_name" getter="get_script_class_name" default="&quot;&quot;">
<member name="script_class_name" type="StringName" setter="set_script_class_name" getter="get_script_class_name" default="&amp;&quot;&quot;">
</member>
</members>
</class>

View file

@ -130,6 +130,7 @@ typedef struct {
godot_bool has_named_classes;
godot_bool supports_builtin_mode;
godot_bool can_inherit_from_file;
godot_bool has_delayed_script_class_metadata;
godot_string (*get_template_source_code)(godot_pluginscript_language_data *p_data, const godot_string *p_class_name, const godot_string *p_base_class_name);
godot_bool (*validate)(godot_pluginscript_language_data *p_data, const godot_string *p_script, const godot_string *p_path, godot_packed_string_array *r_functions, godot_array *r_errors); // errors = Array of Dictionary with "line", "column", "message" keys

View file

@ -136,11 +136,11 @@ bool NativeScript::inherits_script(const Ref<Script> &p_script) const {
return false;
}
void NativeScript::set_class_name(String p_class_name) {
void NativeScript::set_class_name(StringName p_class_name) {
class_name = p_class_name;
}
String NativeScript::get_class_name() const {
StringName NativeScript::get_class_name() const {
return class_name;
}
@ -170,11 +170,11 @@ Ref<GDNativeLibrary> NativeScript::get_library() const {
return library;
}
void NativeScript::set_script_class_name(String p_type) {
void NativeScript::set_script_class_name(StringName p_type) {
script_class_name = p_type;
}
String NativeScript::get_script_class_name() const {
StringName NativeScript::get_script_class_name() const {
return script_class_name;
}
@ -1607,13 +1607,13 @@ void NativeScriptLanguage::thread_exit() {
#endif // NO_THREADS
bool NativeScriptLanguage::handles_global_class_type(const String &p_type) const {
return p_type == "NativeScript";
bool NativeScriptLanguage::handles_global_class_type(const StringName &p_type) const {
return p_type == SNAME("NativeScript");
}
String NativeScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
StringName NativeScriptLanguage::get_global_class_name(const String &p_path, StringName *r_base_type, String *r_icon_path) const {
if (!p_path.is_empty()) {
Ref<NativeScript> script = ResourceLoader::load(p_path, "NativeScript");
Ref<NativeScript> script = ResourceLoader::load(p_path, NativeScript::get_class_static());
if (script.is_valid()) {
if (r_base_type) {
*r_base_type = script->get_instance_base_type();
@ -1624,13 +1624,13 @@ String NativeScriptLanguage::get_global_class_name(const String &p_path, String
return script->get_script_class_name();
}
if (r_base_type) {
*r_base_type = String();
*r_base_type = StringName();
}
if (r_icon_path) {
*r_icon_path = String();
}
}
return String();
return StringName();
}
void NativeReloadNode::_bind_methods() {

View file

@ -112,7 +112,7 @@ class NativeScript : public Script {
String class_name;
String script_class_name;
StringName script_class_name;
String script_class_icon_path;
Mutex owners_lock;
@ -126,14 +126,14 @@ public:
bool inherits_script(const Ref<Script> &p_script) const override;
void set_class_name(String p_class_name);
String get_class_name() const;
void set_class_name(StringName p_class_name);
StringName get_class_name() const;
void set_library(Ref<GDNativeLibrary> p_library);
Ref<GDNativeLibrary> get_library() const;
void set_script_class_name(String p_type);
String get_script_class_name() const;
void set_script_class_name(StringName p_type);
StringName get_script_class_name() const;
void set_script_class_icon_path(String p_icon_path);
String get_script_class_icon_path() const;
@ -354,8 +354,8 @@ public:
void set_global_type_tag(int p_idx, StringName p_class_name, const void *p_type_tag);
const void *get_global_type_tag(int p_idx, StringName p_class_name) const;
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, String *r_icon_path) const;
virtual bool handles_global_class_type(const StringName &p_type) const;
virtual StringName get_global_class_name(const String &p_path, StringName *r_base_type, String *r_icon_path) const;
void profiling_add_data(StringName p_signature, uint64_t p_time);
};

View file

@ -159,6 +159,10 @@ bool PluginScriptLanguage::can_inherit_from_file() const {
return _desc.can_inherit_from_file;
}
bool PluginScriptLanguage::has_delayed_script_class_metadata() const {
return _desc.has_delayed_script_class_metadata;
}
int PluginScriptLanguage::find_function(const String &p_function, const String &p_code) const {
if (_desc.find_function) {
return _desc.find_function(_data, (godot_string *)&p_function, (godot_string *)&p_code);
@ -415,13 +419,13 @@ void PluginScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool
#endif
}
bool PluginScriptLanguage::handles_global_class_type(const String &p_type) const {
return p_type == "PluginScript";
bool PluginScriptLanguage::handles_global_class_type(const StringName &p_type) const {
return p_type == SNAME("PluginScript");
}
String PluginScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
StringName PluginScriptLanguage::get_global_class_name(const String &p_path, StringName *r_base_type, String *r_icon_path) const {
if (!p_path.is_empty()) {
Ref<PluginScript> script = ResourceLoader::load(p_path, "PluginScript");
Ref<PluginScript> script = ResourceLoader::load(p_path, PluginScript::get_class_static());
if (script.is_valid()) {
if (r_base_type) {
*r_base_type = script->get_instance_base_type();
@ -432,13 +436,13 @@ String PluginScriptLanguage::get_global_class_name(const String &p_path, String
return script->get_script_class_name();
}
if (r_base_type) {
*r_base_type = String();
*r_base_type = StringName();
}
if (r_icon_path) {
*r_icon_path = String();
}
}
return String();
return StringName();
}
void PluginScriptLanguage::lock() {

View file

@ -80,6 +80,7 @@ public:
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
virtual bool can_inherit_from_file() const;
virtual bool has_delayed_script_class_metadata() const;
virtual int find_function(const String &p_function, const String &p_code) const;
virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const;
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);
@ -125,8 +126,8 @@ public:
/* GLOBAL CLASSES */
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 = nullptr, String *r_icon_path = nullptr) const;
virtual bool handles_global_class_type(const StringName &p_type) const;
virtual StringName get_global_class_name(const String &p_path, StringName *r_base_type = nullptr, String *r_icon_path = nullptr) const;
void lock();
void unlock();

View file

@ -2053,11 +2053,11 @@ bool GDScriptLanguage::is_control_flow_keyword(String p_keyword) const {
p_keyword == "while";
}
bool GDScriptLanguage::handles_global_class_type(const String &p_type) const {
return p_type == "GDScript";
bool GDScriptLanguage::handles_global_class_type(const StringName &p_type) const {
return p_type == SNAME("GDScript");
}
String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
StringName GDScriptLanguage::get_global_class_name(const String &p_path, StringName *r_base_type, String *r_icon_path) const {
Vector<uint8_t> sourcef;
Error err;
FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err);
@ -2142,12 +2142,12 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
break;
}
} else {
*r_base_type = "RefCounted";
*r_base_type = SNAME("RefCounted");
subclass = nullptr;
}
}
}
return c->identifier != nullptr ? String(c->identifier->name) : String();
return c->identifier != nullptr ? c->identifier->name : StringName();
}
return String();

View file

@ -498,8 +498,8 @@ public:
/* GLOBAL CLASSES */
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 = nullptr, String *r_icon_path = nullptr) const;
virtual bool handles_global_class_type(const StringName &p_type) const;
virtual StringName get_global_class_name(const String &p_path, StringName *r_base_type = nullptr, String *r_icon_path = nullptr) const;
void add_orphan_subclass(const String &p_qualified_name, const ObjectID &p_subclass);
Ref<GDScript> get_orphan_subclass(const String &p_qualified_name);

View file

@ -483,12 +483,22 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
if (parser->script_path == ScriptServer::get_global_class_path(first)) {
result = parser->head->get_datatype();
} else {
Ref<GDScriptParserRef> ref = get_parser_for(ScriptServer::get_global_class_path(first));
if (ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) {
push_error(vformat(R"(Could not parse global class "%s" from "%s".)", first, ScriptServer::get_global_class_path(first)), p_type);
return GDScriptParser::DataType();
String first_path = ScriptServer::get_global_class_path(first);
if (ResourceLoader::get_resource_type(first_path) == GDScript::get_class_static()) {
Ref<GDScriptParserRef> ref = get_parser_for(first_path);
if (ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) {
push_error(vformat(R"(Could not parse global class "%s" from "%s".)", first, ScriptServer::get_global_class_path(first)), p_type);
return GDScriptParser::DataType();
}
result = ref->get_parser()->head->get_datatype();
} else {
result.kind = GDScriptParser::DataType::Kind::SCRIPT;
result.script_path = first_path;
result.script_type = ResourceLoader::load(first_path, Script::get_class_static());
if (result.script_type.is_valid()) {
result.native_type = result.script_type->get_instance_base_type();
}
}
result = ref->get_parser()->head->get_datatype();
}
} else if (ProjectSettings::get_singleton()->has_autoload(first) && ProjectSettings::get_singleton()->get_autoload(first).is_singleton) {
const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(first);

View file

@ -42,6 +42,7 @@
#endif // DEBUG_ENABLED
#ifdef TOOLS_ENABLED
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#endif // TOOLS_ENABLED
@ -3502,7 +3503,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.hint_string = hint_string;
// This is called after tne analyzer is done finding the type, so this should be set here.
// This is called after the analyzer is done finding the type, so this should be set here.
DataType export_type = variable->get_datatype();
if (p_annotation->name == "@export") {
@ -3556,8 +3557,33 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.hint_string = enum_hint_string;
} break;
case GDScriptParser::DataType::CLASS: {
// Can assume type is a global GDScript class.
if (!ClassDB::is_parent_class(export_type.native_type, "Resource")) {
push_error(R"(Exported script type must extend Resource.)", variable);
return false;
}
variable->export_info.type = Variant::OBJECT;
variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
variable->export_info.hint_string = export_type.class_type->identifier->name;
} break;
case GDScriptParser::DataType::SCRIPT: {
StringName script_name = ScriptServer::get_global_class_name(export_type.script_path);
if (ScriptServer::is_global_class(script_name)) {
StringName native = ScriptServer::get_global_class_native_base(script_name);
if (!ClassDB::is_parent_class(native, "Resource")) {
push_error(R"(Exported script type must extend Resource.)", variable);
return false;
}
variable->export_info.type = Variant::OBJECT;
variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
variable->export_info.hint_string = script_name;
} else {
push_error(R"(Exported script type has no global class name.)", variable);
return false;
}
} break;
default:
// TODO: Allow custom user resources.
push_error(R"(Export type can only be built-in, a resource, or an enum.)", variable);
break;
}

View file

@ -264,10 +264,10 @@ bool GDScriptTestRunner::generate_class_index() {
StringName gdscript_name = GDScriptLanguage::get_singleton()->get_name();
for (int i = 0; i < tests.size(); i++) {
GDScriptTest test = tests[i];
String base_type;
StringName base_type;
String class_name = GDScriptLanguage::get_singleton()->get_global_class_name(test.get_source_file(), &base_type);
if (class_name == String()) {
StringName class_name = GDScriptLanguage::get_singleton()->get_global_class_name(test.get_source_file(), &base_type);
if (class_name == StringName()) {
continue;
}
ERR_FAIL_COND_V_MSG(ScriptServer::is_global_class(class_name), false,

View file

@ -566,6 +566,37 @@ String CSharpLanguage::_get_indentation() const {
return "\t";
}
bool CSharpLanguage::handles_global_class_type(const StringName &p_type) const {
return p_type == SNAME("CSharpScript");
}
StringName CSharpLanguage::get_global_class_name(const String &p_path, StringName *r_base_type, String *r_icon_path) const {
Ref<CSharpScript> script = ResourceLoader::load(p_path, CSharpScript::get_class_static());
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 = StringName();
if (r_icon_path)
*r_icon_path = String();
return StringName();
}
String CSharpLanguage::debug_get_error() const {
return _debug_error;
}
@ -1000,6 +1031,11 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
#endif
script->signals_invalidated = true;
#ifdef TOOLS_ENABLED
EditorFileSystem::get_singleton()->remove_compiled_lang_script_class_file_cache(script->get_path());
EditorNode::get_editor_data().script_class_set_icon_path(script->get_script_class_name(), script->get_script_class_icon_path());
#endif
if (!script->get_path().is_empty()) {
script->reload(p_soft_reload);
@ -1098,6 +1134,11 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
to_reload_state.push_back(script);
}
#ifdef TOOLS_ENABLED
ScriptServer::save_global_classes();
EditorNode::get_editor_data().script_class_save_icon_paths();
#endif
for (Ref<CSharpScript> &script : to_reload_state) {
for (const ObjectID &obj_id : script->pending_reload_instances) {
Object *obj = ObjectDB::get_instance(obj_id);
@ -2786,7 +2827,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: '" +
@ -2814,7 +2857,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;
@ -2880,6 +2923,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_allow_generics && p_variant_type == Variant::ARRAY) {
// Nested arrays are not supported in the inspector
@ -2906,7 +2959,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.");
@ -3404,6 +3457,16 @@ Error CSharpScript::reload(bool p_keep_state) {
update_script_class_info(this);
_update_exports();
_update_global_script_class_settings();
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
// Force file cache update for compiled languages to update script class metadata
StringName n = script_class_name;
StringName b = script_class_base;
EditorFileSystem::get_singleton()->update_file_script_class_metadata(get_path(), n, b, get_language()->get_name(), script_class_icon_path);
}
#endif
}
return OK;
@ -3551,6 +3614,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();

View file

@ -130,6 +130,11 @@ private:
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;
Map<StringName, Vector<SignalParameter>> _signals;
@ -167,7 +172,7 @@ private:
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);
@ -242,6 +247,11 @@ public:
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();
};
@ -479,6 +489,11 @@ public:
virtual String _get_indentation() const;
/* 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 {}
virtual bool has_delayed_script_class_metadata() const override { return true; }
/* SCRIPT CLASS FUNCTIONS */
virtual bool handles_global_class_type(const StringName &p_type) const;
virtual StringName get_global_class_name(const String &p_path, StringName *r_base_type = NULL, String *r_icon_path = NULL) const;
/* DEBUGGER FUNCTIONS */
String debug_get_error() const override;

View file

@ -13,5 +13,19 @@ namespace Godot
this.hint = hint;
this.hintString = hintString;
}
public ExportAttribute(string className)
{
if (ClassDB.ClassExists(className) || ScriptServer.IsGlobalClass(className))
{
this.hint = PropertyHint.ResourceType;
this.hintString = className;
}
else
{
this.hint = PropertyHint.None;
this.hintString = "";
}
}
}
}

View file

@ -0,0 +1,17 @@
using System;
namespace Godot
{
[AttributeUsage(AttributeTargets.Class)]
public class GlobalAttribute : Attribute
{
private string name;
private string iconPath;
public GlobalAttribute(string name = "", string iconPath = "")
{
this.name = name;
this.iconPath = iconPath;
}
}
}

View file

@ -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\RPCAttributes.cs" />
<Compile Include="Core\Attributes\ScriptPathAttribute.cs" />

View file

@ -138,6 +138,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_AnyPeerAttribute = nullptr;
@ -263,6 +266,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(AnyPeerAttribute, GODOT_API_CLASS(AnyPeerAttribute));

View file

@ -109,6 +109,9 @@ struct CachedData {
GDMonoClass *class_ExportAttribute;
GDMonoField *field_ExportAttribute_hint;
GDMonoField *field_ExportAttribute_hintString;
GDMonoClass *class_GlobalAttribute;
GDMonoField *field_GlobalAttribute_name;
GDMonoField *field_GlobalAttribute_iconPath;
GDMonoClass *class_SignalAttribute;
GDMonoClass *class_ToolAttribute;
GDMonoClass *class_AnyPeerAttribute;

View file

@ -44,6 +44,7 @@ def get_doc_classes():
"VisualScriptReturn",
"VisualScriptSceneNode",
"VisualScriptSceneTree",
"VisualScriptScriptClass",
"VisualScriptSelect",
"VisualScriptSelf",
"VisualScriptSequence",

View file

@ -154,6 +154,18 @@
Returns a node's position in pixels.
</description>
</method>
<method name="get_script_class_icon_path" qualifiers="const">
<return type="String" />
<description>
Returns this [VisualScript]'s global class icon path provided to the [ScriptServer].
</description>
</method>
<method name="get_script_class_name" qualifiers="const">
<return type="StringName" />
<description>
Returns this [VisualScript]'s global class name provided to the [ScriptServer].
</description>
</method>
<method name="get_scroll" qualifiers="const">
<return type="Vector2" />
<description>
@ -314,6 +326,20 @@
Set the node position in the VisualScript graph.
</description>
</method>
<method name="set_script_class_icon_path">
<return type="void" />
<argument index="0" name="script_class_icon_path" type="String" />
<description>
Assigns the global class icon path that this [VisualScript] provides to the [ScriptServer].
</description>
</method>
<method name="set_script_class_name">
<return type="void" />
<argument index="0" name="script_class_name" type="StringName" />
<description>
Assigns the global class name that this [VisualScript] provides to the [ScriptServer].
</description>
</method>
<method name="set_scroll">
<return type="void" />
<argument index="0" name="ofs" type="Vector2" />

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptScriptClass" inherits="VisualScriptNode" version="4.0">
<brief_description>
A Visual Script node for accessing global script classes defined in any language.
</brief_description>
<description>
A Visual Script node for accessing global script classes defined in any language.
</description>
<tutorials>
</tutorials>
<methods>
<method name="get_script_class" qualifiers="const">
<return type="StringName" />
<description>
Returns the name of the currently selected global Script class.
</description>
</method>
<method name="set_script_class">
<return type="void" />
<argument index="0" name="name" type="StringName" />
<description>
Assigns a new global Script class name, changing which reference this node returns.
</description>
</method>
</methods>
<constants>
</constants>
</class>

View file

@ -67,6 +67,7 @@ void register_visual_script_types() {
GDREGISTER_CLASS(VisualScriptClassConstant);
GDREGISTER_CLASS(VisualScriptMathConstant);
GDREGISTER_CLASS(VisualScriptBasicTypeConstant);
GDREGISTER_CLASS(VisualScriptScriptClass);
GDREGISTER_CLASS(VisualScriptEngineSingleton);
GDREGISTER_CLASS(VisualScriptSceneNode);
GDREGISTER_CLASS(VisualScriptSceneTree);

View file

@ -1011,6 +1011,8 @@ void VisualScript::_set_data(const Dictionary &p_data) {
data_connect(data_connections[j + 0], data_connections[j + 1], data_connections[j + 2], data_connections[j + 3]);
}
}
script_class_name = d["script_class_name"].operator StringName();
script_class_icon_path = d["script_class_icon_path"].operator String();
is_tool_script = d["is_tool_script"];
scroll = d["scroll"];
@ -1109,12 +1111,32 @@ Dictionary VisualScript::_get_data() const {
}
d["data_connections"] = dataconns;
d["script_class_name"] = script_class_name;
d["script_class_icon_path"] = script_class_icon_path;
d["is_tool_script"] = is_tool_script;
d["scroll"] = scroll;
return d;
}
StringName VisualScript::get_script_class_name() const {
return script_class_name;
}
String VisualScript::get_script_class_icon_path() const {
return script_class_icon_path;
}
void VisualScript::set_script_class_name(const StringName &p_name) {
if (String(p_name).is_valid_identifier()) {
script_class_name = p_name;
}
}
void VisualScript::set_script_class_icon_path(const String &p_path) {
script_class_icon_path = p_path;
}
void VisualScript::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_function", "name", "func_node_id"), &VisualScript::add_function);
ClassDB::bind_method(D_METHOD("has_function", "name"), &VisualScript::has_function);
@ -1172,6 +1194,11 @@ void VisualScript::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_data", "data"), &VisualScript::_set_data);
ClassDB::bind_method(D_METHOD("_get_data"), &VisualScript::_get_data);
ClassDB::bind_method(D_METHOD("set_script_class_name", "script_class_name"), &VisualScript::set_script_class_name);
ClassDB::bind_method(D_METHOD("get_script_class_name"), &VisualScript::get_script_class_name);
ClassDB::bind_method(D_METHOD("set_script_class_icon_path", "script_class_icon_path"), &VisualScript::set_script_class_icon_path);
ClassDB::bind_method(D_METHOD("get_script_class_icon_path"), &VisualScript::get_script_class_icon_path);
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
ADD_SIGNAL(MethodInfo("node_ports_changed", PropertyInfo(Variant::INT, "id")));
@ -2227,6 +2254,27 @@ Error VisualScriptLanguage::execute_file(const String &p_path) {
void VisualScriptLanguage::finish() {
}
bool VisualScriptLanguage::handles_global_class_type(const StringName &p_type) const {
return p_type == SNAME("VisualScript");
}
StringName VisualScriptLanguage::get_global_class_name(const String &p_path, StringName *r_base_type, String *r_icon_path) const {
Ref<VisualScript> script = ResourceLoader::load(p_path, VisualScript::get_class_static());
if (script.is_null()) {
if (r_base_type)
*r_base_type = StringName();
if (r_icon_path)
*r_icon_path = String();
return StringName();
}
if (r_base_type)
*r_base_type = script->get_instance_base_type();
if (r_icon_path)
*r_icon_path = script->get_script_class_icon_path();
return String(script->get_script_class_name()).is_valid_identifier() ? script->get_script_class_name() : StringName();
}
/* EDITOR FUNCTIONS */
void VisualScriptLanguage::get_reserved_words(List<String> *p_words) const {
}

View file

@ -239,6 +239,8 @@ private:
Map<Object *, VisualScriptInstance *> instances;
bool is_tool_script;
StringName script_class_name;
String script_class_icon_path;
#ifdef TOOLS_ENABLED
Set<PlaceHolderScriptInstance *> placeholders;
@ -368,6 +370,11 @@ public:
virtual bool are_subnodes_edited() const;
#endif
StringName get_script_class_name() const;
String get_script_class_icon_path() const;
void set_script_class_name(const StringName &p_name);
void set_script_class_icon_path(const String &p_path);
VisualScript();
~VisualScript();
};
@ -563,6 +570,9 @@ public:
virtual Error execute_file(const String &p_path);
virtual void finish();
virtual bool handles_global_class_type(const StringName &p_type) const;
virtual StringName get_global_class_name(const String &p_path, StringName *r_base_type = nullptr, String *r_icon_path = nullptr) const;
/* EDITOR FUNCTIONS */
virtual void get_reserved_words(List<String> *p_words) const;
virtual bool is_control_flow_keyword(String p_keyword) const;

View file

@ -1288,6 +1288,18 @@ void VisualScriptEditor::_member_edited() {
}
}
void VisualScriptEditor::_script_class_icon_btn_gui_input(Ref<InputEvent> p_event) {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid() && !mb->is_pressed()) {
int index = mb->get_button_index();
if (index == MOUSE_BUTTON_LEFT) {
_script_class_icon_path_dialog->popup_centered_ratio();
} else if (index == MOUSE_BUTTON_RIGHT) {
_script_class_icon_btn_set_icon("");
}
}
}
void VisualScriptEditor::_create_function_dialog() {
function_create_dialog->popup_centered();
func_name_box->set_text("");
@ -2570,6 +2582,12 @@ void VisualScriptEditor::set_edited_resource(const RES &p_res) {
script->connect("node_ports_changed", callable_mp(this, &VisualScriptEditor::_node_ports_changed));
_script_class_name_edit->set_text(script->get_script_class_name());
String icon_path = script->get_script_class_icon_path();
Control *gui_base = EditorNode::get_singleton()->get_gui_base();
RES icon = FileAccess::exists(icon_path) ? ResourceLoader::load(icon_path, "Texture") : (RES)gui_base->get_theme_icon("Nil", "EditorIcons");
_script_class_icon_btn->set_icon(icon);
_update_graph();
call_deferred(SNAME("_update_members"));
}
@ -4269,6 +4287,44 @@ void VisualScriptEditor::update_toggle_scripts_button() {
toggle_scripts_button->set_tooltip(vformat("%s (%s)", TTR("Toggle Scripts Panel"), ED_GET_SHORTCUT("script_editor/toggle_scripts_panel")->get_as_text()));
}
void VisualScriptEditor::_script_class_icon_path_dialog_confirmed() {
const String &path = _script_class_icon_path_dialog->get_current_path();
_script_class_icon_btn_set_icon(path);
}
void VisualScriptEditor::_script_class_icon_path_dialog_file_selected(const String &p_path) {
_script_class_icon_btn_set_icon(p_path);
}
void VisualScriptEditor::_script_class_icon_btn_set_icon(const String &p_path) {
ERR_FAIL_COND(!_script_class_icon_btn);
ERR_FAIL_COND_MSG(p_path.length() && !FileAccess::exists(p_path), "The given script class icon path does not exist.");
RES icon = FileAccess::exists(p_path) ? ResourceLoader::load(p_path, "Texture") : (RES)EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Nil", "EditorIcons");
undo_redo->create_action("Set script class icon");
undo_redo->add_do_method(_script_class_icon_btn, "set_button_icon", icon);
undo_redo->add_undo_method(_script_class_icon_btn, "set_button_icon", _script_class_icon_btn->get_icon());
if (script.is_valid()) {
undo_redo->add_do_method(script.operator->(), "set_script_class_icon_path", p_path);
undo_redo->add_undo_method(script.operator->(), "set_script_class_icon_path", script->get_script_class_icon_path());
}
undo_redo->commit_action();
set_edited(true);
}
void VisualScriptEditor::_script_class_name_text_changed(const String &p_text) {
if (script.is_valid()) {
script->set_script_class_name(p_text);
}
set_edited(true);
}
void VisualScriptEditor::_script_class_icon_path_text_changed(const String &p_text) {
if (script.is_valid()) {
script->set_script_class_icon_path(p_text);
}
set_edited(true);
}
void VisualScriptEditor::_bind_methods() {
ClassDB::bind_method("_move_node", &VisualScriptEditor::_move_node);
ClassDB::bind_method("_update_graph", &VisualScriptEditor::_update_graph, DEFVAL(-1));
@ -4287,6 +4343,12 @@ void VisualScriptEditor::_bind_methods() {
ClassDB::bind_method("_update_members", &VisualScriptEditor::_update_members);
ClassDB::bind_method("_generic_search", &VisualScriptEditor::_generic_search);
ClassDB::bind_method("_script_class_icon_path_dialog_confirmed", &VisualScriptEditor::_script_class_icon_path_dialog_confirmed);
ClassDB::bind_method("_script_class_icon_path_dialog_file_selected", &VisualScriptEditor::_script_class_icon_path_dialog_file_selected);
ClassDB::bind_method("_script_class_name_text_changed", &VisualScriptEditor::_script_class_name_text_changed);
ClassDB::bind_method("_script_class_icon_path_text_changed", &VisualScriptEditor::_script_class_icon_path_text_changed);
ClassDB::bind_method("_script_class_icon_btn_gui_input", &VisualScriptEditor::_script_class_icon_btn_gui_input);
}
VisualScriptEditor::VisualScriptEditor() {
@ -4323,6 +4385,37 @@ VisualScriptEditor::VisualScriptEditor() {
members_section->add_child(tool_script_check);
tool_script_check->connect("pressed", callable_mp(this, &VisualScriptEditor::_toggle_tool_script));
HBoxContainer *hb = memnew(HBoxContainer);
hb->set_h_size_flags(SIZE_EXPAND_FILL);
Label *lbl = memnew(Label);
lbl->set_text(TTR("Class Name:"));
hb->add_child(lbl);
LineEdit *le = memnew(LineEdit);
le->set_tooltip(TTR("Script Class names must be valid identifiers. Alphanumeric characters and underscores not starting with a number."));
le->connect("text_changed", callable_mp(this, &VisualScriptEditor::_script_class_name_text_changed));
le->set_h_size_flags(SIZE_EXPAND_FILL);
hb->add_child(le);
_script_class_name_edit = le;
_script_class_icon_btn = memnew(Button);
_script_class_icon_btn->set_tooltip(TTR("Choose a 16x16 icon for the class. Right-click to clear."));
FileDialog *fd = memnew(FileDialog);
fd->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE);
List<String> texture_type_exts;
ResourceLoader::get_recognized_extensions_for_type("Texture", &texture_type_exts);
for (List<String>::Element *E = texture_type_exts.front(); E; E = E->next()) {
fd->add_filter(vformat("*.%s", E->get()));
}
fd->set_enable_multiple_selection(false);
fd->set_show_hidden_files(false);
fd->set_title("Choose an image.");
fd->connect("confirmed", callable_mp(this, &VisualScriptEditor::_script_class_icon_path_dialog_confirmed));
fd->connect("file_selected", callable_mp(this, &VisualScriptEditor::_script_class_icon_path_dialog_file_selected));
_script_class_icon_btn->add_child(fd);
_script_class_icon_path_dialog = fd;
_script_class_icon_btn->connect("gui_input", callable_mp(this, &VisualScriptEditor::_script_class_icon_btn_gui_input));
hb->add_child(_script_class_icon_btn);
members_section->add_child(hb);
/// Members ///
members = memnew(Tree);

View file

@ -34,6 +34,7 @@
#include "editor/create_dialog.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/property_editor.h"
#include "scene/gui/file_dialog.h"
#include "scene/gui/graph_edit.h"
#include "visual_script.h"
#include "visual_script_property_selector.h"
@ -172,6 +173,10 @@ class VisualScriptEditor : public ScriptEditorBase {
Vector2 port_action_pos;
int port_action_new_node;
LineEdit *_script_class_name_edit;
Button *_script_class_icon_btn;
FileDialog *_script_class_icon_path_dialog;
bool saved_pos_dirty;
Vector2 saved_position;
@ -202,6 +207,7 @@ class VisualScriptEditor : public ScriptEditorBase {
void _toggle_tool_script();
void _member_selected();
void _member_edited();
void _script_class_icon_btn_gui_input(Ref<InputEvent> p_event);
void _begin_node_move();
void _end_node_move();
@ -285,6 +291,12 @@ class VisualScriptEditor : public ScriptEditorBase {
void _member_rmb_selected(const Vector2 &p_pos);
void _member_option(int p_option);
void _script_class_icon_path_dialog_confirmed();
void _script_class_icon_path_dialog_file_selected(const String &p_path);
void _script_class_icon_btn_set_icon(const String &p_path);
void _script_class_name_text_changed(const String &p_text);
void _script_class_icon_path_text_changed(const String &p_text);
void _toggle_scripts_pressed();
protected:

View file

@ -2274,6 +2274,118 @@ VisualScriptMathConstant::VisualScriptMathConstant() {
constant = MATH_CONSTANT_ONE;
}
//////////////////////////////////////////
////////////////SCRIPTCLASS///////////////
//////////////////////////////////////////
int VisualScriptScriptClass::get_output_sequence_port_count() const {
return 0;
}
bool VisualScriptScriptClass::has_input_sequence_port() const {
return false;
}
int VisualScriptScriptClass::get_input_value_port_count() const {
return 0;
}
int VisualScriptScriptClass::get_output_value_port_count() const {
return 1;
}
String VisualScriptScriptClass::get_output_sequence_port_text(int p_port) const {
return String();
}
PropertyInfo VisualScriptScriptClass::get_input_value_port_info(int p_idx) const {
return PropertyInfo();
}
PropertyInfo VisualScriptScriptClass::get_output_value_port_info(int p_idx) const {
if (ScriptServer::is_global_class(script_class)) {
Ref<Script> script = ScriptServer::get_global_class_script(script_class);
return PropertyInfo(Variant::OBJECT, script_class, PROPERTY_HINT_RESOURCE_TYPE, script->get_class(), PROPERTY_USAGE_DEFAULT, script->get_class());
}
return PropertyInfo(Variant::OBJECT, script_class, PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT, "Script");
}
String VisualScriptScriptClass::get_caption() const {
return "Script Class";
}
void VisualScriptScriptClass::set_script_class(const StringName &p_name) {
ERR_FAIL_COND(!ScriptServer::is_global_class(p_name));
script_class = p_name;
notify_property_list_changed();
ports_changed_notify();
}
const StringName &VisualScriptScriptClass::get_script_class() const {
return script_class;
}
class VisualScriptNodeInstanceScriptClass : public VisualScriptNodeInstance {
public:
Ref<Script> script;
virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) {
*p_outputs[0] = script;
return OK;
}
};
VisualScriptNodeInstance *VisualScriptScriptClass::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceScriptClass *instance = memnew(VisualScriptNodeInstanceScriptClass);
instance->script = script_class != StringName() ? ScriptServer::get_global_class_script(script_class) : Ref<Script>();
return instance;
}
bool VisualScriptScriptClass::_set(const StringName &p_name, const Variant &p_value) {
if (p_name == "script_class") {
set_script_class(p_value);
return true;
}
return false;
}
bool VisualScriptScriptClass::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name == "script_class") {
r_ret = get_script_class();
return true;
}
return false;
}
void VisualScriptScriptClass::_get_property_list(List<PropertyInfo> *p_list) const {
String cc;
List<StringName> lst;
ScriptServer::get_global_class_list(&lst);
int i = 0;
for (List<StringName>::Element *E = lst.front(); E; E = E->next()) {
if (i > 0)
cc += ",";
i++;
cc += E->get();
}
p_list->push_back(PropertyInfo(Variant::STRING, "script_class", PROPERTY_HINT_ENUM, cc));
}
void VisualScriptScriptClass::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_script_class", "name"), &VisualScriptScriptClass::set_script_class);
ClassDB::bind_method(D_METHOD("get_script_class"), &VisualScriptScriptClass::get_script_class);
}
VisualScriptScriptClass::VisualScriptScriptClass() {
List<StringName> lst;
ScriptServer::get_global_class_list(&lst);
script_class = StringName();
if (lst.size()) {
StringName name = lst.front()->get();
script_class = name;
}
}
//////////////////////////////////////////
////////////////ENGINESINGLETON///////////
//////////////////////////////////////////
@ -3992,6 +4104,7 @@ void register_visual_script_nodes() {
VisualScriptLanguage::singleton->add_register_func("constants/class_constant", create_node_generic<VisualScriptClassConstant>);
VisualScriptLanguage::singleton->add_register_func("constants/global_constant", create_node_generic<VisualScriptGlobalConstant>);
VisualScriptLanguage::singleton->add_register_func("constants/basic_type_constant", create_node_generic<VisualScriptBasicTypeConstant>);
VisualScriptLanguage::singleton->add_register_func("constants/script_class", create_node_generic<VisualScriptScriptClass>);
VisualScriptLanguage::singleton->add_register_func("custom/custom_node", create_node_generic<VisualScriptCustomNode>);
VisualScriptLanguage::singleton->add_register_func("custom/sub_call", create_node_generic<VisualScriptSubCall>);

View file

@ -591,6 +591,41 @@ public:
VARIANT_ENUM_CAST(VisualScriptMathConstant::MathConstant)
class VisualScriptScriptClass : public VisualScriptNode {
GDCLASS(VisualScriptScriptClass, VisualScriptNode);
StringName script_class;
protected:
static void _bind_methods();
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:
virtual int get_output_sequence_port_count() const override;
virtual bool has_input_sequence_port() const override;
virtual String get_output_sequence_port_text(int p_port) const override;
virtual int get_input_value_port_count() const override;
virtual int get_output_value_port_count() const override;
virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
virtual String get_caption() const override;
virtual String get_category() const override { return "constants"; }
void set_script_class(const StringName &p_name);
const StringName &get_script_class() const;
virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptScriptClass();
};
class VisualScriptEngineSingleton : public VisualScriptNode {
GDCLASS(VisualScriptEngineSingleton, VisualScriptNode);