Include more attributes in the global class names cache

This commit is contained in:
Pedro J. Estébanez 2025-01-08 13:21:47 +01:00
parent aca5cfc913
commit 318af42020
16 changed files with 174 additions and 133 deletions

View File

@ -1263,10 +1263,10 @@ void ProjectSettings::refresh_global_class_list() {
Array script_classes = get_global_class_list();
for (int i = 0; i < script_classes.size(); i++) {
Dictionary c = script_classes[i];
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) {
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base") || !c.has("is_abstract") || !c.has("is_tool")) {
continue;
}
ScriptServer::add_global_class(c["class"], c["base"], c["language"], c["path"]);
ScriptServer::add_global_class(c["class"], c["base"], c["language"], c["path"], c["is_abstract"], c["is_tool"]);
}
}

View File

@ -269,10 +269,10 @@ void ScriptServer::init_languages() {
for (const Variant &script_class : script_classes) {
Dictionary c = script_class;
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) {
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base") || !c.has("is_abstract") || !c.has("is_tool")) {
continue;
}
add_global_class(c["class"], c["base"], c["language"], c["path"]);
add_global_class(c["class"], c["base"], c["language"], c["path"], c["is_abstract"], c["is_tool"]);
}
ProjectSettings::get_singleton()->clear("_global_script_classes");
}
@ -281,10 +281,10 @@ void ScriptServer::init_languages() {
Array script_classes = ProjectSettings::get_singleton()->get_global_class_list();
for (const Variant &script_class : script_classes) {
Dictionary c = script_class;
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) {
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base") || !c.has("is_abstract") || !c.has("is_tool")) {
continue;
}
add_global_class(c["class"], c["base"], c["language"], c["path"]);
add_global_class(c["class"], c["base"], c["language"], c["path"], c["is_abstract"], c["is_tool"]);
}
}
@ -390,7 +390,7 @@ void ScriptServer::global_classes_clear() {
inheriters_cache.clear();
}
void ScriptServer::add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path) {
void ScriptServer::add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path, bool p_is_abstract, bool p_is_tool) {
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.");
GlobalScriptClass *existing = global_classes.getptr(p_class);
if (existing) {
@ -399,6 +399,8 @@ void ScriptServer::add_global_class(const StringName &p_class, const StringName
existing->base = p_base;
existing->path = p_path;
existing->language = p_language;
existing->is_abstract = p_is_abstract;
existing->is_tool = p_is_tool;
inheriters_cache_dirty = true;
}
} else {
@ -407,6 +409,8 @@ void ScriptServer::add_global_class(const StringName &p_class, const StringName
g.language = p_language;
g.path = p_path;
g.base = p_base;
g.is_abstract = p_is_abstract;
g.is_tool = p_is_tool;
global_classes[p_class] = g;
inheriters_cache_dirty = true;
}
@ -480,6 +484,16 @@ StringName ScriptServer::get_global_class_native_base(const String &p_class) {
return base;
}
bool ScriptServer::is_global_class_abstract(const String &p_class) {
ERR_FAIL_COND_V(!global_classes.has(p_class), false);
return global_classes[p_class].is_abstract;
}
bool ScriptServer::is_global_class_tool(const String &p_class) {
ERR_FAIL_COND_V(!global_classes.has(p_class), false);
return global_classes[p_class].is_tool;
}
void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) {
List<StringName> classes;
for (const KeyValue<StringName, GlobalScriptClass> &E : global_classes) {
@ -507,12 +521,15 @@ void ScriptServer::save_global_classes() {
get_global_class_list(&gc);
Array gcarr;
for (const StringName &E : gc) {
const GlobalScriptClass &global_class = global_classes[E];
Dictionary d;
d["class"] = E;
d["language"] = global_classes[E].language;
d["path"] = global_classes[E].path;
d["base"] = global_classes[E].base;
d["language"] = global_class.language;
d["path"] = global_class.path;
d["base"] = global_class.base;
d["icon"] = class_icons.get(E, "");
d["is_abstract"] = global_class.is_abstract;
d["is_tool"] = global_class.is_tool;
gcarr.push_back(d);
}
ProjectSettings::get_singleton()->store_global_class_list(gcarr);

View File

@ -62,6 +62,8 @@ class ScriptServer {
StringName language;
String path;
StringName base;
bool is_abstract = false;
bool is_tool = false;
};
static HashMap<StringName, GlobalScriptClass> global_classes;
@ -86,7 +88,7 @@ public:
static void thread_exit();
static void global_classes_clear();
static void add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path);
static void add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path, bool p_is_abstract, bool p_is_tool);
static void remove_global_class(const StringName &p_class);
static void remove_global_class_by_path(const String &p_path);
static bool is_global_class(const StringName &p_class);
@ -94,6 +96,8 @@ public:
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 bool is_global_class_abstract(const String &p_class);
static bool is_global_class_tool(const String &p_class);
static void get_global_class_list(List<StringName> *r_global_classes);
static void get_inheriters_list(const StringName &p_base_type, List<StringName> *r_classes);
static void save_global_classes();
@ -443,7 +447,7 @@ 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 String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr, bool *r_is_abstract = nullptr, bool *r_is_tool = nullptr) const { return String(); }
virtual ~ScriptLanguage() {}
};

View File

@ -672,7 +672,7 @@ public:
GDVIRTUAL1RC_REQUIRED(Dictionary, _get_global_class_name, const String &)
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const override {
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr, bool *r_is_abstract = nullptr, bool *r_is_tool = nullptr) const override {
Dictionary ret;
GDVIRTUAL_CALL(_get_global_class_name, p_path, ret);
if (!ret.has("name")) {
@ -684,6 +684,12 @@ public:
if (r_icon_path != nullptr && ret.has("icon_path")) {
*r_icon_path = ret["icon_path"];
}
if (r_is_abstract != nullptr && ret.has("is_abstract")) {
*r_is_abstract = ret["is_abstract"];
}
if (r_is_tool != nullptr && ret.has("is_tool")) {
*r_is_tool = ret["is_tool"];
}
return ret["name"];
}
};

View File

@ -315,18 +315,14 @@ void CreateDialog::_configure_search_option_item(TreeItem *r_item, const StringN
r_item->set_metadata(0, p_type);
r_item->set_text(0, p_type);
String script_path = ScriptServer::get_global_class_path(p_type);
Ref<Script> scr = ResourceLoader::load(script_path, "Script");
ERR_FAIL_COND(scr.is_null());
is_abstract = scr->is_abstract();
is_abstract = ScriptServer::is_global_class_abstract(p_type);
String tooltip = TTR("Script path: %s");
bool is_tool = scr->is_tool();
bool is_tool = ScriptServer::is_global_class_tool(p_type);
if (is_tool) {
tooltip = TTR("The script will run in the editor.") + "\n" + tooltip;
}
r_item->add_button(0, get_editor_theme_icon(SNAME("Script")), 1, false, vformat(tooltip, script_path));
r_item->add_button(0, get_editor_theme_icon(SNAME("Script")), 1, false, vformat(tooltip, ScriptServer::get_global_class_path(p_type)));
if (is_tool) {
int button_index = r_item->get_button_count(0) - 1;
r_item->set_button_color(0, button_index, get_theme_color(SNAME("accent_color"), EditorStringName(Editor)));

View File

@ -49,7 +49,7 @@
EditorFileSystem *EditorFileSystem::singleton = nullptr;
//the name is the version, to keep compatibility with different versions of Godot
#define CACHE_FILE_NAME "filesystem_cache8"
#define CACHE_FILE_NAME "filesystem_cache10"
int EditorFileSystemDirectory::find_file_index(const String &p_file) const {
for (int i = 0; i < files.size(); i++) {
@ -166,19 +166,19 @@ uint64_t EditorFileSystemDirectory::get_file_import_modified_time(int p_idx) con
}
String EditorFileSystemDirectory::get_file_script_class_name(int p_idx) const {
return files[p_idx]->script_class_name;
return files[p_idx]->class_info.name;
}
String EditorFileSystemDirectory::get_file_script_class_extends(int p_idx) const {
return files[p_idx]->script_class_extends;
return files[p_idx]->class_info.extends;
}
String EditorFileSystemDirectory::get_file_script_class_icon_path(int p_idx) const {
return files[p_idx]->script_class_icon_path;
return files[p_idx]->class_info.icon_path;
}
String EditorFileSystemDirectory::get_file_icon_path(int p_idx) const {
return files[p_idx]->icon_path;
return files[p_idx]->class_info.icon_path;
}
StringName EditorFileSystemDirectory::get_file_type(int p_idx) const {
@ -303,13 +303,13 @@ void EditorFileSystem::_first_scan_process_scripts(const ScannedDirectory *p_sca
const String type = ResourceLoader::get_resource_type(path);
if (ClassDB::is_parent_class(type, SNAME("Script"))) {
String script_class_extends;
String script_class_icon_path;
String script_class_name = _get_global_script_class(type, path, &script_class_extends, &script_class_icon_path);
_register_global_class_script(path, path, type, script_class_name, script_class_extends, script_class_icon_path);
const ScriptClassInfo &info = _get_global_script_class(type, path);
ScriptClassInfoUpdate update(info);
update.type = type;
_register_global_class_script(path, path, update);
if (!script_class_name.is_empty()) {
p_existing_class_names.insert(script_class_name);
if (!info.name.is_empty()) {
p_existing_class_names.insert(info.name);
}
}
}
@ -392,12 +392,14 @@ void EditorFileSystem::_scan_filesystem() {
fc.import_group_file = split[6].strip_edges();
{
const Vector<String> &slices = split[7].split("<>");
ERR_CONTINUE(slices.size() < 5);
fc.script_class_name = slices[0];
fc.script_class_extends = slices[1];
fc.script_class_icon_path = slices[2];
fc.import_md5 = slices[3];
fc.import_dest_paths = slices[4].split("<*>");
ERR_CONTINUE(slices.size() < 7);
fc.class_info.name = slices[0];
fc.class_info.extends = slices[1];
fc.class_info.icon_path = slices[2];
fc.class_info.is_abstract = slices[3].to_int();
fc.class_info.is_tool = slices[4].to_int();
fc.import_md5 = slices[5];
fc.import_dest_paths = slices[6].split("<*>");
}
fc.deps = split[8].strip_edges().split("<>");
@ -849,7 +851,7 @@ bool EditorFileSystem::_update_scan_actions() {
}
if (ClassDB::is_parent_class(ia.new_file->type, SNAME("Script"))) {
_queue_update_script_class(new_file_path, ia.new_file->type, ia.new_file->script_class_name, ia.new_file->script_class_extends, ia.new_file->script_class_icon_path);
_queue_update_script_class(new_file_path, ScriptClassInfoUpdate::from_file_info(ia.new_file));
}
if (ia.new_file->type == SNAME("PackedScene")) {
_queue_update_scene_groups(new_file_path);
@ -860,9 +862,9 @@ bool EditorFileSystem::_update_scan_actions() {
int idx = ia.dir->find_file_index(ia.file);
ERR_CONTINUE(idx == -1);
String script_class_name = ia.dir->files[idx]->script_class_name;
String class_name = ia.dir->files[idx]->class_info.name;
if (ClassDB::is_parent_class(ia.dir->files[idx]->type, SNAME("Script"))) {
_queue_update_script_class(ia.dir->get_file_path(idx), "", "", "", "");
_queue_update_script_class(ia.dir->get_file_path(idx), ScriptClassInfoUpdate());
}
if (ia.dir->files[idx]->type == SNAME("PackedScene")) {
_queue_update_scene_groups(ia.dir->get_file_path(idx));
@ -873,11 +875,11 @@ bool EditorFileSystem::_update_scan_actions() {
ia.dir->files.remove_at(idx);
// Restore another script with the same global class name if it exists.
if (!script_class_name.is_empty()) {
if (!class_name.is_empty()) {
EditorFileSystemDirectory::FileInfo *old_fi = nullptr;
String old_file = _get_file_by_class_name(filesystem, script_class_name, old_fi);
String old_file = _get_file_by_class_name(filesystem, class_name, old_fi);
if (!old_file.is_empty() && old_fi) {
_queue_update_script_class(old_file, old_fi->type, old_fi->script_class_name, old_fi->script_class_extends, old_fi->script_class_icon_path);
_queue_update_script_class(old_file, ScriptClassInfoUpdate::from_file_info(old_fi));
}
}
@ -1151,10 +1153,8 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
fi->import_md5 = fc->import_md5;
fi->import_dest_paths = fc->import_dest_paths;
fi->import_valid = fc->import_valid;
fi->script_class_name = fc->script_class_name;
fi->import_group_file = fc->import_group_file;
fi->script_class_extends = fc->script_class_extends;
fi->script_class_icon_path = fc->script_class_icon_path;
fi->class_info = fc->class_info;
// Ensures backward compatibility when the project is loaded for the first time with the added import_md5
// and import_dest_paths properties in the file cache.
@ -1192,7 +1192,7 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
} else {
// Using get_resource_import_info() to prevent calling 3 times ResourceFormatImporter::_get_path_and_type.
ResourceFormatImporter::get_singleton()->get_resource_import_info(path, fi->type, fi->uid, fi->import_group_file);
fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path);
fi->class_info = _get_global_script_class(fi->type, path);
fi->modified_time = 0;
fi->import_modified_time = 0;
fi->import_md5 = "";
@ -1217,22 +1217,20 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
fi->import_md5 = "";
fi->import_dest_paths = Vector<String>();
fi->import_valid = true;
fi->script_class_name = fc->script_class_name;
fi->script_class_extends = fc->script_class_extends;
fi->script_class_icon_path = fc->script_class_icon_path;
fi->class_info = fc->class_info;
if (first_scan && ClassDB::is_parent_class(fi->type, SNAME("Script"))) {
bool update_script = false;
String old_class_name = fi->script_class_name;
fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path);
if (old_class_name != fi->script_class_name) {
String old_class_name = fi->class_info.name;
fi->class_info = _get_global_script_class(fi->type, path);
if (old_class_name != fi->class_info.name) {
update_script = true;
} else if (!fi->script_class_name.is_empty() && (!ScriptServer::is_global_class(fi->script_class_name) || ScriptServer::get_global_class_path(fi->script_class_name) != path)) {
} else if (!fi->class_info.name.is_empty() && (!ScriptServer::is_global_class(fi->class_info.name) || ScriptServer::get_global_class_path(fi->class_info.name) != path)) {
// This script has a class name but is not in the global class names or the path of the class has changed.
update_script = true;
}
if (update_script) {
_queue_update_script_class(path, fi->type, fi->script_class_name, fi->script_class_extends, fi->script_class_icon_path);
_queue_update_script_class(path, ScriptClassInfoUpdate::from_file_info(fi));
}
}
} else {
@ -1246,7 +1244,7 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
fi->type = "OtherFile";
}
fi->uid = ResourceLoader::get_resource_uid(path);
fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path);
fi->class_info = _get_global_script_class(fi->type, path);
fi->deps = _get_dependencies(path);
fi->modified_time = mt;
fi->import_modified_time = 0;
@ -1257,7 +1255,7 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
// Files in dep_update_list are forced for rescan to update dependencies. They don't need other updates.
if (!dep_update_list.has(path)) {
if (ClassDB::is_parent_class(fi->type, SNAME("Script"))) {
_queue_update_script_class(path, fi->type, fi->script_class_name, fi->script_class_extends, fi->script_class_icon_path);
_queue_update_script_class(path, ScriptClassInfoUpdate::from_file_info(fi));
}
if (fi->type == SNAME("PackedScene")) {
_queue_update_scene_groups(path);
@ -1418,7 +1416,7 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanPr
if (fi->type == "" && other_file_extensions.has(ext)) {
fi->type = "OtherFile";
}
fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path);
fi->class_info = _get_global_script_class(fi->type, path);
fi->import_valid = (fi->type == "TextFile" || fi->type == "OtherFile") ? true : ResourceLoader::is_import_valid(path);
fi->import_group_file = ResourceLoader::get_import_group_file(path);
@ -1577,7 +1575,7 @@ void EditorFileSystem::_remove_invalid_global_class_names(const HashSet<String>
String EditorFileSystem::_get_file_by_class_name(EditorFileSystemDirectory *p_dir, const String &p_class_name, EditorFileSystemDirectory::FileInfo *&r_file_info) {
for (EditorFileSystemDirectory::FileInfo *fi : p_dir->files) {
if (fi->script_class_name == p_class_name) {
if (fi->class_info.name == p_class_name) {
r_file_info = fi;
return p_dir->get_path().path_join(fi->file);
}
@ -1758,7 +1756,7 @@ void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir,
cache_string.append(itos(file_info->import_modified_time));
cache_string.append(itos(file_info->import_valid));
cache_string.append(file_info->import_group_file);
cache_string.append(String("<>").join({ file_info->script_class_name, file_info->script_class_extends, file_info->script_class_icon_path, file_info->import_md5, String("<*>").join(file_info->import_dest_paths) }));
cache_string.append(String("<>").join({ file_info->class_info.name, file_info->class_info.extends, file_info->class_info.icon_path, itos(file_info->class_info.is_abstract), itos(file_info->class_info.is_tool), file_info->import_md5, String("<*>").join(file_info->import_dest_paths) }));
cache_string.append(String("<>").join(file_info->deps));
p_file->store_line(String("::").join(cache_string));
@ -1970,29 +1968,21 @@ 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 {
EditorFileSystem::ScriptClassInfo EditorFileSystem::_get_global_script_class(const String &p_type, const String &p_path) const {
ScriptClassInfo info;
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;
info.name = ScriptServer::get_language(i)->get_global_class_name(p_path, &info.extends, &info.icon_path, &info.is_abstract, &info.is_tool);
}
}
*r_extends = String();
*r_icon_path = String();
return String();
return info;
}
void EditorFileSystem::_update_file_icon_path(EditorFileSystemDirectory::FileInfo *file_info) {
String icon_path;
if (file_info->resource_script_class != StringName()) {
icon_path = EditorNode::get_editor_data().script_class_get_icon_path(file_info->resource_script_class);
} else if (file_info->script_class_icon_path.is_empty() && !file_info->deps.is_empty()) {
} else if (file_info->class_info.icon_path.is_empty() && !file_info->deps.is_empty()) {
const String &script_dep = file_info->deps[0]; // Assuming the first dependency is a script.
const String &script_path = script_dep.contains("::") ? script_dep.get_slice("::", 2) : script_dep;
if (!script_path.is_empty()) {
@ -2004,7 +1994,7 @@ void EditorFileSystem::_update_file_icon_path(EditorFileSystemDirectory::FileInf
int script_file;
EditorFileSystemDirectory *efsd = find_file(script_path, &script_file);
if (efsd) {
icon_path = efsd->files[script_file]->script_class_icon_path;
icon_path = efsd->files[script_file]->class_info.icon_path;
}
}
file_icon_cache.insert(script_path, icon_path);
@ -2019,7 +2009,7 @@ void EditorFileSystem::_update_file_icon_path(EditorFileSystemDirectory::FileInf
}
}
file_info->icon_path = icon_path;
file_info->class_info.icon_path = icon_path;
}
void EditorFileSystem::_update_files_icon_path(EditorFileSystemDirectory *edp) {
@ -2058,10 +2048,10 @@ void EditorFileSystem::_update_script_classes() {
}
int step_count = 0;
for (const KeyValue<String, ScriptInfo> &E : update_script_paths) {
_register_global_class_script(E.key, E.key, E.value.type, E.value.script_class_name, E.value.script_class_extends, E.value.script_class_icon_path);
for (const KeyValue<String, ScriptClassInfoUpdate> &E : update_script_paths) {
_register_global_class_script(E.key, E.key, E.value);
if (ep) {
ep->step(E.value.script_class_name, step_count++, false);
ep->step(E.value.name, step_count++, false);
}
}
@ -2171,16 +2161,10 @@ void EditorFileSystem::_process_update_pending() {
_update_pending_scene_groups();
}
void EditorFileSystem::_queue_update_script_class(const String &p_path, const String &p_type, const String &p_script_class_name, const String &p_script_class_extends, const String &p_script_class_icon_path) {
void EditorFileSystem::_queue_update_script_class(const String &p_path, const ScriptClassInfoUpdate &p_script_update) {
MutexLock update_script_lock(update_script_mutex);
ScriptInfo si;
si.type = p_type;
si.script_class_name = p_script_class_name;
si.script_class_extends = p_script_class_extends;
si.script_class_icon_path = p_script_class_icon_path;
update_script_paths.insert(p_path, si);
update_script_paths.insert(p_path, p_script_update);
update_script_paths_documentation.insert(p_path);
}
@ -2281,8 +2265,10 @@ void EditorFileSystem::update_files(const Vector<String> &p_script_paths) {
}
}
if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) {
_queue_update_script_class(file, fs->files[cpos]->type, "", "", "");
if (!fs->files[cpos]->script_class_icon_path.is_empty()) {
ScriptClassInfoUpdate update;
update.type = fs->files[cpos]->type;
_queue_update_script_class(file, update);
if (!fs->files[cpos]->class_info.icon_path.is_empty()) {
update_files_icon_cache = true;
}
}
@ -2339,16 +2325,16 @@ void EditorFileSystem::update_files(const Vector<String> &p_script_paths) {
}
EditorFileSystemDirectory::FileInfo *fi = fs->files[cpos];
const String old_script_class_icon_path = fi->script_class_icon_path;
const String old_class_name = fi->script_class_name;
const String old_script_class_icon_path = fi->class_info.icon_path;
const String old_class_name = fi->class_info.name;
fi->type = type;
fi->resource_script_class = script_class;
fi->uid = uid;
fi->script_class_name = _get_global_script_class(type, file, &fi->script_class_extends, &fi->script_class_icon_path);
fi->class_info = _get_global_script_class(type, file);
fi->import_group_file = ResourceLoader::get_import_group_file(file);
fi->modified_time = FileAccess::get_modified_time(file);
fi->deps = _get_dependencies(file);
fi->import_valid = type == "TextFile" || type == "OtherFile" || ResourceLoader::is_import_valid(file);
fi->import_valid = (type == "TextFile" || type == "OtherFile") ? true : ResourceLoader::is_import_valid(file);
if (uid != ResourceUID::INVALID_ID) {
if (ResourceUID::get_singleton()->has_id(uid)) {
@ -2374,7 +2360,7 @@ void EditorFileSystem::update_files(const Vector<String> &p_script_paths) {
EditorResourcePreview::get_singleton()->check_for_invalidation(file);
if (ClassDB::is_parent_class(fi->type, SNAME("Script"))) {
_queue_update_script_class(file, fi->type, fi->script_class_name, fi->script_class_extends, fi->script_class_icon_path);
_queue_update_script_class(file, ScriptClassInfoUpdate::from_file_info(fi));
}
if (fi->type == SNAME("PackedScene")) {
_queue_update_scene_groups(file);
@ -2382,16 +2368,16 @@ void EditorFileSystem::update_files(const Vector<String> &p_script_paths) {
if (ClassDB::is_parent_class(fi->type, SNAME("Resource"))) {
files_to_update_icon_path.push_back(fi);
} else if (old_script_class_icon_path != fi->script_class_icon_path) {
} else if (old_script_class_icon_path != fi->class_info.icon_path) {
update_files_icon_cache = true;
}
// Restore another script as the global class name if multiple scripts had the same old class name.
if (!old_class_name.is_empty() && fi->script_class_name != old_class_name && ClassDB::is_parent_class(type, SNAME("Script"))) {
if (!old_class_name.is_empty() && fi->class_info.name != old_class_name && ClassDB::is_parent_class(type, SNAME("Script"))) {
EditorFileSystemDirectory::FileInfo *old_fi = nullptr;
String old_file = _get_file_by_class_name(filesystem, old_class_name, old_fi);
if (!old_file.is_empty() && old_fi) {
_queue_update_script_class(old_file, old_fi->type, old_fi->script_class_name, old_fi->script_class_extends, old_fi->script_class_icon_path);
_queue_update_script_class(old_file, ScriptClassInfoUpdate::from_file_info(old_fi));
}
}
updated = true;
@ -2425,16 +2411,16 @@ HashSet<String> EditorFileSystem::get_valid_extensions() const {
return valid_extensions;
}
void EditorFileSystem::_register_global_class_script(const String &p_search_path, const String &p_target_path, const String &p_type, const String &p_script_class_name, const String &p_script_class_extends, const String &p_script_class_icon_path) {
void EditorFileSystem::_register_global_class_script(const String &p_search_path, const String &p_target_path, const ScriptClassInfoUpdate &p_script_update) {
ScriptServer::remove_global_class_by_path(p_search_path); // First remove, just in case it changed
if (p_script_class_name.is_empty()) {
if (p_script_update.name.is_empty()) {
return;
}
String lang;
for (int j = 0; j < ScriptServer::get_language_count(); j++) {
if (ScriptServer::get_language(j)->handles_global_class_type(p_type)) {
if (ScriptServer::get_language(j)->handles_global_class_type(p_script_update.type)) {
lang = ScriptServer::get_language(j)->get_name();
break;
}
@ -2443,9 +2429,9 @@ void EditorFileSystem::_register_global_class_script(const String &p_search_path
return; // No lang found that can handle this global class
}
ScriptServer::add_global_class(p_script_class_name, p_script_class_extends, lang, p_target_path);
EditorNode::get_editor_data().script_class_set_icon_path(p_script_class_name, p_script_class_icon_path);
EditorNode::get_editor_data().script_class_set_name(p_target_path, p_script_class_name);
ScriptServer::add_global_class(p_script_update.name, p_script_update.extends, lang, p_target_path, p_script_update.is_abstract, p_script_update.is_tool);
EditorNode::get_editor_data().script_class_set_icon_path(p_script_update.name, p_script_update.icon_path);
EditorNode::get_editor_data().script_class_set_name(p_target_path, p_script_update.name);
}
void EditorFileSystem::register_global_class_script(const String &p_search_path, const String &p_target_path) {
@ -2453,7 +2439,7 @@ void EditorFileSystem::register_global_class_script(const String &p_search_path,
EditorFileSystemDirectory *efsd = find_file(p_search_path, &index_file);
if (efsd) {
const EditorFileSystemDirectory::FileInfo *fi = efsd->files[index_file];
EditorFileSystem::get_singleton()->_register_global_class_script(p_search_path, p_target_path, fi->type, fi->script_class_name, fi->script_class_extends, fi->script_class_icon_path);
EditorFileSystem::get_singleton()->_register_global_class_script(p_search_path, p_target_path, ScriptClassInfoUpdate::from_file_info(fi));
} else {
ScriptServer::remove_global_class_by_path(p_search_path);
}

View File

@ -66,11 +66,15 @@ class EditorFileSystemDirectory : public Object {
String import_group_file;
Vector<String> deps;
bool verified = false; //used for checking changes
// These are for script resources only.
String script_class_name;
String script_class_extends;
String script_class_icon_path;
String icon_path;
// This is for script resources only.
struct ScriptClassInfo {
String name;
String extends;
String icon_path;
bool is_abstract = false;
bool is_tool = false;
};
ScriptClassInfo class_info;
};
Vector<FileInfo *> files;
@ -203,9 +207,11 @@ class EditorFileSystem : public Node {
static EditorFileSystem *singleton;
using ScriptClassInfo = EditorFileSystemDirectory::FileInfo::ScriptClassInfo;
/* Used for reading the filesystem cache file */
struct FileCache {
String type;
StringName type;
String resource_script_class;
ResourceUID::ID uid = ResourceUID::INVALID_ID;
uint64_t modification_time = 0;
@ -215,9 +221,7 @@ class EditorFileSystem : public Node {
Vector<String> deps;
bool import_valid = false;
String import_group_file;
String script_class_name;
String script_class_extends;
String script_class_icon_path;
ScriptClassInfo class_info;
};
HashMap<String, FileCache> file_cache;
@ -288,17 +292,27 @@ class EditorFileSystem : public Node {
}
};
struct ScriptInfo {
String type;
String script_class_name;
String script_class_extends;
String script_class_icon_path;
struct ScriptClassInfoUpdate : public ScriptClassInfo {
StringName type;
ScriptClassInfoUpdate() = default;
explicit ScriptClassInfoUpdate(const ScriptClassInfo &p_info) :
ScriptClassInfo(p_info) {}
static ScriptClassInfoUpdate from_file_info(const EditorFileSystemDirectory::FileInfo *p_fi) {
ScriptClassInfoUpdate update;
update.type = p_fi->type;
update.name = p_fi->class_info.name;
update.extends = p_fi->class_info.extends;
update.icon_path = p_fi->class_info.icon_path;
update.is_abstract = p_fi->class_info.is_abstract;
update.is_tool = p_fi->class_info.is_tool;
return update;
}
};
Mutex update_script_mutex;
HashMap<String, ScriptInfo> update_script_paths;
HashMap<String, ScriptClassInfoUpdate> update_script_paths;
HashSet<String> update_script_paths_documentation;
void _queue_update_script_class(const String &p_path, const String &p_type, const String &p_script_class_name, const String &p_script_class_extends, const String &p_script_class_icon_path);
void _queue_update_script_class(const String &p_path, const ScriptClassInfoUpdate &p_script_update);
void _update_script_classes();
void _update_script_documentation();
void _process_update_pending();
@ -312,7 +326,7 @@ class EditorFileSystem : public Node {
void _update_pending_scene_groups();
void _get_all_scenes(EditorFileSystemDirectory *p_dir, HashSet<String> &r_list);
String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const;
ScriptClassInfo _get_global_script_class(const String &p_type, const String &p_path) const;
static Error _resource_import(const String &p_path);
static Ref<Resource> _load_resource_on_startup(ResourceFormatImporter *p_importer, const String &p_path, Error *r_error, bool p_use_sub_threads, float *r_progress, ResourceFormatLoader::CacheMode p_cache_mode);
@ -359,7 +373,7 @@ class EditorFileSystem : public Node {
void _remove_invalid_global_class_names(const HashSet<String> &p_existing_class_names);
String _get_file_by_class_name(EditorFileSystemDirectory *p_dir, const String &p_class_name, EditorFileSystemDirectory::FileInfo *&r_file_info);
void _register_global_class_script(const String &p_search_path, const String &p_target_path, const String &p_type, const String &p_script_class_name, const String &p_script_class_extends, const String &p_script_class_icon_path);
void _register_global_class_script(const String &p_search_path, const String &p_target_path, const ScriptClassInfoUpdate &p_script_update);
protected:
void _notification(int p_what);

View File

@ -2831,7 +2831,7 @@ bool GDScriptLanguage::handles_global_class_type(const String &p_type) const {
return p_type == "GDScript";
}
String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path, bool *r_is_abstract, bool *r_is_tool) const {
Error err;
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
if (err) {
@ -2932,6 +2932,12 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
if (r_icon_path) {
*r_icon_path = c->simplified_icon_path;
}
if (r_is_abstract) {
*r_is_abstract = false;
}
if (r_is_tool) {
*r_is_tool = parser.is_tool();
}
return c->identifier != nullptr ? String(c->identifier->name) : String();
}

View File

@ -638,7 +638,7 @@ public:
/* GLOBAL CLASSES */
virtual bool handles_global_class_type(const String &p_type) const override;
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const override;
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr, bool *r_is_abstract = nullptr, bool *r_is_tool = nullptr) const override;
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

@ -368,7 +368,9 @@ static bool generate_class_index_recursive(const String &p_dir) {
}
String base_type;
String source_file = current_dir.path_join(next);
String class_name = GDScriptLanguage::get_singleton()->get_global_class_name(source_file, &base_type);
bool is_abstract = false;
bool is_tool = false;
String class_name = GDScriptLanguage::get_singleton()->get_global_class_name(source_file, &base_type, nullptr, &is_abstract, &is_tool);
if (class_name.is_empty()) {
next = dir->get_next();
continue;
@ -376,7 +378,7 @@ static bool generate_class_index_recursive(const String &p_dir) {
ERR_FAIL_COND_V_MSG(ScriptServer::is_global_class(class_name), false,
"Class name '" + class_name + "' from " + source_file + " is already used in " + ScriptServer::get_global_class_path(class_name));
ScriptServer::add_global_class(class_name, base_type, gdscript_name, source_file);
ScriptServer::add_global_class(class_name, base_type, gdscript_name, source_file, is_abstract, is_tool);
}
next = dir->get_next();

View File

@ -297,10 +297,10 @@ void test(TestType p_type) {
TypedArray<Dictionary> script_classes = ProjectSettings::get_singleton()->get_global_class_list();
for (int i = 0; i < script_classes.size(); i++) {
Dictionary c = script_classes[i];
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) {
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base") || !c.has("is_abstract") || !c.has("is_tool")) {
continue;
}
ScriptServer::add_global_class(c["class"], c["base"], c["language"], c["path"]);
ScriptServer::add_global_class(c["class"], c["base"], c["language"], c["path"], c["is_abstract"], c["is_tool"]);
}
Vector<uint8_t> buf;

View File

@ -445,9 +445,9 @@ bool CSharpLanguage::handles_global_class_type(const String &p_type) const {
return p_type == get_type();
}
String CSharpLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
String CSharpLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path, bool *r_is_abstract, bool *r_is_tool) const {
String class_name;
GDMonoCache::managed_callbacks.ScriptManagerBridge_GetGlobalClassName(&p_path, r_base_type, r_icon_path, &class_name);
GDMonoCache::managed_callbacks.ScriptManagerBridge_GetGlobalClassName(&p_path, r_base_type, r_icon_path, r_is_abstract, r_is_tool, &class_name);
return class_name;
}

View File

@ -530,7 +530,7 @@ public:
/* SCRIPT GLOBAL CLASS FUNCTIONS */
virtual bool handles_global_class_type(const String &p_type) const override;
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const override;
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr, bool *r_is_abstract = nullptr, bool *r_is_tool = nullptr) const override;
/* DEBUGGER FUNCTIONS */
String debug_get_error() const override;

View File

@ -19,7 +19,7 @@ namespace Godot.Bridge
public delegate* unmanaged<godot_string_name*, IntPtr, IntPtr> ScriptManagerBridge_CreateManagedForGodotObjectBinding;
public delegate* unmanaged<IntPtr, IntPtr, godot_variant**, int, godot_bool> ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance;
public delegate* unmanaged<IntPtr, godot_string_name*, void> ScriptManagerBridge_GetScriptNativeName;
public delegate* unmanaged<godot_string*, godot_string*, godot_string*, godot_string*, void> ScriptManagerBridge_GetGlobalClassName;
public delegate* unmanaged<godot_string*, godot_string*, godot_string*, godot_bool*, godot_bool*, godot_string*, void> ScriptManagerBridge_GetGlobalClassName;
public delegate* unmanaged<IntPtr, IntPtr, void> ScriptManagerBridge_SetGodotObjectPtr;
public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant**, int, godot_bool*, void> ScriptManagerBridge_RaiseEventSignal;
public delegate* unmanaged<IntPtr, IntPtr, godot_bool> ScriptManagerBridge_ScriptIsOrInherits;

View File

@ -199,7 +199,7 @@ namespace Godot.Bridge
}
[UnmanagedCallersOnly]
internal static unsafe void GetGlobalClassName(godot_string* scriptPath, godot_string* outBaseType, godot_string* outIconPath, godot_string* outClassName)
internal static unsafe void GetGlobalClassName(godot_string* scriptPath, godot_string* outBaseType, godot_string* outIconPath, godot_bool* outIsAbstract, godot_bool* outIsTool, godot_string* outClassName)
{
// This method must always return the outBaseType for every script, even if the script is
// not a global class. But if the script is not a global class it must return an empty
@ -254,6 +254,16 @@ namespace Godot.Bridge
}
}
if (outIsAbstract != null)
{
*outIsAbstract = scriptType.IsAbstract.ToGodotBool();
}
if (outIsTool != null)
{
*outIsTool = Attribute.IsDefined(scriptType, typeof(ToolAttribute)).ToGodotBool();
}
if (!IsGlobalClass(scriptType))
{
// Scripts that are not global classes should not have a name.

View File

@ -85,7 +85,7 @@ struct ManagedCallbacks {
using FuncScriptManagerBridge_CreateManagedForGodotObjectBinding = GCHandleIntPtr(GD_CLR_STDCALL *)(const StringName *, Object *);
using FuncScriptManagerBridge_CreateManagedForGodotObjectScriptInstance = bool(GD_CLR_STDCALL *)(const CSharpScript *, Object *, const Variant **, int32_t);
using FuncScriptManagerBridge_GetScriptNativeName = void(GD_CLR_STDCALL *)(const CSharpScript *, StringName *);
using FuncScriptManagerBridge_GetGlobalClassName = void(GD_CLR_STDCALL *)(const String *, String *, String *, String *);
using FuncScriptManagerBridge_GetGlobalClassName = void(GD_CLR_STDCALL *)(const String *, String *, String *, bool *, bool *, String *);
using FuncScriptManagerBridge_SetGodotObjectPtr = void(GD_CLR_STDCALL *)(GCHandleIntPtr, Object *);
using FuncScriptManagerBridge_RaiseEventSignal = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, const Variant **, int32_t, bool *);
using FuncScriptManagerBridge_ScriptIsOrInherits = bool(GD_CLR_STDCALL *)(const CSharpScript *, const CSharpScript *);