Global class names (and GDScript support for it)

This commit is contained in:
Juan Linietsky 2018-07-15 19:29:00 -03:00
parent f6ce73f724
commit a3f1ee5c57
13 changed files with 381 additions and 10 deletions

View file

@ -515,7 +515,11 @@ Error ProjectSettings::_load_settings_text(const String p_path) {
}
} else {
// config_version is checked and dropped
set(section + "/" + assign, value);
if (section == String()) {
set(assign, value);
} else {
set(section + "/" + assign, value);
}
}
} else if (next_tag.name != String()) {
section = next_tag.name;

View file

@ -29,6 +29,7 @@
/*************************************************************************/
#include "script_language.h"
#include "project_settings.h"
ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES];
int ScriptServer::_language_count = 0;
@ -103,6 +104,20 @@ void ScriptServer::unregister_language(ScriptLanguage *p_language) {
void ScriptServer::init_languages() {
{ //load global classes
global_classes_clear();
if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) {
Array script_classes = ProjectSettings::get_singleton()->get("_global_script_classes");
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"))
continue;
add_global_class(c["class"], c["base"], c["language"], c["path"]);
}
}
}
for (int i = 0; i < _language_count; i++) {
_languages[i]->init();
}
@ -113,6 +128,7 @@ void ScriptServer::finish_languages() {
for (int i = 0; i < _language_count; i++) {
_languages[i]->finish();
}
global_classes_clear();
}
void ScriptServer::set_reload_scripts_on_save(bool p_enable) {
@ -139,6 +155,67 @@ void ScriptServer::thread_exit() {
}
}
HashMap<StringName, ScriptServer::GlobalScriptClass> ScriptServer::global_classes;
void ScriptServer::global_classes_clear() {
global_classes.clear();
}
void ScriptServer::add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path) {
GlobalScriptClass g;
g.language = p_language;
g.path = p_path;
g.base = p_base;
global_classes[p_class] = g;
}
void ScriptServer::remove_global_class(const StringName &p_class) {
global_classes.erase(p_class);
}
bool ScriptServer::is_global_class(const StringName &p_class) {
return global_classes.has(p_class);
}
StringName ScriptServer::get_global_class_language(const StringName &p_class) {
ERR_FAIL_COND_V(!global_classes.has(p_class), StringName());
return global_classes[p_class].language;
}
String ScriptServer::get_global_class_path(const String &p_class) {
ERR_FAIL_COND_V(!global_classes.has(p_class), String());
return global_classes[p_class].path;
}
StringName ScriptServer::get_global_class_base(const String &p_class) {
ERR_FAIL_COND_V(!global_classes.has(p_class), String());
return global_classes[p_class].base;
}
void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) {
const StringName *K = NULL;
List<StringName> classes;
while ((K = global_classes.next(K))) {
classes.push_back(*K);
}
classes.sort_custom<StringName::AlphCompare>();
for (List<StringName>::Element *E = classes.front(); E; E = E->next()) {
r_global_classes->push_back(E->get());
}
}
void ScriptServer::save_global_classes() {
List<StringName> gc;
get_global_class_list(&gc);
Array gcarr;
for (List<StringName>::Element *E = gc.front(); E; E = E->next()) {
Dictionary d;
d["class"] = E->get();
d["language"] = global_classes[E->get()].language;
d["path"] = global_classes[E->get()].path;
d["base"] = global_classes[E->get()].base;
gcarr.push_back(d);
}
ProjectSettings::get_singleton()->set("_global_script_classes", gcarr);
ProjectSettings::get_singleton()->save();
}
////////////////////
void ScriptInstance::get_property_state(List<Pair<StringName, Variant> > &state) {
List<PropertyInfo> pinfo;

View file

@ -54,6 +54,14 @@ class ScriptServer {
static bool scripting_enabled;
static bool reload_scripts_on_save;
struct GlobalScriptClass {
StringName language;
String path;
String base;
};
static HashMap<StringName, GlobalScriptClass> global_classes;
public:
static ScriptEditRequestFunction edit_request_func;
@ -70,6 +78,16 @@ public:
static void thread_enter();
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 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 void get_global_class_list(List<StringName> *r_global_classes);
static void save_global_classes();
static void init_languages();
static void finish_languages();
};
@ -285,7 +303,10 @@ public:
virtual void frame();
virtual ~ScriptLanguage(){};
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 = NULL) const { return String(); }
virtual ~ScriptLanguage() {}
};
extern uint8_t script_encryption_key[32];

View file

@ -243,6 +243,18 @@ void CreateDialog::_update_search() {
_parse_fs(EditorFileSystem::get_singleton()->get_filesystem());
*/
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
Map<String, List<String> > global_class_map;
for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) {
String base = ScriptServer::get_global_class_base(E->get());
if (!global_class_map.has(base)) {
global_class_map[base] = List<String>();
}
global_class_map[base].push_back(E->get());
}
HashMap<String, TreeItem *> types;
TreeItem *root = search_options->create_item();
@ -293,6 +305,32 @@ void CreateDialog::_update_search() {
add_type(I->get(), types, root, &to_select);
}
if (global_class_map.has(type) && ClassDB::is_parent_class(type, base_type)) {
for (List<String>::Element *J = global_class_map[type].front(); J; J = J->next()) {
bool show = search_box->get_text().is_subsequence_ofi(J->get());
if (!show)
continue;
if (!types.has(type))
add_type(type, types, root, &to_select);
TreeItem *ti;
if (types.has(type))
ti = types[type];
else
ti = search_options->get_root();
TreeItem *item = search_options->create_item(ti);
item->set_metadata(0, J->get());
item->set_text(0, J->get() + " (" + ScriptServer::get_global_class_path(J->get()).get_file() + ")");
item->set_icon(0, _get_editor_icon(type));
if (!to_select || J->get() == search_box->get_text()) {
to_select = item;
}
}
}
if (EditorNode::get_editor_data().get_custom_types().has(type) && ClassDB::is_parent_class(type, base_type)) {
//there are custom types based on this... cool.
@ -444,6 +482,17 @@ Object *CreateDialog::instance_selected() {
custom = md;
if (custom != String()) {
if (ScriptServer::is_global_class(custom)) {
RES script = ResourceLoader::load(ScriptServer::get_global_class_path(custom));
ERR_FAIL_COND_V(!script.is_valid(), NULL);
Object *obj = ClassDB::instance(ScriptServer::get_global_class_base(custom));
ERR_FAIL_COND_V(!obj, NULL);
obj->set_script(script.get_ref_ptr());
return obj;
}
return EditorNode::get_editor_data().instance_custom_type(selected->get_text(0), custom);
} else {
return ClassDB::instance(selected->get_text(0));

View file

@ -125,6 +125,14 @@ bool EditorFileSystemDirectory::get_file_import_is_valid(int p_idx) const {
return files[p_idx]->import_valid;
}
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;
}
StringName EditorFileSystemDirectory::get_file_type(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, files.size(), "");
@ -149,6 +157,8 @@ 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);
@ -189,7 +199,7 @@ void EditorFileSystem::_scan_filesystem() {
String project = ProjectSettings::get_singleton()->get_resource_path();
String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_cache3");
String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_cache4");
FileAccess *f = FileAccess::open(fscache, FileAccess::READ);
if (f) {
@ -209,7 +219,7 @@ void EditorFileSystem::_scan_filesystem() {
} else {
Vector<String> split = l.split("::");
ERR_CONTINUE(split.size() != 6);
ERR_CONTINUE(split.size() != 7);
String name = split[0];
String file;
@ -221,8 +231,10 @@ void EditorFileSystem::_scan_filesystem() {
fc.modification_time = split[2].to_int64();
fc.import_modification_time = split[3].to_int64();
fc.import_valid = split[4].to_int64() != 0;
fc.script_class_name = split[5].get_slice("<>", 0);
fc.script_class_extends = split[5].get_slice("<>", 1);
String deps = split[5].strip_edges();
String deps = split[6].strip_edges();
if (deps.length()) {
Vector<String> dp = deps.split("<>");
for (int i = 0; i < dp.size(); i++) {
@ -239,7 +251,7 @@ void EditorFileSystem::_scan_filesystem() {
memdelete(f);
}
String update_cache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update3");
String update_cache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update4");
if (FileAccess::exists(update_cache)) {
{
@ -287,7 +299,7 @@ void EditorFileSystem::_scan_filesystem() {
}
void EditorFileSystem::_save_filesystem_cache() {
String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_cache3");
String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_cache4");
FileAccess *f = FileAccess::open(fscache, FileAccess::WRITE);
if (f == NULL) {
@ -563,6 +575,7 @@ void EditorFileSystem::scan() {
scanning = false;
emit_signal("filesystem_changed");
emit_signal("sources_changed", sources_changed.size() > 0);
_queue_update_script_classes();
} else {
@ -706,6 +719,9 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
fi->modified_time = fc->modification_time;
fi->import_modified_time = fc->import_modification_time;
fi->import_valid = fc->import_valid;
fi->script_class_name = fc->script_class_name;
fi->script_class_extends = fc->script_class_extends;
if (fc->type == String()) {
fi->type = ResourceLoader::get_resource_type(path);
//there is also the chance that file type changed due to reimport, must probably check this somehow here (or kind of note it for next time in another file?)
@ -715,6 +731,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
} else {
fi->type = ResourceFormatImporter::get_singleton()->get_resource_type(path);
fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends);
fi->modified_time = 0;
fi->import_modified_time = 0;
fi->import_valid = ResourceLoader::is_import_valid(path);
@ -734,9 +751,12 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
fi->deps = fc->deps;
fi->import_modified_time = 0;
fi->import_valid = true;
fi->script_class_name = fc->script_class_name;
fi->script_class_extends = fc->script_class_extends;
} else {
//new or modified time
fi->type = ResourceLoader::get_resource_type(path);
fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends);
fi->deps = _get_dependencies(path);
fi->modified_time = mt;
fi->import_modified_time = 0;
@ -835,6 +855,7 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const
fi->modified_time = FileAccess::get_modified_time(path);
fi->import_modified_time = 0;
fi->type = ResourceLoader::get_resource_type(path);
fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends);
fi->import_valid = ResourceLoader::is_import_valid(path);
{
@ -1044,6 +1065,7 @@ void EditorFileSystem::_notification(int p_what) {
if (_update_scan_actions())
emit_signal("filesystem_changed");
emit_signal("sources_changed", sources_changed.size() > 0);
_queue_update_script_classes();
}
} else if (!scanning) {
@ -1059,6 +1081,7 @@ void EditorFileSystem::_notification(int p_what) {
_update_scan_actions();
emit_signal("filesystem_changed");
emit_signal("sources_changed", sources_changed.size() > 0);
_queue_update_script_classes();
}
}
} break;
@ -1087,7 +1110,7 @@ void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir,
for (int i = 0; i < p_dir->files.size(); i++) {
String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid);
String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid) + "::" + p_dir->files[i]->script_class_name + "<>" + p_dir->files[i]->script_class_extends;
s += "::";
for (int j = 0; j < p_dir->files[i]->deps.size(); j++) {
@ -1268,7 +1291,7 @@ EditorFileSystemDirectory *EditorFileSystem::get_filesystem_path(const String &p
void EditorFileSystem::_save_late_updated_files() {
//files that already existed, and were modified, need re-scanning for dependencies upon project restart. This is done via saving this special file
String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update3");
String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update4");
FileAccessRef f = FileAccess::open(fscache, FileAccess::WRITE);
for (Set<String>::Element *E = late_update_files.front(); E; E = E->next()) {
f->store_line(E->get());
@ -1293,6 +1316,67 @@ 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) 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;
global_name = ScriptServer::get_language(i)->get_global_class_name(p_path, &extends);
*r_extends = extends;
return global_name;
}
}
*r_extends = String();
return String();
}
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()) {
continue;
}
String lang;
for (int j = 0; j < ScriptServer::get_language_count(); j++) {
if (ScriptServer::get_language(j)->handles_global_class_type(files[i]->type)) {
lang = ScriptServer::get_language(j)->get_name();
}
}
ScriptServer::add_global_class(files[i]->script_class_name, files[i]->script_class_extends, lang, p_dir->get_file_path(i));
}
for (int i = 0; i < p_dir->get_subdir_count(); i++) {
_scan_script_classes(p_dir->get_subdir(i));
}
}
void EditorFileSystem::update_script_classes() {
if (!update_script_classes_queued)
return;
update_script_classes_queued = false;
ScriptServer::global_classes_clear();
if (get_filesystem()) {
_scan_script_classes(get_filesystem());
}
ScriptServer::save_global_classes();
}
void EditorFileSystem::_queue_update_script_classes() {
if (update_script_classes_queued) {
return;
}
update_script_classes_queued = true;
call_deferred("update_script_classes");
}
void EditorFileSystem::update_file(const String &p_file) {
EditorFileSystemDirectory *fs = NULL;
@ -1311,7 +1395,9 @@ void EditorFileSystem::update_file(const String &p_file) {
memdelete(fs->files[cpos]);
fs->files.remove(cpos);
}
call_deferred("emit_signal", "filesystem_changed"); //update later
_queue_update_script_classes();
return;
}
@ -1351,6 +1437,7 @@ void EditorFileSystem::update_file(const String &p_file) {
}
fs->files[cpos]->type = type;
fs->files[cpos]->script_class_name = _get_global_script_class(type, p_file, &fs->files[cpos]->script_class_extends);
fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file);
fs->files[cpos]->deps = _get_dependencies(p_file);
fs->files[cpos]->import_valid = ResourceLoader::is_import_valid(p_file);
@ -1359,6 +1446,7 @@ void EditorFileSystem::update_file(const String &p_file) {
EditorResourcePreview::get_singleton()->check_for_invalidation(p_file);
call_deferred("emit_signal", "filesystem_changed"); //update later
_queue_update_script_classes();
}
void EditorFileSystem::_reimport_file(const String &p_file) {
@ -1611,6 +1699,7 @@ void EditorFileSystem::_bind_methods() {
ClassDB::bind_method(D_METHOD("update_file", "path"), &EditorFileSystem::update_file);
ClassDB::bind_method(D_METHOD("get_filesystem_path", "path"), &EditorFileSystem::get_filesystem_path);
ClassDB::bind_method(D_METHOD("get_file_type", "path"), &EditorFileSystem::get_file_type);
ClassDB::bind_method(D_METHOD("update_script_classes"), &EditorFileSystem::update_script_classes);
ADD_SIGNAL(MethodInfo("filesystem_changed"));
ADD_SIGNAL(MethodInfo("sources_changed", PropertyInfo(Variant::BOOL, "exist")));
@ -1664,6 +1753,7 @@ EditorFileSystem::EditorFileSystem() {
memdelete(da);
scan_total = 0;
update_script_classes_queued = false;
}
EditorFileSystem::~EditorFileSystem() {

View file

@ -58,6 +58,8 @@ class EditorFileSystemDirectory : public Object {
bool import_valid;
Vector<String> deps;
bool verified; //used for checking changes
String script_class_name;
String script_class_extends;
};
struct FileInfoSort {
@ -86,6 +88,8 @@ public:
StringName get_file_type(int p_idx) const;
Vector<String> get_file_deps(int p_idx) const;
bool get_file_import_is_valid(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
EditorFileSystemDirectory *get_parent();
@ -157,6 +161,8 @@ class EditorFileSystem : public Node {
uint64_t import_modification_time;
Vector<String> deps;
bool import_valid;
String script_class_name;
String script_class_extends;
};
HashMap<String, FileCache> file_cache;
@ -215,6 +221,12 @@ class EditorFileSystem : public Node {
}
};
void _scan_script_classes(EditorFileSystemDirectory *p_dir);
volatile bool 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) const;
protected:
void _notification(int p_what);
static void _bind_methods();
@ -237,6 +249,8 @@ public:
void reimport_files(const Vector<String> &p_files);
void update_script_classes();
EditorFileSystem();
~EditorFileSystem();
};

View file

@ -1872,6 +1872,7 @@ void ScriptEditor::save_all_scripts() {
}
_update_script_names();
EditorFileSystem::get_singleton()->update_script_classes();
}
void ScriptEditor::apply_scripts() const {

View file

@ -1739,6 +1739,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"assert",
"breakpoint",
"class",
"class_name",
"extends",
"is",
"func",
@ -1788,6 +1789,50 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
}
}
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) const {
PoolVector<uint8_t> sourcef;
Error err;
FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
if (err) {
return String();
}
int len = f->get_len();
sourcef.resize(len + 1);
PoolVector<uint8_t>::Write w = sourcef.write();
int r = f->get_buffer(w.ptr(), len);
f->close();
memdelete(f);
ERR_FAIL_COND_V(r != len, String());
w[len] = 0;
String s;
if (s.parse_utf8((const char *)w.ptr())) {
return String();
}
GDScriptParser parser;
parser.parse(s, p_path.get_base_dir(), true, p_path);
if (parser.get_parse_tree() && parser.get_parse_tree()->type == GDScriptParser::Node::TYPE_CLASS) {
const GDScriptParser::ClassNode *c = static_cast<const GDScriptParser::ClassNode *>(parser.get_parse_tree());
if (r_base_type && c->extends_used && c->extends_class.size() == 1) {
*r_base_type = c->extends_class[0]; //todo, should work much better
}
return c->name;
}
return String();
}
GDScriptLanguage::GDScriptLanguage() {
calls = 0;

View file

@ -439,6 +439,11 @@ public:
virtual void get_recognized_extensions(List<String> *p_extensions) const;
/* 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 = NULL) const;
GDScriptLanguage();
~GDScriptLanguage();
};

View file

@ -278,6 +278,41 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
return idx | (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
}
/* TRY GLOBAL CLASSES */
if (ScriptServer::is_global_class(identifier)) {
const GDScriptParser::ClassNode *class_node = codegen.class_node;
while (class_node->owner) {
class_node = class_node->owner;
}
if (class_node->name == identifier) {
_set_error("Using own name in class file is not allowed (creates a cyclic reference)", p_expression);
return -1;
}
RES res = ResourceLoader::load(ScriptServer::get_global_class_path(identifier));
if (res.is_null()) {
_set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
return -1;
}
Variant key = res;
int idx;
if (!codegen.constant_map.has(key)) {
idx = codegen.constant_map.size();
codegen.constant_map[key] = idx;
} else {
idx = codegen.constant_map[key];
}
return idx | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS); //make it a local constant (faster access)
}
#ifdef TOOLS_ENABLED
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {

View file

@ -3111,6 +3111,28 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
} break;
case GDScriptTokenizer::TK_PR_CLASS_NAME: {
if (p_class->owner) {
_set_error("'class_name' is only valid for the main class namespace.");
return;
}
if (tokenizer->get_token(1) != GDScriptTokenizer::TK_IDENTIFIER) {
_set_error("'class_name' syntax: 'class_name <UniqueName>'");
return;
}
p_class->name = tokenizer->get_token_identifier(1);
if (self_path != String() && ScriptServer::is_global_class(p_class->name) && ScriptServer::get_global_class_path(p_class->name) != self_path) {
_set_error("Unique global class '" + p_class->name + "' already exists at path: " + ScriptServer::get_global_class_path(p_class->name));
return;
}
tokenizer->advance(2);
} break;
case GDScriptTokenizer::TK_PR_TOOL: {
@ -3138,6 +3160,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
name = tokenizer->get_token_identifier(1);
tokenizer->advance(2);
if (ScriptServer::is_global_class(name)) {
_set_error("Can't override name of unique global class '" + name + "' already exists at path: " + ScriptServer::get_global_class_path(p_class->name));
return;
}
ClassNode *newclass = alloc_node<ClassNode>();
newclass->initializer = alloc_node<BlockNode>();
newclass->initializer->parent_class = newclass;

View file

@ -91,6 +91,7 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = {
"match",
"func",
"class",
"class_name",
"extends",
"is",
"onready",
@ -187,6 +188,7 @@ static const _kws _keyword_list[] = {
//func
{ GDScriptTokenizer::TK_PR_FUNCTION, "func" },
{ GDScriptTokenizer::TK_PR_CLASS, "class" },
{ GDScriptTokenizer::TK_PR_CLASS_NAME, "class_name" },
{ GDScriptTokenizer::TK_PR_EXTENDS, "extends" },
{ GDScriptTokenizer::TK_PR_IS, "is" },
{ GDScriptTokenizer::TK_PR_ONREADY, "onready" },
@ -1137,7 +1139,7 @@ void GDScriptTokenizerText::advance(int p_amount) {
//////////////////////////////////////////////////////////////////////////////////////////////////////
#define BYTECODE_VERSION 12
#define BYTECODE_VERSION 13
Error GDScriptTokenizerBuffer::set_code_buffer(const Vector<uint8_t> &p_buffer) {

View file

@ -96,6 +96,7 @@ public:
TK_CF_MATCH,
TK_PR_FUNCTION,
TK_PR_CLASS,
TK_PR_CLASS_NAME,
TK_PR_EXTENDS,
TK_PR_IS,
TK_PR_ONREADY,