diff options
Diffstat (limited to 'core')
114 files changed, 6077 insertions, 1135 deletions
diff --git a/core/SCsub b/core/SCsub index 383aaf0e12..c8e2e10b9f 100644 --- a/core/SCsub +++ b/core/SCsub @@ -2,6 +2,10 @@ Import('env') +import core_builders +import make_binders +from platform_methods import run_in_subprocess + env.core_sources = [] @@ -19,7 +23,7 @@ gd_cpp += gd_inc gd_cpp += "void ProjectSettings::register_global_defaults() {\n" + gd_call + "\n}\n" with open("global_defaults.gen.cpp", "w") as f: - f.write(gd_cpp) + f.write(gd_cpp) # Generate AES256 script encryption key @@ -46,26 +50,27 @@ if ("SCRIPT_AES256_ENCRYPTION_KEY" in os.environ): txt = "0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0" print("Invalid AES256 encryption key, not 64 bits hex: " + e) +# NOTE: It is safe to generate this file here, since this is still executed serially with open("script_encryption_key.gen.cpp", "w") as f: - f.write("#include \"project_settings.h\"\nuint8_t script_encryption_key[32]={" + txt + "};\n") + f.write("#include \"project_settings.h\"\nuint8_t script_encryption_key[32]={" + txt + "};\n") # Add required thirdparty code. Header paths are hardcoded, we don't need to append # to the include path (saves a few chars on the compiler invocation for touchy MSVC...) thirdparty_dir = "#thirdparty/misc/" thirdparty_sources = [ - # C sources - "base64.c", - "fastlz.c", - "sha256.c", - "smaz.c", - - # C++ sources - "aes256.cpp", - "hq2x.cpp", - "md5.cpp", - "pcg.cpp", - "triangulator.cpp", + # C sources + "base64.c", + "fastlz.c", + "sha256.c", + "smaz.c", + + # C++ sources + "aes256.cpp", + "hq2x.cpp", + "md5.cpp", + "pcg.cpp", + "triangulator.cpp", ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] env.add_source_files(env.core_sources, thirdparty_sources) @@ -74,9 +79,9 @@ env.add_source_files(env.core_sources, thirdparty_sources) # However, our version has some custom modifications, so it won't compile with the system one thirdparty_minizip_dir = "#thirdparty/minizip/" thirdparty_minizip_sources = [ - "ioapi.c", - "unzip.c", - "zip.c", + "ioapi.c", + "unzip.c", + "zip.c", ] thirdparty_minizip_sources = [thirdparty_minizip_dir + file for file in thirdparty_minizip_sources] env.add_source_files(env.core_sources, thirdparty_minizip_sources) @@ -90,9 +95,19 @@ env.add_source_files(env.core_sources, "*.cpp") # Make binders -import make_binders -env.Command(['method_bind.gen.inc', 'method_bind_ext.gen.inc'], 'make_binders.py', make_binders.run) +env.CommandNoCache(['method_bind.gen.inc', 'method_bind_ext.gen.inc'], 'make_binders.py', run_in_subprocess(make_binders.run)) + +# Authors +env.Depends('#core/authors.gen.h', "../AUTHORS.md") +env.CommandNoCache('#core/authors.gen.h', "../AUTHORS.md", run_in_subprocess(core_builders.make_authors_header)) + +# Donors +env.Depends('#core/donors.gen.h', "../DONORS.md") +env.CommandNoCache('#core/donors.gen.h', "../DONORS.md", run_in_subprocess(core_builders.make_donors_header)) +# License +env.Depends('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"]) +env.CommandNoCache('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"], run_in_subprocess(core_builders.make_license_header)) # Chain load SCsubs SConscript('os/SCsub') diff --git a/core/array.cpp b/core/array.cpp index 9e3250fd47..44c553e4eb 100644 --- a/core/array.cpp +++ b/core/array.cpp @@ -72,7 +72,7 @@ void Array::_unref() const { Variant &Array::operator[](int p_idx) { - return _p->array[p_idx]; + return _p->array.write[p_idx]; } const Variant &Array::operator[](int p_idx) const { @@ -259,7 +259,7 @@ Array &Array::sort_custom(Object *p_obj, const StringName &p_function) { ERR_FAIL_NULL_V(p_obj, *this); - SortArray<Variant, _ArrayVariantSortCustom> avs; + SortArray<Variant, _ArrayVariantSortCustom, true> avs; avs.compare.obj = p_obj; avs.compare.func = p_function; avs.sort(_p->array.ptrw(), _p->array.size()); diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index fa0921ae95..1c3f4fad01 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -221,6 +221,10 @@ String _OS::get_audio_driver_name(int p_driver) const { return OS::get_singleton()->get_audio_driver_name(p_driver); } +PoolStringArray _OS::get_connected_midi_inputs() { + return OS::get_singleton()->get_connected_midi_inputs(); +} + void _OS::set_video_mode(const Size2 &p_size, bool p_fullscreen, bool p_resizeable, int p_screen) { OS::VideoMode vm; @@ -348,6 +352,11 @@ bool _OS::get_borderless_window() const { return OS::get_singleton()->get_borderless_window(); } +void _OS::set_ime_active(const bool p_active) { + + return OS::get_singleton()->set_ime_active(p_active); +} + void _OS::set_ime_position(const Point2 &p_pos) { return OS::get_singleton()->set_ime_position(p_pos); @@ -399,7 +408,7 @@ Error _OS::shell_open(String p_uri) { int _OS::execute(const String &p_path, const Vector<String> &p_arguments, bool p_blocking, Array p_output) { - OS::ProcessID pid; + OS::ProcessID pid = -2; List<String> args; for (int i = 0; i < p_arguments.size(); i++) args.push_back(p_arguments[i]); @@ -412,6 +421,7 @@ int _OS::execute(const String &p_path, const Vector<String> &p_arguments, bool p else return pid; } + Error _OS::kill(int p_pid) { return OS::get_singleton()->kill(p_pid); @@ -793,6 +803,11 @@ uint32_t _OS::get_ticks_msec() const { return OS::get_singleton()->get_ticks_msec(); } +uint64_t _OS::get_ticks_usec() const { + + return OS::get_singleton()->get_ticks_usec(); +} + uint32_t _OS::get_splash_tick_msec() const { return OS::get_singleton()->get_splash_tick_msec(); @@ -1047,6 +1062,7 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_video_driver_name", "driver"), &_OS::get_video_driver_name); ClassDB::bind_method(D_METHOD("get_audio_driver_count"), &_OS::get_audio_driver_count); ClassDB::bind_method(D_METHOD("get_audio_driver_name", "driver"), &_OS::get_audio_driver_name); + ClassDB::bind_method(D_METHOD("get_connected_midi_inputs"), &_OS::get_connected_midi_inputs); ClassDB::bind_method(D_METHOD("get_screen_count"), &_OS::get_screen_count); ClassDB::bind_method(D_METHOD("get_current_screen"), &_OS::get_current_screen); @@ -1125,6 +1141,7 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("delay_usec", "usec"), &_OS::delay_usec); ClassDB::bind_method(D_METHOD("delay_msec", "msec"), &_OS::delay_msec); ClassDB::bind_method(D_METHOD("get_ticks_msec"), &_OS::get_ticks_msec); + ClassDB::bind_method(D_METHOD("get_ticks_usec"), &_OS::get_ticks_usec); ClassDB::bind_method(D_METHOD("get_splash_tick_msec"), &_OS::get_splash_tick_msec); ClassDB::bind_method(D_METHOD("get_locale"), &_OS::get_locale); ClassDB::bind_method(D_METHOD("get_latin_keyboard_variant"), &_OS::get_latin_keyboard_variant); @@ -2718,6 +2735,26 @@ Dictionary _Engine::get_version_info() const { return Engine::get_singleton()->get_version_info(); } +Dictionary _Engine::get_author_info() const { + return Engine::get_singleton()->get_author_info(); +} + +Array _Engine::get_copyright_info() const { + return Engine::get_singleton()->get_copyright_info(); +} + +Dictionary _Engine::get_donor_info() const { + return Engine::get_singleton()->get_donor_info(); +} + +Dictionary _Engine::get_license_info() const { + return Engine::get_singleton()->get_license_info(); +} + +String _Engine::get_license_text() const { + return Engine::get_singleton()->get_license_text(); +} + bool _Engine::is_in_physics_frame() const { return Engine::get_singleton()->is_in_physics_frame(); } @@ -2760,6 +2797,11 @@ void _Engine::_bind_methods() { ClassDB::bind_method(D_METHOD("get_main_loop"), &_Engine::get_main_loop); ClassDB::bind_method(D_METHOD("get_version_info"), &_Engine::get_version_info); + ClassDB::bind_method(D_METHOD("get_author_info"), &_Engine::get_author_info); + ClassDB::bind_method(D_METHOD("get_copyright_info"), &_Engine::get_copyright_info); + ClassDB::bind_method(D_METHOD("get_donor_info"), &_Engine::get_donor_info); + ClassDB::bind_method(D_METHOD("get_license_info"), &_Engine::get_license_info); + ClassDB::bind_method(D_METHOD("get_license_text"), &_Engine::get_license_text); ClassDB::bind_method(D_METHOD("is_in_physics_frame"), &_Engine::is_in_physics_frame); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 4bf4fd9146..18f84e0c04 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -152,6 +152,8 @@ public: virtual int get_audio_driver_count() const; virtual String get_audio_driver_name(int p_driver) const; + virtual PoolStringArray get_connected_midi_inputs(); + virtual int get_screen_count() const; virtual int get_current_screen() const; virtual void set_current_screen(int p_screen); @@ -183,6 +185,7 @@ public: virtual bool get_window_per_pixel_transparency_enabled() const; virtual void set_window_per_pixel_transparency_enabled(bool p_enabled); + virtual void set_ime_active(const bool p_active); virtual void set_ime_position(const Point2 &p_pos); Error native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track); @@ -276,6 +279,7 @@ public: void delay_usec(uint32_t p_usec) const; void delay_msec(uint32_t p_msec) const; uint32_t get_ticks_msec() const; + uint64_t get_ticks_usec() const; uint32_t get_splash_tick_msec() const; bool can_use_threads() const; @@ -690,6 +694,11 @@ public: MainLoop *get_main_loop() const; Dictionary get_version_info() const; + Dictionary get_author_info() const; + Array get_copyright_info() const; + Dictionary get_donor_info() const; + Dictionary get_license_info() const; + String get_license_text() const; bool is_in_physics_frame() const; diff --git a/core/class_db.cpp b/core/class_db.cpp index 59b100e282..03b214aa41 100644 --- a/core/class_db.cpp +++ b/core/class_db.cpp @@ -58,8 +58,8 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(2); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); return md; } @@ -68,9 +68,9 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(3); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); return md; } @@ -79,10 +79,10 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(4); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); return md; } @@ -91,11 +91,11 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(5); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); return md; } @@ -104,12 +104,12 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(6); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); return md; } @@ -118,13 +118,13 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(7); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); return md; } @@ -133,14 +133,14 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(8); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); return md; } @@ -149,15 +149,15 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(9); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); return md; } @@ -166,16 +166,16 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(10); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); - md.args[9] = StaticCString::create(p_arg10); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); + md.args.write[9] = StaticCString::create(p_arg10); return md; } @@ -184,17 +184,17 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(11); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); - md.args[9] = StaticCString::create(p_arg10); - md.args[10] = StaticCString::create(p_arg11); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); + md.args.write[9] = StaticCString::create(p_arg10); + md.args.write[10] = StaticCString::create(p_arg11); return md; } @@ -203,18 +203,18 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(12); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); - md.args[9] = StaticCString::create(p_arg10); - md.args[10] = StaticCString::create(p_arg11); - md.args[11] = StaticCString::create(p_arg12); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); + md.args.write[9] = StaticCString::create(p_arg10); + md.args.write[10] = StaticCString::create(p_arg11); + md.args.write[11] = StaticCString::create(p_arg12); return md; } @@ -223,19 +223,19 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(13); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); - md.args[9] = StaticCString::create(p_arg10); - md.args[10] = StaticCString::create(p_arg11); - md.args[11] = StaticCString::create(p_arg12); - md.args[12] = StaticCString::create(p_arg13); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); + md.args.write[9] = StaticCString::create(p_arg10); + md.args.write[10] = StaticCString::create(p_arg11); + md.args.write[11] = StaticCString::create(p_arg12); + md.args.write[12] = StaticCString::create(p_arg13); return md; } @@ -248,9 +248,9 @@ void ClassDB::set_current_api(APIType p_api) { current_api = p_api; } -HashMap<StringName, ClassDB::ClassInfo, StringNameHasher> ClassDB::classes; -HashMap<StringName, StringName, StringNameHasher> ClassDB::resource_base_extensions; -HashMap<StringName, StringName, StringNameHasher> ClassDB::compat_classes; +HashMap<StringName, ClassDB::ClassInfo> ClassDB::classes; +HashMap<StringName, StringName> ClassDB::resource_base_extensions; +HashMap<StringName, StringName> ClassDB::compat_classes; ClassDB::ClassInfo::ClassInfo() { @@ -1246,7 +1246,7 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const c defvals.resize(p_defcount); for (int i = 0; i < p_defcount; i++) { - defvals[i] = *p_defs[p_defcount - i - 1]; + defvals.write[i] = *p_defs[p_defcount - i - 1]; } p_bind->set_default_arguments(defvals); diff --git a/core/class_db.h b/core/class_db.h index 2c77ffe65f..f1d1879236 100644 --- a/core/class_db.h +++ b/core/class_db.h @@ -114,10 +114,10 @@ public: APIType api; ClassInfo *inherits_ptr; - HashMap<StringName, MethodBind *, StringNameHasher> method_map; - HashMap<StringName, int, StringNameHasher> constant_map; + HashMap<StringName, MethodBind *> method_map; + HashMap<StringName, int> constant_map; HashMap<StringName, List<StringName> > enum_map; - HashMap<StringName, MethodInfo, StringNameHasher> signal_map; + HashMap<StringName, MethodInfo> signal_map; List<PropertyInfo> property_list; #ifdef DEBUG_METHODS_ENABLED List<StringName> constant_order; @@ -126,7 +126,7 @@ public: List<MethodInfo> virtual_methods; StringName category; #endif - HashMap<StringName, PropertySetGet, StringNameHasher> property_setget; + HashMap<StringName, PropertySetGet> property_setget; StringName inherits; StringName name; @@ -143,9 +143,9 @@ public: } static RWLock *lock; - static HashMap<StringName, ClassInfo, StringNameHasher> classes; - static HashMap<StringName, StringName, StringNameHasher> resource_base_extensions; - static HashMap<StringName, StringName, StringNameHasher> compat_classes; + static HashMap<StringName, ClassInfo> classes; + static HashMap<StringName, StringName> resource_base_extensions; + static HashMap<StringName, StringName> compat_classes; #ifdef DEBUG_METHODS_ENABLED static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount); diff --git a/core/color.cpp b/core/color.cpp index b2f5889166..fcfcf20355 100644 --- a/core/color.cpp +++ b/core/color.cpp @@ -37,38 +37,78 @@ uint32_t Color::to_argb32() const { - uint32_t c = (uint8_t)(a * 255); + uint32_t c = (uint8_t)Math::round(a * 255); c <<= 8; - c |= (uint8_t)(r * 255); + c |= (uint8_t)Math::round(r * 255); c <<= 8; - c |= (uint8_t)(g * 255); + c |= (uint8_t)Math::round(g * 255); c <<= 8; - c |= (uint8_t)(b * 255); + c |= (uint8_t)Math::round(b * 255); return c; } uint32_t Color::to_abgr32() const { - uint32_t c = (uint8_t)(a * 255); + + uint32_t c = (uint8_t)Math::round(a * 255); c <<= 8; - c |= (uint8_t)(b * 255); + c |= (uint8_t)Math::round(b * 255); c <<= 8; - c |= (uint8_t)(g * 255); + c |= (uint8_t)Math::round(g * 255); c <<= 8; - c |= (uint8_t)(r * 255); + c |= (uint8_t)Math::round(r * 255); return c; } uint32_t Color::to_rgba32() const { - uint32_t c = (uint8_t)(r * 255); + uint32_t c = (uint8_t)Math::round(r * 255); c <<= 8; - c |= (uint8_t)(g * 255); + c |= (uint8_t)Math::round(g * 255); c <<= 8; - c |= (uint8_t)(b * 255); + c |= (uint8_t)Math::round(b * 255); c <<= 8; - c |= (uint8_t)(a * 255); + c |= (uint8_t)Math::round(a * 255); + + return c; +} + +uint64_t Color::to_abgr64() const { + + uint64_t c = (uint16_t)Math::round(a * 65535); + c <<= 16; + c |= (uint16_t)Math::round(b * 65535); + c <<= 16; + c |= (uint16_t)Math::round(g * 65535); + c <<= 16; + c |= (uint16_t)Math::round(r * 65535); + + return c; +} + +uint64_t Color::to_argb64() const { + + uint64_t c = (uint16_t)Math::round(a * 65535); + c <<= 16; + c |= (uint16_t)Math::round(r * 65535); + c <<= 16; + c |= (uint16_t)Math::round(g * 65535); + c <<= 16; + c |= (uint16_t)Math::round(b * 65535); + + return c; +} + +uint64_t Color::to_rgba64() const { + + uint64_t c = (uint16_t)Math::round(r * 65535); + c <<= 16; + c |= (uint16_t)Math::round(g * 65535); + c <<= 16; + c |= (uint16_t)Math::round(b * 65535); + c <<= 16; + c |= (uint16_t)Math::round(a * 65535); return c; } @@ -200,6 +240,19 @@ Color Color::hex(uint32_t p_hex) { return Color(r, g, b, a); } +Color Color::hex64(uint64_t p_hex) { + + float a = (p_hex & 0xFFFF) / 65535.0; + p_hex >>= 16; + float b = (p_hex & 0xFFFF) / 65535.0; + p_hex >>= 16; + float g = (p_hex & 0xFFFF) / 65535.0; + p_hex >>= 16; + float r = (p_hex & 0xFFFF) / 65535.0; + + return Color(r, g, b, a); +} + static float _parse_col(const String &p_str, int p_ofs) { int ig = 0; @@ -368,7 +421,7 @@ Color Color::named(const String &p_name) { String _to_hex(float p_val) { - int v = p_val * 255; + int v = Math::round(p_val * 255); v = CLAMP(v, 0, 255); String ret; diff --git a/core/color.h b/core/color.h index a2015a34d6..c0516e55fe 100644 --- a/core/color.h +++ b/core/color.h @@ -55,6 +55,9 @@ struct Color { uint32_t to_rgba32() const; uint32_t to_argb32() const; uint32_t to_abgr32() const; + uint64_t to_rgba64() const; + uint64_t to_argb64() const; + uint64_t to_abgr64() const; float gray() const; float get_h() const; float get_s() const; @@ -186,6 +189,7 @@ struct Color { } static Color hex(uint32_t p_hex); + static Color hex64(uint64_t p_hex); static Color html(const String &p_color); static bool html_is_valid(const String &p_color); static Color named(const String &p_name); diff --git a/core/color_names.inc b/core/color_names.inc index b05684acc6..3ae42648d0 100644 --- a/core/color_names.inc +++ b/core/color_names.inc @@ -3,7 +3,7 @@ static Map<String, Color> _named_colors; static void _populate_named_colors() { - if(!_named_colors.empty()) return; + if (!_named_colors.empty()) return; _named_colors.insert("aliceblue", Color(0.94, 0.97, 1.00)); _named_colors.insert("antiquewhite", Color(0.98, 0.92, 0.84)); _named_colors.insert("aqua", Color(0.00, 1.00, 1.00)); diff --git a/core/command_queue_mt.h b/core/command_queue_mt.h index 3942b961d3..7978eaa7bf 100644 --- a/core/command_queue_mt.h +++ b/core/command_queue_mt.h @@ -54,9 +54,13 @@ #define _COMMA_10 , #define _COMMA_11 , #define _COMMA_12 , +#define _COMMA_13 , // 1-based comma separated list of ITEMs #define COMMA_SEP_LIST(ITEM, LENGTH) _COMMA_SEP_LIST_##LENGTH(ITEM) +#define _COMMA_SEP_LIST_13(ITEM) \ + _COMMA_SEP_LIST_12(ITEM) \ + , ITEM(13) #define _COMMA_SEP_LIST_12(ITEM) \ _COMMA_SEP_LIST_11(ITEM) \ , ITEM(12) @@ -97,6 +101,9 @@ // 1-based semicolon separated list of ITEMs #define SEMIC_SEP_LIST(ITEM, LENGTH) _SEMIC_SEP_LIST_##LENGTH(ITEM) +#define _SEMIC_SEP_LIST_13(ITEM) \ + _SEMIC_SEP_LIST_12(ITEM); \ + ITEM(13) #define _SEMIC_SEP_LIST_12(ITEM) \ _SEMIC_SEP_LIST_11(ITEM); \ ITEM(12) @@ -137,6 +144,9 @@ // 1-based space separated list of ITEMs #define SPACE_SEP_LIST(ITEM, LENGTH) _SPACE_SEP_LIST_##LENGTH(ITEM) +#define _SPACE_SEP_LIST_13(ITEM) \ + _SPACE_SEP_LIST_12(ITEM) \ + ITEM(13) #define _SPACE_SEP_LIST_12(ITEM) \ _SPACE_SEP_LIST_11(ITEM) \ ITEM(12) @@ -262,7 +272,7 @@ ss->sem->wait(); \ } -#define MAX_CMD_PARAMS 12 +#define MAX_CMD_PARAMS 13 class CommandQueueMT { @@ -290,15 +300,15 @@ class CommandQueueMT { }; DECL_CMD(0) - SPACE_SEP_LIST(DECL_CMD, 12) + SPACE_SEP_LIST(DECL_CMD, 13) /* comands that return */ DECL_CMD_RET(0) - SPACE_SEP_LIST(DECL_CMD_RET, 12) + SPACE_SEP_LIST(DECL_CMD_RET, 13) /* commands that don't return but sync */ DECL_CMD_SYNC(0) - SPACE_SEP_LIST(DECL_CMD_SYNC, 12) + SPACE_SEP_LIST(DECL_CMD_SYNC, 13) /***** BASE *******/ @@ -432,15 +442,15 @@ class CommandQueueMT { public: /* NORMAL PUSH COMMANDS */ DECL_PUSH(0) - SPACE_SEP_LIST(DECL_PUSH, 12) + SPACE_SEP_LIST(DECL_PUSH, 13) /* PUSH AND RET COMMANDS */ DECL_PUSH_AND_RET(0) - SPACE_SEP_LIST(DECL_PUSH_AND_RET, 12) + SPACE_SEP_LIST(DECL_PUSH_AND_RET, 13) /* PUSH AND RET SYNC COMMANDS*/ DECL_PUSH_AND_SYNC(0) - SPACE_SEP_LIST(DECL_PUSH_AND_SYNC, 12) + SPACE_SEP_LIST(DECL_PUSH_AND_SYNC, 13) void wait_and_flush_one() { ERR_FAIL_COND(!sync); diff --git a/core/compressed_translation.cpp b/core/compressed_translation.cpp index 266d793af7..5c1fd26e2a 100644 --- a/core/compressed_translation.cpp +++ b/core/compressed_translation.cpp @@ -73,7 +73,7 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { Pair<int, CharString> p; p.first = idx; p.second = cs; - buckets[h % size].push_back(p); + buckets.write[h % size].push_back(p); //compress string CharString src_s = p_from->get_message(E->get()).operator String().utf8(); @@ -100,7 +100,7 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { ps.compressed[0] = 0; } - compressed[idx] = ps; + compressed.write[idx] = ps; total_compression_size += ps.compressed.size(); total_string_size += src_s.size(); idx++; @@ -111,8 +111,8 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { for (int i = 0; i < size; i++) { - Vector<Pair<int, CharString> > &b = buckets[i]; - Map<uint32_t, int> &t = table[i]; + const Vector<Pair<int, CharString> > &b = buckets[i]; + Map<uint32_t, int> &t = table.write[i]; if (b.size() == 0) continue; @@ -136,7 +136,7 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { } } - hfunc_table[i] = d; + hfunc_table.write[i] = d; bucket_table_size += 2 + b.size() * 4; } @@ -157,7 +157,7 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { for (int i = 0; i < size; i++) { - Map<uint32_t, int> &t = table[i]; + const Map<uint32_t, int> &t = table[i]; if (t.size() == 0) { htw[i] = 0xFFFFFFFF; //nothing continue; diff --git a/core/core_builders.py b/core/core_builders.py new file mode 100644 index 0000000000..90e505aab9 --- /dev/null +++ b/core/core_builders.py @@ -0,0 +1,236 @@ +"""Functions used to generate source files during build time + +All such functions are invoked in a subprocess on Windows to prevent build flakiness. + +""" +from platform_methods import subprocess_main +from compat import iteritems, itervalues, open_utf8, escape_string + + +def make_authors_header(target, source, env): + sections = ["Project Founders", "Lead Developer", "Project Manager", "Developers"] + sections_id = ["AUTHORS_FOUNDERS", "AUTHORS_LEAD_DEVELOPERS", "AUTHORS_PROJECT_MANAGERS", "AUTHORS_DEVELOPERS"] + + src = source[0] + dst = target[0] + f = open_utf8(src, "r") + g = open_utf8(dst, "w") + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _EDITOR_AUTHORS_H\n") + g.write("#define _EDITOR_AUTHORS_H\n") + + reading = False + + def close_section(): + g.write("\t0\n") + g.write("};\n") + + for line in f: + if reading: + if line.startswith(" "): + g.write("\t\"" + escape_string(line.strip()) + "\",\n") + continue + if line.startswith("## "): + if reading: + close_section() + reading = False + for section, section_id in zip(sections, sections_id): + if line.strip().endswith(section): + current_section = escape_string(section_id) + reading = True + g.write("const char *const " + current_section + "[] = {\n") + break + + if reading: + close_section() + + g.write("#endif\n") + + g.close() + f.close() + + +def make_donors_header(target, source, env): + sections = ["Platinum sponsors", "Gold sponsors", "Mini sponsors", + "Gold donors", "Silver donors", "Bronze donors"] + sections_id = ["DONORS_SPONSOR_PLAT", "DONORS_SPONSOR_GOLD", "DONORS_SPONSOR_MINI", + "DONORS_GOLD", "DONORS_SILVER", "DONORS_BRONZE"] + + src = source[0] + dst = target[0] + f = open_utf8(src, "r") + g = open_utf8(dst, "w") + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _EDITOR_DONORS_H\n") + g.write("#define _EDITOR_DONORS_H\n") + + reading = False + + def close_section(): + g.write("\t0\n") + g.write("};\n") + + for line in f: + if reading >= 0: + if line.startswith(" "): + g.write("\t\"" + escape_string(line.strip()) + "\",\n") + continue + if line.startswith("## "): + if reading: + close_section() + reading = False + for section, section_id in zip(sections, sections_id): + if line.strip().endswith(section): + current_section = escape_string(section_id) + reading = True + g.write("const char *const " + current_section + "[] = {\n") + break + + if reading: + close_section() + + g.write("#endif\n") + + g.close() + f.close() + + +def make_license_header(target, source, env): + src_copyright = source[0] + src_license = source[1] + dst = target[0] + + class LicenseReader: + def __init__(self, license_file): + self._license_file = license_file + self.line_num = 0 + self.current = self.next_line() + + def next_line(self): + line = self._license_file.readline() + self.line_num += 1 + while line.startswith("#"): + line = self._license_file.readline() + self.line_num += 1 + self.current = line + return line + + def next_tag(self): + if not ':' in self.current: + return ('', []) + tag, line = self.current.split(":", 1) + lines = [line.strip()] + while self.next_line() and self.current.startswith(" "): + lines.append(self.current.strip()) + return (tag, lines) + + from collections import OrderedDict + projects = OrderedDict() + license_list = [] + + with open_utf8(src_copyright, "r") as copyright_file: + reader = LicenseReader(copyright_file) + part = {} + while reader.current: + tag, content = reader.next_tag() + if tag in ("Files", "Copyright", "License"): + part[tag] = content[:] + elif tag == "Comment": + # attach part to named project + projects[content[0]] = projects.get(content[0], []) + [part] + + if not tag or not reader.current: + # end of a paragraph start a new part + if "License" in part and not "Files" in part: + # no Files tag in this one, so assume standalone license + license_list.append(part["License"]) + part = {} + reader.next_line() + + data_list = [] + for project in itervalues(projects): + for part in project: + part["file_index"] = len(data_list) + data_list += part["Files"] + part["copyright_index"] = len(data_list) + data_list += part["Copyright"] + + with open_utf8(dst, "w") as f: + + f.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + f.write("#ifndef _EDITOR_LICENSE_H\n") + f.write("#define _EDITOR_LICENSE_H\n") + f.write("const char *const GODOT_LICENSE_TEXT =") + + with open_utf8(src_license, "r") as license_file: + for line in license_file: + escaped_string = escape_string(line.strip()) + f.write("\n\t\t\"" + escaped_string + "\\n\"") + f.write(";\n\n") + + f.write("struct ComponentCopyrightPart {\n" + "\tconst char *license;\n" + "\tconst char *const *files;\n" + "\tconst char *const *copyright_statements;\n" + "\tint file_count;\n" + "\tint copyright_count;\n" + "};\n\n") + + f.write("struct ComponentCopyright {\n" + "\tconst char *name;\n" + "\tconst ComponentCopyrightPart *parts;\n" + "\tint part_count;\n" + "};\n\n") + + f.write("const char *const COPYRIGHT_INFO_DATA[] = {\n") + for line in data_list: + f.write("\t\"" + escape_string(line) + "\",\n") + f.write("};\n\n") + + f.write("const ComponentCopyrightPart COPYRIGHT_PROJECT_PARTS[] = {\n") + part_index = 0 + part_indexes = {} + for project_name, project in iteritems(projects): + part_indexes[project_name] = part_index + for part in project: + f.write("\t{ \"" + escape_string(part["License"][0]) + "\", " + + "©RIGHT_INFO_DATA[" + str(part["file_index"]) + "], " + + "©RIGHT_INFO_DATA[" + str(part["copyright_index"]) + "], " + + str(len(part["Files"])) + ", " + + str(len(part["Copyright"])) + " },\n") + part_index += 1 + f.write("};\n\n") + + f.write("const int COPYRIGHT_INFO_COUNT = " + str(len(projects)) + ";\n") + + f.write("const ComponentCopyright COPYRIGHT_INFO[] = {\n") + for project_name, project in iteritems(projects): + f.write("\t{ \"" + escape_string(project_name) + "\", " + + "©RIGHT_PROJECT_PARTS[" + str(part_indexes[project_name]) + "], " + + str(len(project)) + " },\n") + f.write("};\n\n") + + f.write("const int LICENSE_COUNT = " + str(len(license_list)) + ";\n") + + f.write("const char *const LICENSE_NAMES[] = {\n") + for l in license_list: + f.write("\t\"" + escape_string(l[0]) + "\",\n") + f.write("};\n\n") + + f.write("const char *const LICENSE_BODIES[] = {\n\n") + for l in license_list: + for line in l[1:]: + if line == ".": + f.write("\t\"\\n\"\n") + else: + f.write("\t\"" + escape_string(line) + "\\n\"\n") + f.write("\t\"\",\n\n") + f.write("};\n\n") + + f.write("#endif\n") + + +if __name__ == '__main__': + subprocess_main(globals()) diff --git a/core/cowdata.h b/core/cowdata.h new file mode 100644 index 0000000000..6a8f644d53 --- /dev/null +++ b/core/cowdata.h @@ -0,0 +1,338 @@ +/*************************************************************************/ +/* cowdata.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef COWDATA_H_ +#define COWDATA_H_ + +#include "os/memory.h" +#include "safe_refcount.h" + +template <class T> +class Vector; +class String; +class CharString; +template <class T, class V> +class VMap; + +template <class T> +class CowData { + template <class TV> + friend class Vector; + friend class String; + friend class CharString; + template <class TV, class VV> + friend class VMap; + +private: + mutable T *_ptr; + + // internal helpers + + _FORCE_INLINE_ uint32_t *_get_refcount() const { + + if (!_ptr) + return NULL; + + return reinterpret_cast<uint32_t *>(_ptr) - 2; + } + + _FORCE_INLINE_ uint32_t *_get_size() const { + + if (!_ptr) + return NULL; + + return reinterpret_cast<uint32_t *>(_ptr) - 1; + } + + _FORCE_INLINE_ T *_get_data() const { + + if (!_ptr) + return NULL; + return reinterpret_cast<T *>(_ptr); + } + + _FORCE_INLINE_ size_t _get_alloc_size(size_t p_elements) const { + //return nearest_power_of_2_templated(p_elements*sizeof(T)+sizeof(SafeRefCount)+sizeof(int)); + return next_power_of_2(p_elements * sizeof(T)); + } + + _FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const { +#if defined(_add_overflow) && defined(_mul_overflow) + size_t o; + size_t p; + if (_mul_overflow(p_elements, sizeof(T), &o)) return false; + *out = next_power_of_2(o); + if (_add_overflow(o, static_cast<size_t>(32), &p)) return false; //no longer allocated here + return true; +#else + // Speed is more important than correctness here, do the operations unchecked + // and hope the best + *out = _get_alloc_size(p_elements); + return true; +#endif + } + + void _unref(void *p_data); + void _ref(const CowData *p_from); + void _ref(const CowData &p_from); + void _copy_on_write(); + +public: + void operator=(const CowData<T> &p_from) { _ref(p_from); } + + _FORCE_INLINE_ T *ptrw() { + _copy_on_write(); + return (T *)_get_data(); + } + + _FORCE_INLINE_ const T *ptr() const { + return _get_data(); + } + + _FORCE_INLINE_ int size() const { + uint32_t *size = (uint32_t *)_get_size(); + if (size) + return *size; + else + return 0; + } + + _FORCE_INLINE_ void clear() { resize(0); } + _FORCE_INLINE_ bool empty() const { return _ptr == 0; } + + _FORCE_INLINE_ void set(int p_index, const T &p_elem) { + + CRASH_BAD_INDEX(p_index, size()); + _copy_on_write(); + _get_data()[p_index] = p_elem; + } + + _FORCE_INLINE_ T &get_m(int p_index) { + + CRASH_BAD_INDEX(p_index, size()); + _copy_on_write(); + return _get_data()[p_index]; + } + + _FORCE_INLINE_ const T &get(int p_index) const { + + CRASH_BAD_INDEX(p_index, size()); + + return _get_data()[p_index]; + } + + Error resize(int p_size); + + _FORCE_INLINE_ void remove(int p_index) { + + ERR_FAIL_INDEX(p_index, size()); + T *p = ptrw(); + int len = size(); + for (int i = p_index; i < len - 1; i++) { + + p[i] = p[i + 1]; + }; + + resize(len - 1); + }; + + Error insert(int p_pos, const T &p_val) { + + ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER); + resize(size() + 1); + for (int i = (size() - 1); i > p_pos; i--) + set(i, get(i - 1)); + set(p_pos, p_val); + + return OK; + }; + + _FORCE_INLINE_ CowData(); + _FORCE_INLINE_ ~CowData(); + _FORCE_INLINE_ CowData(CowData<T> &p_from) { _ref(p_from); }; +}; + +template <class T> +void CowData<T>::_unref(void *p_data) { + + if (!p_data) + return; + + uint32_t *refc = _get_refcount(); + + if (atomic_decrement(refc) > 0) + return; // still in use + // clean up + + uint32_t *count = _get_size(); + T *data = (T *)(count + 1); + + for (uint32_t i = 0; i < *count; ++i) { + // call destructors + data[i].~T(); + } + + // free mem + Memory::free_static((uint8_t *)p_data, true); +} + +template <class T> +void CowData<T>::_copy_on_write() { + + if (!_ptr) + return; + + uint32_t *refc = _get_refcount(); + + if (unlikely(*refc > 1)) { + /* in use by more than me */ + uint32_t current_size = *_get_size(); + + uint32_t *mem_new = (uint32_t *)Memory::alloc_static(_get_alloc_size(current_size), true); + + *(mem_new - 2) = 1; //refcount + *(mem_new - 1) = current_size; //size + + T *_data = (T *)(mem_new); + + // initialize new elements + for (uint32_t i = 0; i < current_size; i++) { + + memnew_placement(&_data[i], T(_get_data()[i])); + } + + _unref(_ptr); + _ptr = _data; + } +} + +template <class T> +Error CowData<T>::resize(int p_size) { + + ERR_FAIL_COND_V(p_size < 0, ERR_INVALID_PARAMETER); + + if (p_size == size()) + return OK; + + if (p_size == 0) { + // wants to clean up + _unref(_ptr); + _ptr = NULL; + return OK; + } + + // possibly changing size, copy on write + _copy_on_write(); + + size_t alloc_size; + ERR_FAIL_COND_V(!_get_alloc_size_checked(p_size, &alloc_size), ERR_OUT_OF_MEMORY); + + if (p_size > size()) { + + if (size() == 0) { + // alloc from scratch + uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true); + ERR_FAIL_COND_V(!ptr, ERR_OUT_OF_MEMORY); + *(ptr - 1) = 0; //size, currently none + *(ptr - 2) = 1; //refcount + + _ptr = (T *)ptr; + + } else { + void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true); + ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); + _ptr = (T *)(_ptrnew); + } + + // construct the newly created elements + T *elems = _get_data(); + + for (int i = *_get_size(); i < p_size; i++) { + + memnew_placement(&elems[i], T); + } + + *_get_size() = p_size; + + } else if (p_size < size()) { + + // deinitialize no longer needed elements + for (uint32_t i = p_size; i < *_get_size(); i++) { + + T *t = &_get_data()[i]; + t->~T(); + } + + void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true); + ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); + + _ptr = (T *)(_ptrnew); + + *_get_size() = p_size; + } + + return OK; +} + +template <class T> +void CowData<T>::_ref(const CowData *p_from) { + _ref(*p_from); +} + +template <class T> +void CowData<T>::_ref(const CowData &p_from) { + + if (_ptr == p_from._ptr) + return; // self assign, do nothing. + + _unref(_ptr); + _ptr = NULL; + + if (!p_from._ptr) + return; //nothing to do + + if (atomic_conditional_increment(p_from._get_refcount()) > 0) { // could reference + _ptr = p_from._ptr; + } +} + +template <class T> +CowData<T>::CowData() { + + _ptr = NULL; +} + +template <class T> +CowData<T>::~CowData() { + + _unref(_ptr); +} + +#endif /* COW_H_ */ diff --git a/core/dictionary.cpp b/core/dictionary.cpp index ba0de95861..42d9eab310 100644 --- a/core/dictionary.cpp +++ b/core/dictionary.cpp @@ -50,6 +50,32 @@ void Dictionary::get_key_list(List<Variant> *p_keys) const { } } +Variant Dictionary::get_key_at_index(int p_index) const { + + int index = 0; + for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { + if (index == p_index) { + return E.key(); + } + index++; + } + + return Variant(); +} + +Variant Dictionary::get_value_at_index(int p_index) const { + + int index = 0; + for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { + if (index == p_index) { + return E.value(); + } + index++; + } + + return Variant(); +} + Variant &Dictionary::operator[](const Variant &p_key) { return _p->variant_map[p_key]; @@ -114,6 +140,11 @@ void Dictionary::erase(const Variant &p_key) { _p->variant_map.erase(p_key); } +bool Dictionary::erase_checked(const Variant &p_key) { + + return _p->variant_map.erase(p_key); +} + bool Dictionary::operator==(const Dictionary &p_dictionary) const { return _p == p_dictionary._p; diff --git a/core/dictionary.h b/core/dictionary.h index 9eef265d5b..00ec67fb99 100644 --- a/core/dictionary.h +++ b/core/dictionary.h @@ -47,6 +47,8 @@ class Dictionary { public: void get_key_list(List<Variant> *p_keys) const; + Variant get_key_at_index(int p_index) const; + Variant get_value_at_index(int p_index) const; Variant &operator[](const Variant &p_key); const Variant &operator[](const Variant &p_key) const; @@ -64,6 +66,7 @@ public: bool has_all(const Array &p_keys) const; void erase(const Variant &p_key); + bool erase_checked(const Variant &p_key); bool operator==(const Dictionary &p_dictionary) const; diff --git a/core/dvector.h b/core/dvector.h index c0190fb9e3..e03a755e6c 100644 --- a/core/dvector.h +++ b/core/dvector.h @@ -150,7 +150,7 @@ class PoolVector { } if (old_alloc->refcount.unref() == true) { - //this should never happen but.. + //this should never happen but.. #ifdef DEBUG_ENABLED MemoryPool::alloc_mutex->lock(); diff --git a/core/engine.cpp b/core/engine.cpp index b2c34a853c..7c8024b946 100644 --- a/core/engine.cpp +++ b/core/engine.cpp @@ -30,6 +30,9 @@ #include "engine.h" +#include "authors.gen.h" +#include "donors.gen.h" +#include "license.gen.h" #include "version.h" #include "version_hash.gen.h" @@ -111,6 +114,78 @@ Dictionary Engine::get_version_info() const { return dict; } +static Array array_from_info(const char *const *info_list) { + Array arr; + for (int i = 0; info_list[i] != NULL; i++) { + arr.push_back(info_list[i]); + } + return arr; +} + +static Array array_from_info_count(const char *const *info_list, int info_count) { + Array arr; + for (int i = 0; i < info_count; i++) { + arr.push_back(info_list[i]); + } + return arr; +} + +Dictionary Engine::get_author_info() const { + Dictionary dict; + + dict["lead_developers"] = array_from_info(AUTHORS_LEAD_DEVELOPERS); + dict["project_managers"] = array_from_info(AUTHORS_PROJECT_MANAGERS); + dict["founders"] = array_from_info(AUTHORS_FOUNDERS); + dict["developers"] = array_from_info(AUTHORS_DEVELOPERS); + + return dict; +} + +Array Engine::get_copyright_info() const { + Array components; + for (int component_index = 0; component_index < COPYRIGHT_INFO_COUNT; component_index++) { + const ComponentCopyright &cp_info = COPYRIGHT_INFO[component_index]; + Dictionary component_dict; + component_dict["name"] = cp_info.name; + Array parts; + for (int i = 0; i < cp_info.part_count; i++) { + const ComponentCopyrightPart &cp_part = cp_info.parts[i]; + Dictionary part_dict; + part_dict["files"] = array_from_info_count(cp_part.files, cp_part.file_count); + part_dict["copyright"] = array_from_info_count(cp_part.copyright_statements, cp_part.copyright_count); + part_dict["license"] = cp_part.license; + parts.push_back(part_dict); + } + component_dict["parts"] = parts; + + components.push_back(component_dict); + } + return components; +} + +Dictionary Engine::get_donor_info() const { + Dictionary donors; + donors["platinum_sponsors"] = array_from_info(DONORS_SPONSOR_PLAT); + donors["gold_sponsors"] = array_from_info(DONORS_SPONSOR_GOLD); + donors["mini_sponsors"] = array_from_info(DONORS_SPONSOR_MINI); + donors["gold_donors"] = array_from_info(DONORS_GOLD); + donors["silver_donors"] = array_from_info(DONORS_SILVER); + donors["bronze_donors"] = array_from_info(DONORS_BRONZE); + return donors; +} + +Dictionary Engine::get_license_info() const { + Dictionary licenses; + for (int i = 0; i < LICENSE_COUNT; i++) { + licenses[LICENSE_NAMES[i]] = LICENSE_BODIES[i]; + } + return licenses; +} + +String Engine::get_license_text() const { + return String(GODOT_LICENSE_TEXT); +} + void Engine::add_singleton(const Singleton &p_singleton) { singletons.push_back(p_singleton); diff --git a/core/engine.h b/core/engine.h index 665992699a..031ba29cd6 100644 --- a/core/engine.h +++ b/core/engine.h @@ -118,6 +118,11 @@ public: #endif Dictionary get_version_info() const; + Dictionary get_author_info() const; + Array get_copyright_info() const; + Dictionary get_donor_info() const; + Dictionary get_license_info() const; + String get_license_text() const; Engine(); }; diff --git a/core/error_macros.h b/core/error_macros.h index 168b2e06fe..bee738ceea 100644 --- a/core/error_macros.h +++ b/core/error_macros.h @@ -311,14 +311,14 @@ extern bool _err_error_exists; _err_error_exists = false; \ } -#define WARN_DEPRECATED \ - { \ - static bool warning_shown=false;\ - if (!warning_shown) {\ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__,"This method has been deprecated and will be removed in the future", ERR_HANDLER_WARNING); \ - _err_error_exists = false; \ - warning_shown=true;\ - }\ +#define WARN_DEPRECATED \ + { \ + static volatile bool warning_shown = false; \ + if (!warning_shown) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future", ERR_HANDLER_WARNING); \ + _err_error_exists = false; \ + warning_shown = true; \ + } \ } #endif diff --git a/core/global_constants.cpp b/core/global_constants.cpp index 04810afe73..187813f9d0 100644 --- a/core/global_constants.cpp +++ b/core/global_constants.cpp @@ -89,6 +89,7 @@ VARIANT_ENUM_CAST(KeyList); VARIANT_ENUM_CAST(KeyModifierMask); VARIANT_ENUM_CAST(ButtonList); VARIANT_ENUM_CAST(JoystickList); +VARIANT_ENUM_CAST(MidiMessageList); void register_global_constants() { @@ -378,6 +379,8 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(BUTTON_LEFT); BIND_GLOBAL_ENUM_CONSTANT(BUTTON_RIGHT); BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MIDDLE); + BIND_GLOBAL_ENUM_CONSTANT(BUTTON_XBUTTON1); + BIND_GLOBAL_ENUM_CONSTANT(BUTTON_XBUTTON2); BIND_GLOBAL_ENUM_CONSTANT(BUTTON_WHEEL_UP); BIND_GLOBAL_ENUM_CONSTANT(BUTTON_WHEEL_DOWN); BIND_GLOBAL_ENUM_CONSTANT(BUTTON_WHEEL_LEFT); @@ -385,6 +388,8 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_LEFT); BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_RIGHT); BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_MIDDLE); + BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_XBUTTON1); + BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_XBUTTON2); //joypads BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_0); @@ -454,6 +459,15 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_L2); BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_R2); + // midi + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_OFF); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_ON); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_AFTERTOUCH); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_CONTROL_CHANGE); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_PROGRAM_CHANGE); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_CHANNEL_PRESSURE); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_PITCH_BEND); + // error list BIND_GLOBAL_ENUM_CONSTANT(OK); diff --git a/core/hashfuncs.h b/core/hashfuncs.h index ae99fa39c8..735e679d1e 100644 --- a/core/hashfuncs.h +++ b/core/hashfuncs.h @@ -33,6 +33,8 @@ #include "math_defs.h" #include "math_funcs.h" +#include "node_path.h" +#include "string_db.h" #include "typedefs.h" #include "ustring.h" @@ -131,6 +133,7 @@ static inline uint64_t make_uint64_t(T p_in) { } struct HashMapHasherDefault { + static _FORCE_INLINE_ uint32_t hash(const String &p_string) { return p_string.hash(); } static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); } static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); } @@ -145,6 +148,10 @@ struct HashMapHasherDefault { static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return p_int; } static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return (uint32_t)p_int; } static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return (uint32_t)p_wchar; } + + static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); } + static _FORCE_INLINE_ uint32_t hash(const NodePath &p_path) { return p_path.hash(); } + //static _FORCE_INLINE_ uint32_t hash(const void* p_ptr) { return uint32_t(uint64_t(p_ptr))*(0x9e3779b1L); } }; diff --git a/core/image.cpp b/core/image.cpp index 51fbe75dec..65905c83e8 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -33,6 +33,7 @@ #include "core/io/image_loader.h" #include "core/os/copymem.h" #include "hash_map.h" +#include "math_funcs.h" #include "print_string.h" #include "thirdparty/misc/hq2x.h" @@ -525,7 +526,7 @@ static double _bicubic_interp_kernel(double x) { } template <int CC> -static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { +static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { // get source image size int width = p_src_width; @@ -555,7 +556,7 @@ static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_wi // initial pixel value - uint8_t *dst = p_dst + (y * p_dst_width + x) * CC; + uint8_t *__restrict dst = p_dst + (y * p_dst_width + x) * CC; double color[CC]; for (int i = 0; i < CC; i++) { @@ -583,7 +584,7 @@ static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_wi ox2 = xmax; // get pixel of original image - const uint8_t *p = p_src + (oy2 * p_src_width + ox2) * CC; + const uint8_t *__restrict p = p_src + (oy2 * p_src_width + ox2) * CC; for (int i = 0; i < CC; i++) { @@ -600,7 +601,7 @@ static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_wi } template <int CC> -static void _scale_bilinear(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { +static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { enum { FRAC_BITS = 8, @@ -655,7 +656,7 @@ static void _scale_bilinear(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src } template <int CC> -static void _scale_nearest(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { +static void _scale_nearest(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { for (uint32_t i = 0; i < p_dst_height; i++) { @@ -676,6 +677,16 @@ static void _scale_nearest(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_ } } +static void _overlay(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, float p_alpha, uint32_t p_width, uint32_t p_height, uint32_t p_pixel_size) { + + uint16_t alpha = CLAMP((uint16_t)(p_alpha * 256.0f), 0, 256); + + for (uint32_t i = 0; i < p_width * p_height * p_pixel_size; i++) { + + p_dst[i] = (p_dst[i] * (256 - alpha) + p_src[i] * alpha) >> 8; + } +} + void Image::resize_to_po2(bool p_square) { if (!_can_modify(format)) { @@ -707,6 +718,8 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { ERR_FAIL(); } + bool mipmap_aware = p_interpolation == INTERPOLATE_TRILINEAR /* || p_interpolation == INTERPOLATE_TRICUBIC */; + ERR_FAIL_COND(p_width <= 0); ERR_FAIL_COND(p_height <= 0); ERR_FAIL_COND(p_width > MAX_WIDTH); @@ -717,6 +730,32 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { Image dst(p_width, p_height, 0, format); + // Setup mipmap-aware scaling + Image dst2; + int mip1; + int mip2; + float mip1_weight; + if (mipmap_aware) { + float avg_scale = ((float)p_width / width + (float)p_height / height) * 0.5f; + if (avg_scale >= 1.0f) { + mipmap_aware = false; + } else { + float level = Math::log(1.0f / avg_scale) / Math::log(2.0f); + mip1 = CLAMP((int)Math::floor(level), 0, get_mipmap_count()); + mip2 = CLAMP((int)Math::ceil(level), 0, get_mipmap_count()); + mip1_weight = 1.0f - (level - mip1); + } + } + bool interpolate_mipmaps = mipmap_aware && mip1 != mip2; + if (interpolate_mipmaps) { + dst2.create(p_width, p_height, 0, format); + } + bool had_mipmaps = mipmaps; + if (interpolate_mipmaps && !had_mipmaps) { + generate_mipmaps(); + } + // -- + PoolVector<uint8_t>::Read r = data.read(); const unsigned char *r_ptr = r.ptr(); @@ -734,13 +773,57 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { case 4: _scale_nearest<4>(r_ptr, w_ptr, width, height, p_width, p_height); break; } } break; - case INTERPOLATE_BILINEAR: { + case INTERPOLATE_BILINEAR: + case INTERPOLATE_TRILINEAR: { + + for (int i = 0; i < 2; ++i) { + int src_width; + int src_height; + const unsigned char *src_ptr; + + if (!mipmap_aware) { + if (i == 0) { + // Standard behavior + src_width = width; + src_height = height; + src_ptr = r_ptr; + } else { + // No need for a second iteration + break; + } + } else { + if (i == 0) { + // Read from the first mipmap that will be interpolated + // (if both levels are the same, we will not interpolate, but at least we'll sample from the right level) + int offs; + _get_mipmap_offset_and_size(mip1, offs, src_width, src_height); + src_ptr = r_ptr + offs; + } else if (!interpolate_mipmaps) { + // No need generate a second image + break; + } else { + // Switch to read from the second mipmap that will be interpolated + int offs; + _get_mipmap_offset_and_size(mip2, offs, src_width, src_height); + src_ptr = r_ptr + offs; + // Switch to write to the second destination image + w = dst2.data.write(); + w_ptr = w.ptr(); + } + } - switch (get_format_pixel_size(format)) { - case 1: _scale_bilinear<1>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 2: _scale_bilinear<2>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 3: _scale_bilinear<3>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 4: _scale_bilinear<4>(r_ptr, w_ptr, width, height, p_width, p_height); break; + switch (get_format_pixel_size(format)) { + case 1: _scale_bilinear<1>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 2: _scale_bilinear<2>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 3: _scale_bilinear<3>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 4: _scale_bilinear<4>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + } + } + + if (interpolate_mipmaps) { + // Switch to read again from the first scaled mipmap to overlay it over the second + r = dst.data.read(); + _overlay(r.ptr(), w.ptr(), mip1_weight, p_width, p_height, get_format_pixel_size(format)); } } break; @@ -759,7 +842,11 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { r = PoolVector<uint8_t>::Read(); w = PoolVector<uint8_t>::Write(); - if (mipmaps > 0) + if (interpolate_mipmaps) { + dst._copy_internals_from(dst2); + } + + if (had_mipmaps) dst.generate_mipmaps(); _copy_internals_from(dst); @@ -900,8 +987,10 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int & int pixsize = get_format_pixel_size(p_format); int pixshift = get_format_pixel_rshift(p_format); int block = get_format_block_size(p_format); - int minw, minh; - get_format_min_pixel_size(p_format, minw, minh); + //technically, you can still compress up to 1 px no matter the format, so commenting this + //int minw, minh; + //get_format_min_pixel_size(p_format, minw, minh); + int minw = 1, minh = 1; while (true) { @@ -1076,6 +1165,36 @@ void Image::shrink_x2() { } } +void Image::normalize() { + + bool used_mipmaps = has_mipmaps(); + if (used_mipmaps) { + clear_mipmaps(); + } + + lock(); + + for (int y = 0; y < height; y++) { + + for (int x = 0; x < width; x++) { + + Color c = get_pixel(x, y); + Vector3 v(c.r * 2.0 - 1.0, c.g * 2.0 - 1.0, c.b * 2.0 - 1.0); + v.normalize(); + c.r = v.x * 0.5 + 0.5; + c.g = v.y * 0.5 + 0.5; + c.b = v.z * 0.5 + 0.5; + set_pixel(x, y, c); + } + } + + unlock(); + + if (used_mipmaps) { + generate_mipmaps(true); + } +} + Error Image::generate_mipmaps(bool p_renormalize) { if (!_can_modify(format)) { @@ -1187,7 +1306,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0); if (size != p_data.size()) { - ERR_EXPLAIN("Expected data size of " + itos(size) + " in Image::create()"); + ERR_EXPLAIN("Expected data size of " + itos(size) + " bytes in Image::create(), got instead " + itos(p_data.size()) + " bytes."); ERR_FAIL_COND(p_data.size() != size); } @@ -1475,10 +1594,10 @@ Error Image::save_png(const String &p_path) const { return save_png_func(p_path, Ref<Image>((Image *)this)); } -int Image::get_image_data_size(int p_width, int p_height, Format p_format, int p_mipmaps) { +int Image::get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps) { int mm; - return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmaps); + return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmaps ? -1 : 0); } int Image::get_image_required_mipmaps(int p_width, int p_height, Format p_format) { @@ -1868,8 +1987,9 @@ void Image::fill(const Color &c) { unlock(); } -Ref<Image> (*Image::_png_mem_loader_func)(const uint8_t *, int) = NULL; -Ref<Image> (*Image::_jpg_mem_loader_func)(const uint8_t *, int) = NULL; +ImageMemLoadFunc Image::_png_mem_loader_func = NULL; +ImageMemLoadFunc Image::_jpg_mem_loader_func = NULL; +ImageMemLoadFunc Image::_webp_mem_loader_func = NULL; void (*Image::_image_compress_bc_func)(Image *, Image::CompressSource) = NULL; void (*Image::_image_compress_pvrtc2_func)(Image *) = NULL; @@ -2258,6 +2378,17 @@ Image::DetectChannels Image::get_detected_channels() { return DETECTED_RGBA; } +void Image::optimize_channels() { + switch (get_detected_channels()) { + case DETECTED_L: convert(FORMAT_L8); break; + case DETECTED_LA: convert(FORMAT_LA8); break; + case DETECTED_R: convert(FORMAT_R8); break; + case DETECTED_RG: convert(FORMAT_RG8); break; + case DETECTED_RGB: convert(FORMAT_RGB8); break; + case DETECTED_RGBA: convert(FORMAT_RGBA8); break; + } +} + void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("get_width"), &Image::get_width); @@ -2301,6 +2432,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("premultiply_alpha"), &Image::premultiply_alpha); ClassDB::bind_method(D_METHOD("srgb_to_linear"), &Image::srgb_to_linear); ClassDB::bind_method(D_METHOD("normalmap_to_xy"), &Image::normalmap_to_xy); + ClassDB::bind_method(D_METHOD("rgbe_to_srgb"), &Image::rgbe_to_srgb); ClassDB::bind_method(D_METHOD("bumpmap_to_normalmap", "bump_scale"), &Image::bumpmap_to_normalmap, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("blit_rect", "src", "src_rect", "dst"), &Image::blit_rect); @@ -2326,6 +2458,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("load_png_from_buffer", "buffer"), &Image::load_png_from_buffer); ClassDB::bind_method(D_METHOD("load_jpg_from_buffer", "buffer"), &Image::load_jpg_from_buffer); + ClassDB::bind_method(D_METHOD("load_webp_from_buffer", "buffer"), &Image::load_webp_from_buffer); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "_set_data", "_get_data"); @@ -2371,6 +2504,7 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(INTERPOLATE_NEAREST); BIND_ENUM_CONSTANT(INTERPOLATE_BILINEAR); BIND_ENUM_CONSTANT(INTERPOLATE_CUBIC); + BIND_ENUM_CONSTANT(INTERPOLATE_TRILINEAR); BIND_ENUM_CONSTANT(ALPHA_NONE); BIND_ENUM_CONSTANT(ALPHA_BIT); @@ -2412,6 +2546,37 @@ void Image::normalmap_to_xy() { convert(Image::FORMAT_LA8); } +Ref<Image> Image::rgbe_to_srgb() { + + if (data.size() == 0) + return Ref<Image>(); + + ERR_FAIL_COND_V(format != FORMAT_RGBE9995, Ref<Image>()); + + Ref<Image> new_image; + new_image.instance(); + new_image->create(width, height, 0, Image::FORMAT_RGB8); + + lock(); + + new_image->lock(); + + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + new_image->set_pixel(col, row, get_pixel(col, row).to_srgb()); + } + } + + unlock(); + new_image->unlock(); + + if (has_mipmaps()) { + new_image->generate_mipmaps(); + } + + return new_image; +} + void Image::bumpmap_to_normalmap(float bump_scale) { ERR_FAIL_COND(!_can_modify(format)); convert(Image::FORMAT_RF); @@ -2587,32 +2752,26 @@ String Image::get_format_name(Format p_format) { } Error Image::load_png_from_buffer(const PoolVector<uint8_t> &p_array) { - - int buffer_size = p_array.size(); - - ERR_FAIL_COND_V(buffer_size == 0, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(!_png_mem_loader_func, ERR_INVALID_PARAMETER); - - PoolVector<uint8_t>::Read r = p_array.read(); - - Ref<Image> image = _png_mem_loader_func(r.ptr(), buffer_size); - ERR_FAIL_COND_V(!image.is_valid(), ERR_PARSE_ERROR); - - copy_internals_from(image); - - return OK; + return _load_from_buffer(p_array, _png_mem_loader_func); } Error Image::load_jpg_from_buffer(const PoolVector<uint8_t> &p_array) { + return _load_from_buffer(p_array, _jpg_mem_loader_func); +} + +Error Image::load_webp_from_buffer(const PoolVector<uint8_t> &p_array) { + return _load_from_buffer(p_array, _webp_mem_loader_func); +} +Error Image::_load_from_buffer(const PoolVector<uint8_t> &p_array, ImageMemLoadFunc p_loader) { int buffer_size = p_array.size(); ERR_FAIL_COND_V(buffer_size == 0, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(!_jpg_mem_loader_func, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(!p_loader, ERR_INVALID_PARAMETER); PoolVector<uint8_t>::Read r = p_array.read(); - Ref<Image> image = _jpg_mem_loader_func(r.ptr(), buffer_size); + Ref<Image> image = p_loader(r.ptr(), buffer_size); ERR_FAIL_COND_V(!image.is_valid(), ERR_PARSE_ERROR); copy_internals_from(image); diff --git a/core/image.h b/core/image.h index 80a0c339dd..c8dd647c31 100644 --- a/core/image.h +++ b/core/image.h @@ -47,6 +47,7 @@ class Image; typedef Error (*SavePNGFunc)(const String &p_path, const Ref<Image> &p_img); +typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size); class Image : public Resource { GDCLASS(Image, Resource); @@ -107,19 +108,23 @@ public: INTERPOLATE_NEAREST, INTERPOLATE_BILINEAR, INTERPOLATE_CUBIC, + INTERPOLATE_TRILINEAR, + /* INTERPOLATE_TRICUBIC, */ /* INTERPOLATE GAUSS */ }; enum CompressSource { COMPRESS_SOURCE_GENERIC, COMPRESS_SOURCE_SRGB, - COMPRESS_SOURCE_NORMAL + COMPRESS_SOURCE_NORMAL, + COMPRESS_SOURCE_LAYERED, }; //some functions provided by something else - static Ref<Image> (*_png_mem_loader_func)(const uint8_t *p_png, int p_size); - static Ref<Image> (*_jpg_mem_loader_func)(const uint8_t *p_png, int p_size); + static ImageMemLoadFunc _png_mem_loader_func; + static ImageMemLoadFunc _jpg_mem_loader_func; + static ImageMemLoadFunc _webp_mem_loader_func; static void (*_image_compress_bc_func)(Image *, CompressSource p_source); static void (*_image_compress_pvrtc2_func)(Image *); @@ -175,6 +180,8 @@ private: void _set_data(const Dictionary &p_data); Dictionary _get_data() const; + Error _load_from_buffer(const PoolVector<uint8_t> &p_array, ImageMemLoadFunc p_loader); + public: int get_width() const; ///< Get image width int get_height() const; ///< Get image height @@ -220,6 +227,7 @@ public: Error generate_mipmaps(bool p_renormalize = false); void clear_mipmaps(); + void normalize(); //for normal maps /** * Create a new image of a given size and format. Current image will be lost @@ -265,7 +273,7 @@ public: static int get_format_block_size(Format p_format); static void get_format_min_pixel_size(Format p_format, int &r_w, int &r_h); - static int get_image_data_size(int p_width, int p_height, Format p_format, int p_mipmaps = 0); + static int get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps = false); static int get_image_required_mipmaps(int p_width, int p_height, Format p_format); enum CompressMode { @@ -284,6 +292,7 @@ public: void premultiply_alpha(); void srgb_to_linear(); void normalmap_to_xy(); + Ref<Image> rgbe_to_srgb(); void bumpmap_to_normalmap(float bump_scale = 1.0); void blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest); @@ -300,6 +309,7 @@ public: Error load_png_from_buffer(const PoolVector<uint8_t> &p_array); Error load_jpg_from_buffer(const PoolVector<uint8_t> &p_array); + Error load_webp_from_buffer(const PoolVector<uint8_t> &p_array); Image(const uint8_t *p_mem_png_jpg, int p_len = -1); Image(const char **p_xpm); @@ -320,6 +330,7 @@ public: }; DetectChannels get_detected_channels(); + void optimize_channels(); Color get_pixelv(const Point2 &p_src) const; Color get_pixel(int p_x, int p_y) const; diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index 84268c6698..1e47be4c08 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -89,7 +89,7 @@ Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8 for (size_t i = 0; i < ds; i += 16) { - aes256_decrypt_ecb(&ctx, &data[i]); + aes256_decrypt_ecb(&ctx, &data.write[i]); } aes256_done(&ctx); @@ -117,7 +117,7 @@ Error FileAccessEncrypted::open_and_parse_password(FileAccess *p_base, const Str key.resize(32); for (int i = 0; i < 32; i++) { - key[i] = cs[i]; + key.write[i] = cs[i]; } return open_and_parse(p_base, key, p_mode); @@ -148,7 +148,7 @@ void FileAccessEncrypted::close() { compressed.resize(len); zeromem(compressed.ptrw(), len); for (int i = 0; i < data.size(); i++) { - compressed[i] = data[i]; + compressed.write[i] = data[i]; } aes256_context ctx; @@ -156,7 +156,7 @@ void FileAccessEncrypted::close() { for (size_t i = 0; i < len; i += 16) { - aes256_encrypt_ecb(&ctx, &compressed[i]); + aes256_encrypt_ecb(&ctx, &compressed.write[i]); } aes256_done(&ctx); @@ -263,7 +263,7 @@ void FileAccessEncrypted::store_buffer(const uint8_t *p_src, int p_length) { data.resize(pos + p_length); for (int i = 0; i < p_length; i++) { - data[pos + i] = p_src[i]; + data.write[pos + i] = p_src[i]; } pos += p_length; } @@ -280,7 +280,7 @@ void FileAccessEncrypted::store_8(uint8_t p_dest) { ERR_FAIL_COND(!writing); if (pos < data.size()) { - data[pos] = p_dest; + data.write[pos] = p_dest; pos++; } else if (pos == data.size()) { data.push_back(p_dest); diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index 1aa1e4a595..c4eb2848b1 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -92,7 +92,7 @@ Error FileAccessMemory::_open(const String &p_path, int p_mode_flags) { Map<String, Vector<uint8_t> >::Element *E = files->find(name); ERR_FAIL_COND_V(!E, ERR_FILE_NOT_FOUND); - data = &(E->get()[0]); + data = E->get().ptrw(); length = E->get().size(); pos = 0; diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index 98403bf3f1..561f56fd82 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -258,8 +258,8 @@ void FileAccessNetwork::_set_block(int p_offset, const Vector<uint8_t> &p_block) } buffer_mutex->lock(); - pages[page].buffer = p_block; - pages[page].queued = false; + pages.write[page].buffer = p_block; + pages.write[page].queued = false; buffer_mutex->unlock(); if (waiting_on_page == page) { @@ -389,7 +389,7 @@ void FileAccessNetwork::_queue_page(int p_page) const { br.offset = size_t(p_page) * page_size; br.size = page_size; nc->block_requests.push_back(br); - pages[p_page].queued = true; + pages.write[p_page].queued = true; nc->blockrequest_mutex->unlock(); DEBUG_PRINT("QUEUE PAGE POST"); nc->sem->post(); @@ -433,12 +433,12 @@ int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const { _queue_page(page + j); } - buff = pages[page].buffer.ptrw(); + buff = pages.write[page].buffer.ptrw(); //queue pages buffer_mutex->unlock(); } - buff = pages[page].buffer.ptrw(); + buff = pages.write[page].buffer.ptrw(); last_page_buff = buff; last_page = page; } diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index 9e301ccac5..2425bb6d69 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -30,6 +30,7 @@ #include "http_client.h" #include "io/stream_peer_ssl.h" +#include "version.h" const char *HTTPClient::_methods[METHOD_MAX] = { "GET", @@ -121,16 +122,30 @@ Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n"; } bool add_clen = p_body.size() > 0; + bool add_uagent = true; + bool add_accept = true; for (int i = 0; i < p_headers.size(); i++) { request += p_headers[i] + "\r\n"; - if (add_clen && p_headers[i].find("Content-Length:") == 0) { + if (add_clen && p_headers[i].findn("Content-Length:") == 0) { add_clen = false; } + if (add_uagent && p_headers[i].findn("User-Agent:") == 0) { + add_uagent = false; + } + if (add_accept && p_headers[i].findn("Accept:") == 0) { + add_accept = false; + } } if (add_clen) { request += "Content-Length: " + itos(p_body.size()) + "\r\n"; // Should it add utf8 encoding? } + if (add_uagent) { + request += "User-Agent: GodotEngine/" + String(VERSION_FULL_BUILD) + " (" + OS::get_singleton()->get_name() + ")\r\n"; + } + if (add_accept) { + request += "Accept: */*\r\n"; + } request += "\r\n"; CharString cs = request.utf8(); @@ -173,17 +188,31 @@ Error HTTPClient::request(Method p_method, const String &p_url, const Vector<Str } else { request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n"; } + bool add_uagent = true; + bool add_accept = true; bool add_clen = p_body.length() > 0; for (int i = 0; i < p_headers.size(); i++) { request += p_headers[i] + "\r\n"; - if (add_clen && p_headers[i].find("Content-Length:") == 0) { + if (add_clen && p_headers[i].findn("Content-Length:") == 0) { add_clen = false; } + if (add_uagent && p_headers[i].findn("User-Agent:") == 0) { + add_uagent = false; + } + if (add_accept && p_headers[i].findn("Accept:") == 0) { + add_accept = false; + } } if (add_clen) { request += "Content-Length: " + itos(p_body.utf8().length()) + "\r\n"; // Should it add utf8 encoding? } + if (add_uagent) { + request += "User-Agent: GodotEngine/" + String(VERSION_FULL_BUILD) + " (" + OS::get_singleton()->get_name() + ")\r\n"; + } + if (add_accept) { + request += "Accept: */*\r\n"; + } request += "\r\n"; request += p_body; @@ -248,7 +277,9 @@ void HTTPClient::close() { body_size = 0; body_left = 0; chunk_left = 0; + read_until_eof = false; response_num = 0; + handshaking = false; } Error HTTPClient::poll() { @@ -297,16 +328,40 @@ Error HTTPClient::poll() { } break; case StreamPeerTCP::STATUS_CONNECTED: { if (ssl) { - Ref<StreamPeerSSL> ssl = StreamPeerSSL::create(); - Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host); - if (err != OK) { + Ref<StreamPeerSSL> ssl; + if (!handshaking) { + // Connect the StreamPeerSSL and start handshaking + ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); + ssl->set_blocking_handshake_enabled(false); + Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host); + if (err != OK) { + close(); + status = STATUS_SSL_HANDSHAKE_ERROR; + return ERR_CANT_CONNECT; + } + connection = ssl; + handshaking = true; + } else { + // We are already handshaking, which means we can use your already active SSL connection + ssl = static_cast<Ref<StreamPeerSSL> >(connection); + ssl->poll(); // Try to finish the handshake + } + + if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) { + // Handshake has been successfull + handshaking = false; + status = STATUS_CONNECTED; + return OK; + } else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) { + // Handshake has failed close(); status = STATUS_SSL_HANDSHAKE_ERROR; return ERR_CANT_CONNECT; } - connection = ssl; + // ... we will need to poll more for handshake to finish + } else { + status = STATUS_CONNECTED; } - status = STATUS_CONNECTED; return OK; } break; case StreamPeerTCP::STATUS_ERROR: @@ -352,10 +407,17 @@ Error HTTPClient::poll() { chunked = false; body_left = 0; chunk_left = 0; + read_until_eof = false; response_str.clear(); response_headers.clear(); response_num = RESPONSE_OK; + // Per the HTTP 1.1 spec, keep-alive is the default, but in practice + // it's safe to assume it only if the explicit header is found, allowing + // to handle body-up-to-EOF responses on naive servers; that's what Curl + // and browsers do + bool keep_alive = false; + for (int i = 0; i < responses.size(); i++) { String header = responses[i].strip_edges(); @@ -365,13 +427,14 @@ Error HTTPClient::poll() { if (s.begins_with("content-length:")) { body_size = s.substr(s.find(":") + 1, s.length()).strip_edges().to_int(); body_left = body_size; - } - if (s.begins_with("transfer-encoding:")) { + } else if (s.begins_with("transfer-encoding:")) { String encoding = header.substr(header.find(":") + 1, header.length()).strip_edges(); if (encoding == "chunked") { chunked = true; } + } else if (s.begins_with("connection: keep-alive")) { + keep_alive = true; } if (i == 0 && responses[i].begins_with("HTTP")) { @@ -384,11 +447,16 @@ Error HTTPClient::poll() { } } - if (body_size == 0 && !chunked) { + if (body_size || chunked) { - status = STATUS_CONNECTED; // Ready for new requests - } else { status = STATUS_BODY; + } else if (!keep_alive) { + + read_until_eof = true; + status = STATUS_BODY; + } else { + + status = STATUS_CONNECTED; } return OK; } @@ -484,7 +552,7 @@ PoolByteArray HTTPClient::read_response_body_chunk() { } else { int rec = 0; - err = _get_http_data(&chunk[chunk.size() - chunk_left], chunk_left, rec); + err = _get_http_data(&chunk.write[chunk.size() - chunk_left], chunk_left, rec); if (rec == 0) { break; } @@ -515,34 +583,53 @@ PoolByteArray HTTPClient::read_response_body_chunk() { } else { - int to_read = MIN(body_left, read_chunk_size); + int to_read = !read_until_eof ? MIN(body_left, read_chunk_size) : read_chunk_size; PoolByteArray ret; ret.resize(to_read); int _offset = 0; - while (to_read > 0) { + while (read_until_eof || to_read > 0) { int rec = 0; { PoolByteArray::Write w = ret.write(); err = _get_http_data(w.ptr() + _offset, to_read, rec); } - if (rec > 0) { - body_left -= rec; - to_read -= rec; - _offset += rec; - } else { + if (rec < 0) { if (to_read > 0) // Ended up reading less ret.resize(_offset); break; + } else { + _offset += rec; + if (!read_until_eof) { + body_left -= rec; + to_read -= rec; + } else { + if (rec < to_read) { + ret.resize(_offset); + err = ERR_FILE_EOF; + break; + } + ret.resize(_offset + to_read); + } } } - if (body_left == 0) { - status = STATUS_CONNECTED; + if (!read_until_eof) { + if (body_left == 0) { + status = STATUS_CONNECTED; + } + return ret; + } else { + if (err == ERR_FILE_EOF) { + err = OK; // EOF is expected here + close(); + return ret; + } } - return ret; } if (err != OK) { + close(); + if (err == ERR_FILE_EOF) { status = STATUS_DISCONNECTED; // Server disconnected @@ -602,10 +689,12 @@ HTTPClient::HTTPClient() { body_size = 0; chunked = false; body_left = 0; + read_until_eof = false; chunk_left = 0; response_num = 0; ssl = false; blocking = false; + handshaking = false; read_chunk_size = 4096; } diff --git a/core/io/http_client.h b/core/io/http_client.h index 839012e701..82b56b01db 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -165,6 +165,7 @@ private: bool ssl; bool ssl_verify_host; bool blocking; + bool handshaking; Vector<uint8_t> response_str; @@ -173,6 +174,7 @@ private: int chunk_left; int body_size; int body_left; + bool read_until_eof; Ref<StreamPeerTCP> tcp_connection; Ref<StreamPeer> connection; diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index 8ebd9d6cd9..b8fd13d67c 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -107,3 +107,83 @@ void ImageLoader::add_image_format_loader(ImageFormatLoader *p_loader) { ERR_FAIL_COND(loader_count >= MAX_LOADERS); loader[loader_count++] = p_loader; } + +///////////////// + +RES ResourceFormatLoaderImage::load(const String &p_path, const String &p_original_path, Error *r_error) { + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + if (!f) { + if (r_error) { + *r_error = ERR_CANT_OPEN; + } + memdelete(f); + return RES(); + } + + uint8_t header[4] = { 0, 0, 0, 0 }; + f->get_buffer(header, 4); + + bool unrecognized = header[0] != 'G' || header[1] != 'D' || header[2] != 'I' || header[3] != 'M'; + if (unrecognized) { + memdelete(f); + if (r_error) { + *r_error = ERR_FILE_UNRECOGNIZED; + } + ERR_FAIL_V(RES()); + } + + String extension = f->get_pascal_string(); + + int idx = -1; + + for (int i = 0; i < ImageLoader::loader_count; i++) { + if (ImageLoader::loader[i]->recognize(extension)) { + idx = i; + break; + } + } + + if (idx == -1) { + memdelete(f); + if (r_error) { + *r_error = ERR_FILE_UNRECOGNIZED; + } + ERR_FAIL_V(RES()); + } + + Ref<Image> image; + image.instance(); + + Error err = ImageLoader::loader[idx]->load_image(image, f, false, 1.0); + + memdelete(f); + + if (err != OK) { + if (r_error) { + *r_error = err; + } + return RES(); + } + + if (r_error) { + *r_error = OK; + } + + return image; +} + +void ResourceFormatLoaderImage::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("image"); +} + +bool ResourceFormatLoaderImage::handles_type(const String &p_type) const { + + return p_type == "Image"; +} + +String ResourceFormatLoaderImage::get_resource_type(const String &p_path) const { + + return p_path.get_extension().to_lower() == "image" ? "Image" : String(); +} diff --git a/core/io/image_loader.h b/core/io/image_loader.h index 052a8b8a40..fbb654c326 100644 --- a/core/io/image_loader.h +++ b/core/io/image_loader.h @@ -32,9 +32,11 @@ #define IMAGE_LOADER_H #include "image.h" +#include "io/resource_loader.h" #include "list.h" #include "os/file_access.h" #include "ustring.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -55,6 +57,7 @@ class ImageLoader; class ImageFormatLoader { friend class ImageLoader; + friend class ResourceFormatLoaderImage; protected: virtual Error load_image(Ref<Image> p_image, FileAccess *p_fileaccess, bool p_force_linear, float p_scale) = 0; @@ -70,7 +73,7 @@ class ImageLoader { enum { MAX_LOADERS = 8 }; - + friend class ResourceFormatLoaderImage; static ImageFormatLoader *loader[MAX_LOADERS]; static int loader_count; @@ -83,4 +86,12 @@ public: static void add_image_format_loader(ImageFormatLoader *p_loader); }; +class ResourceFormatLoaderImage : public ResourceFormatLoader { +public: + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; + #endif diff --git a/core/io/logger.cpp b/core/io/logger.cpp index 983b829d8d..786bec461b 100644 --- a/core/io/logger.cpp +++ b/core/io/logger.cpp @@ -112,7 +112,7 @@ void RotatedFileLogger::clear_old_backups() { int max_backups = max_files - 1; // -1 for the current file String basename = base_path.get_file().get_basename(); - String extension = "." + base_path.get_extension(); + String extension = base_path.get_extension(); DirAccess *da = DirAccess::open(base_path.get_base_dir()); if (!da) { @@ -123,7 +123,7 @@ void RotatedFileLogger::clear_old_backups() { String f = da->get_next(); Set<String> backups; while (f != String()) { - if (!da->current_is_dir() && f.begins_with(basename) && f.ends_with(extension) && f != base_path.get_file()) { + if (!da->current_is_dir() && f.begins_with(basename) && f.get_extension() == extension && f != base_path.get_file()) { backups.insert(f); } f = da->get_next(); @@ -152,7 +152,10 @@ void RotatedFileLogger::rotate_file() { OS::Time time = OS::get_singleton()->get_time(); sprintf(timestamp, "-%04d-%02d-%02d-%02d-%02d-%02d", date.year, date.month, date.day, time.hour, time.min, time.sec); - String backup_name = base_path.get_basename() + timestamp + "." + base_path.get_extension(); + String backup_name = base_path.get_basename() + timestamp; + if (base_path.get_extension() != String()) { + backup_name += "." + base_path.get_extension(); + } DirAccess *da = DirAccess::open(base_path.get_base_dir()); if (da) { diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 0a3a6c1ba1..e97df0c261 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -32,8 +32,13 @@ #include "os/keyboard.h" #include "print_string.h" #include "reference.h" +#include <limits.h> #include <stdio.h> +#define _S(a) ((int32_t)a) +#define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(_S(b) < 0 || _S(a) < 0 || _S(a) > INT_MAX - _S(b), err) +#define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(_S(a) < 0 || _S(b) <= 0 || _S(a) > INT_MAX / _S(b), err) + void EncodedObjectAsID::_bind_methods() { ClassDB::bind_method(D_METHOD("set_object_id", "id"), &EncodedObjectAsID::set_object_id); ClassDB::bind_method(D_METHOD("get_object_id"), &EncodedObjectAsID::get_object_id); @@ -60,23 +65,31 @@ EncodedObjectAsID::EncodedObjectAsID() { static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r_string) { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t strlen = decode_uint32(buf); + int32_t strlen = decode_uint32(buf); + int32_t pad = 0; + + // Handle padding + if (strlen % 4) { + pad = 4 - strlen % 4; + } + buf += 4; len -= 4; - ERR_FAIL_COND_V((int)strlen > len, ERR_FILE_EOF); + + // Ensure buffer is big enough + ERR_FAIL_ADD_OF(strlen, pad, ERR_FILE_EOF); + ERR_FAIL_COND_V(strlen < 0 || strlen + pad > len, ERR_FILE_EOF); String str; - str.parse_utf8((const char *)buf, strlen); + ERR_FAIL_COND_V(str.parse_utf8((const char *)buf, strlen), ERR_INVALID_DATA); r_string = str; - //handle padding - if (strlen % 4) { - strlen += 4 - strlen % 4; - } + // Add padding + strlen += pad; + // Update buffer pos, left data count, and return size buf += strlen; len -= strlen; - if (r_len) { (*r_len) += 4 + strlen; } @@ -119,14 +132,15 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::INT: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); if (type & ENCODE_FLAG_64) { + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); int64_t val = decode_uint64(buf); r_variant = val; if (r_len) (*r_len) += 8; } else { + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t val = decode_uint32(buf); r_variant = val; if (r_len) @@ -136,14 +150,14 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::REAL: { - ERR_FAIL_COND_V(len < (int)4, ERR_INVALID_DATA); - if (type & ENCODE_FLAG_64) { + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); double val = decode_double(buf); r_variant = val; if (r_len) (*r_len) += 8; } else { + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); float val = decode_float(buf); r_variant = val; if (r_len) @@ -164,7 +178,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int // math types case Variant::VECTOR2: { - ERR_FAIL_COND_V(len < (int)4 * 2, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 2, ERR_INVALID_DATA); Vector2 val; val.x = decode_float(&buf[0]); val.y = decode_float(&buf[4]); @@ -176,7 +190,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; // 5 case Variant::RECT2: { - ERR_FAIL_COND_V(len < (int)4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Rect2 val; val.position.x = decode_float(&buf[0]); val.position.y = decode_float(&buf[4]); @@ -190,7 +204,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::VECTOR3: { - ERR_FAIL_COND_V(len < (int)4 * 3, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 3, ERR_INVALID_DATA); Vector3 val; val.x = decode_float(&buf[0]); val.y = decode_float(&buf[4]); @@ -203,7 +217,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::TRANSFORM2D: { - ERR_FAIL_COND_V(len < (int)4 * 6, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 6, ERR_INVALID_DATA); Transform2D val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { @@ -220,7 +234,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::PLANE: { - ERR_FAIL_COND_V(len < (int)4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Plane val; val.normal.x = decode_float(&buf[0]); val.normal.y = decode_float(&buf[4]); @@ -234,7 +248,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::QUAT: { - ERR_FAIL_COND_V(len < (int)4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Quat val; val.x = decode_float(&buf[0]); val.y = decode_float(&buf[4]); @@ -248,7 +262,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::AABB: { - ERR_FAIL_COND_V(len < (int)4 * 6, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 6, ERR_INVALID_DATA); AABB val; val.position.x = decode_float(&buf[0]); val.position.y = decode_float(&buf[4]); @@ -264,7 +278,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::BASIS: { - ERR_FAIL_COND_V(len < (int)4 * 9, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 9, ERR_INVALID_DATA); Basis val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { @@ -281,7 +295,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::TRANSFORM: { - ERR_FAIL_COND_V(len < (int)4 * 12, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 12, ERR_INVALID_DATA); Transform val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { @@ -303,7 +317,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int // misc types case Variant::COLOR: { - ERR_FAIL_COND_V(len < (int)4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Color val; val.r = decode_float(&buf[0]); val.g = decode_float(&buf[4]); @@ -318,7 +332,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::NODE_PATH: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t strlen = decode_uint32(buf); + int32_t strlen = decode_uint32(buf); if (strlen & 0x80000000) { //new format @@ -343,31 +357,15 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int for (uint32_t i = 0; i < total; i++) { - ERR_FAIL_COND_V((int)len < 4, ERR_INVALID_DATA); - strlen = decode_uint32(buf); - - int pad = 0; - - if (strlen % 4) - pad += 4 - strlen % 4; - - buf += 4; - len -= 4; - ERR_FAIL_COND_V((int)strlen + pad > len, ERR_INVALID_DATA); - String str; - str.parse_utf8((const char *)buf, strlen); + Error err = _decode_string(buf, len, r_len, str); + if (err) + return err; if (i < namecount) names.push_back(str); else subnames.push_back(str); - - buf += strlen + pad; - len -= strlen + pad; - - if (r_len) - (*r_len) += 4 + strlen + pad; } r_variant = NodePath(names, subnames, flags & 1); @@ -375,17 +373,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } else { //old format, just a string - buf += 4; - len -= 4; - ERR_FAIL_COND_V((int)strlen > len, ERR_INVALID_DATA); - - String str; - str.parse_utf8((const char *)buf, strlen); - - r_variant = NodePath(str); - - if (r_len) - (*r_len) += 4 + strlen; + ERR_FAIL_V(ERR_INVALID_DATA); } } break; @@ -402,6 +390,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int if (type & ENCODE_FLAG_OBJECT_AS_ID) { //this _is_ allowed + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); ObjectID val = decode_uint64(buf); if (r_len) (*r_len) += 8; @@ -475,7 +464,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::DICTIONARY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); // bool shared = count&0x80000000; count &= 0x7FFFFFFF; @@ -488,7 +477,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Dictionary d; - for (uint32_t i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { Variant key, value; @@ -520,7 +509,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); // bool shared = count&0x80000000; count &= 0x7FFFFFFF; @@ -533,7 +522,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Array varr; - for (uint32_t i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { int used = 0; Variant v; @@ -555,17 +544,17 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_BYTE_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count > len, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count > len, ERR_INVALID_DATA); PoolVector<uint8_t> data; if (count) { data.resize(count); PoolVector<uint8_t>::Write w = data.write(); - for (uint32_t i = 0; i < count; i++) { + for (int32_t i = 0; i < count; i++) { w[i] = buf[i]; } @@ -585,10 +574,11 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_INT_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 > len, ERR_INVALID_DATA); PoolVector<int> data; @@ -596,7 +586,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int //const int*rbuf=(const int*)buf; data.resize(count); PoolVector<int>::Write w = data.write(); - for (uint32_t i = 0; i < count; i++) { + for (int32_t i = 0; i < count; i++) { w[i] = decode_uint32(&buf[i * 4]); } @@ -612,10 +602,11 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_REAL_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 > len, ERR_INVALID_DATA); PoolVector<float> data; @@ -623,7 +614,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int //const float*rbuf=(const float*)buf; data.resize(count); PoolVector<float>::Write w = data.write(); - for (uint32_t i = 0; i < count; i++) { + for (int32_t i = 0; i < count; i++) { w[i] = decode_float(&buf[i * 4]); } @@ -640,7 +631,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_STRING_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); PoolVector<String> strings; buf += 4; @@ -650,35 +641,14 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int (*r_len) += 4; //printf("string count: %i\n",count); - for (int i = 0; i < (int)count; i++) { + for (int32_t i = 0; i < count; i++) { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t strlen = decode_uint32(buf); - - buf += 4; - len -= 4; - ERR_FAIL_COND_V((int)strlen > len, ERR_INVALID_DATA); - - //printf("loaded string: %s\n",(const char*)buf); String str; - str.parse_utf8((const char *)buf, strlen); + Error err = _decode_string(buf, len, r_len, str); + if (err) + return err; strings.push_back(str); - - buf += strlen; - len -= strlen; - - if (r_len) - (*r_len) += 4 + strlen; - - if (strlen % 4) { - int pad = 4 - (strlen % 4); - buf += pad; - len -= pad; - if (r_len) { - (*r_len) += pad; - } - } } r_variant = strings; @@ -687,11 +657,12 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_VECTOR2_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 * 2 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4 * 2, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 * 2 > len, ERR_INVALID_DATA); PoolVector<Vector2> varray; if (r_len) { @@ -702,7 +673,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int varray.resize(count); PoolVector<Vector2>::Write w = varray.write(); - for (int i = 0; i < (int)count; i++) { + for (int32_t i = 0; i < count; i++) { w[i].x = decode_float(buf + i * 4 * 2 + 4 * 0); w[i].y = decode_float(buf + i * 4 * 2 + 4 * 1); @@ -722,11 +693,13 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_VECTOR3_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 * 3 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4 * 3, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 * 3 > len, ERR_INVALID_DATA); + PoolVector<Vector3> varray; if (r_len) { @@ -737,7 +710,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int varray.resize(count); PoolVector<Vector3>::Write w = varray.write(); - for (int i = 0; i < (int)count; i++) { + for (int32_t i = 0; i < count; i++) { w[i].x = decode_float(buf + i * 4 * 3 + 4 * 0); w[i].y = decode_float(buf + i * 4 * 3 + 4 * 1); @@ -758,11 +731,13 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_COLOR_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 * 4 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 * 4 > len, ERR_INVALID_DATA); + PoolVector<Color> carray; if (r_len) { @@ -773,7 +748,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int carray.resize(count); PoolVector<Color>::Write w = carray.write(); - for (int i = 0; i < (int)count; i++) { + for (int32_t i = 0; i < count; i++) { w[i].r = decode_float(buf + i * 4 * 4 + 4 * 0); w[i].g = decode_float(buf + i * 4 * 4 + 4 * 1); @@ -1323,7 +1298,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo while (r_len % 4) { r_len++; //pad if (buf) - buf++; + *(buf++) = 0; } } diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index 456d29520f..8e67f1c97a 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -1,7 +1,92 @@ +/*************************************************************************/ +/* multiplayer_api.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + #include "core/io/multiplayer_api.h" #include "core/io/marshalls.h" #include "scene/main/node.h" +_FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_master, bool &r_skip_rpc) { + + switch (mode) { + + case MultiplayerAPI::RPC_MODE_DISABLED: { + //do nothing + } break; + case MultiplayerAPI::RPC_MODE_REMOTE: { + //do nothing also, no need to call local + } break; + case MultiplayerAPI::RPC_MODE_REMOTESYNC: + case MultiplayerAPI::RPC_MODE_MASTERSYNC: + case MultiplayerAPI::RPC_MODE_SLAVESYNC: + case MultiplayerAPI::RPC_MODE_SYNC: { + //call it, sync always results in call + return true; + } break; + case MultiplayerAPI::RPC_MODE_MASTER: { + if (is_master) + r_skip_rpc = true; //no other master so.. + return is_master; + } break; + case MultiplayerAPI::RPC_MODE_SLAVE: { + return !is_master; + } break; + } + return false; +} + +_FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, int p_remote_id) { + switch (mode) { + + case MultiplayerAPI::RPC_MODE_DISABLED: { + return false; + } break; + case MultiplayerAPI::RPC_MODE_REMOTE: { + return true; + } break; + case MultiplayerAPI::RPC_MODE_REMOTESYNC: + case MultiplayerAPI::RPC_MODE_SYNC: { + return true; + } break; + case MultiplayerAPI::RPC_MODE_MASTERSYNC: + case MultiplayerAPI::RPC_MODE_MASTER: { + return p_node->is_network_master(); + } break; + case MultiplayerAPI::RPC_MODE_SLAVESYNC: + case MultiplayerAPI::RPC_MODE_SLAVE: { + return !p_node->is_network_master() && p_remote_id == p_node->get_network_master(); + } break; + } + + return false; +} + void MultiplayerAPI::poll() { if (!network_peer.is_valid() || network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) @@ -172,11 +257,19 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int } void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { - if (!p_node->can_call_rpc(p_name, p_from)) - return; ERR_FAIL_COND(p_offset >= p_packet_len); + // Check that remote can call the RPC on this node + RPCMode rpc_mode = RPC_MODE_DISABLED; + const Map<StringName, RPCMode>::Element *E = p_node->get_node_rpc_mode(p_name); + if (E) { + rpc_mode = E->get(); + } else if (p_node->get_script_instance()) { + rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_name); + } + ERR_FAIL_COND(!_can_call_mode(p_node, rpc_mode, p_from)); + int argc = p_packet[p_offset]; Vector<Variant> args; Vector<const Variant *> argp; @@ -189,10 +282,10 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ ERR_FAIL_COND(p_offset >= p_packet_len); int vlen; - Error err = decode_variant(args[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); + Error err = decode_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); ERR_FAIL_COND(err != OK); //args[i]=p_packet[3+i]; - argp[i] = &args[i]; + argp.write[i] = &args[i]; p_offset += vlen; } @@ -208,11 +301,18 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { - if (!p_node->can_call_rset(p_name, p_from)) - return; - ERR_FAIL_COND(p_offset >= p_packet_len); + // Check that remote can call the RSET on this node + RPCMode rset_mode = RPC_MODE_DISABLED; + const Map<StringName, RPCMode>::Element *E = p_node->get_node_rset_mode(p_name); + if (E) { + rset_mode = E->get(); + } else if (p_node->get_script_instance()) { + rset_mode = p_node->get_script_instance()->get_rset_mode(p_name); + } + ERR_FAIL_COND(!_can_call_mode(p_node, rset_mode, p_from)); + Variant value; decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset); @@ -254,8 +354,8 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, Vector<uint8_t> packet; packet.resize(1 + len); - packet[0] = NETWORK_COMMAND_CONFIRM_PATH; - encode_cstring(pname.get_data(), &packet[1]); + packet.write[0] = NETWORK_COMMAND_CONFIRM_PATH; + encode_cstring(pname.get_data(), &packet.write[1]); network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); network_peer->set_target_peer(p_from); @@ -315,9 +415,9 @@ bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int Vector<uint8_t> packet; packet.resize(1 + 4 + len); - packet[0] = NETWORK_COMMAND_SIMPLIFY_PATH; - encode_uint32(psc->id, &packet[1]); - encode_cstring(pname.get_data(), &packet[5]); + packet.write[0] = NETWORK_COMMAND_SIMPLIFY_PATH; + encode_uint32(psc->id, &packet.write[1]); + encode_cstring(pname.get_data(), &packet.write[5]); network_peer->set_target_peer(E->get()); //to all of you network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); @@ -382,19 +482,19 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p //encode type MAKE_ROOM(1); - packet_cache[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL; + packet_cache.write[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL; ofs += 1; //encode ID MAKE_ROOM(ofs + 4); - encode_uint32(psc->id, &(packet_cache[ofs])); + encode_uint32(psc->id, &(packet_cache.write[ofs])); ofs += 4; //encode function name CharString name = String(p_name).utf8(); int len = encode_cstring(name.get_data(), NULL); MAKE_ROOM(ofs + len); - encode_cstring(name.get_data(), &(packet_cache[ofs])); + encode_cstring(name.get_data(), &(packet_cache.write[ofs])); ofs += len; if (p_set) { @@ -402,19 +502,19 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p Error err = encode_variant(*p_arg[0], NULL, len); ERR_FAIL_COND(err != OK); MAKE_ROOM(ofs + len); - encode_variant(*p_arg[0], &(packet_cache[ofs]), len); + encode_variant(*p_arg[0], &(packet_cache.write[ofs]), len); ofs += len; } else { //call arguments MAKE_ROOM(ofs + 1); - packet_cache[ofs] = p_argcount; + packet_cache.write[ofs] = p_argcount; ofs += 1; for (int i = 0; i < p_argcount; i++) { Error err = encode_variant(*p_arg[i], NULL, len); ERR_FAIL_COND(err != OK); MAKE_ROOM(ofs + len); - encode_variant(*p_arg[i], &(packet_cache[ofs]), len); + encode_variant(*p_arg[i], &(packet_cache.write[ofs]), len); ofs += len; } } @@ -437,7 +537,7 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p CharString pname = String(from_path).utf8(); int path_len = encode_cstring(pname.get_data(), NULL); MAKE_ROOM(ofs + path_len); - encode_cstring(pname.get_data(), &(packet_cache[ofs])); + encode_cstring(pname.get_data(), &(packet_cache.write[ofs])); for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { @@ -454,11 +554,11 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p if (F->get() == true) { //this one confirmed path, so use id - encode_uint32(psc->id, &(packet_cache[1])); + encode_uint32(psc->id, &(packet_cache.write[1])); network_peer->put_packet(packet_cache.ptr(), ofs); } else { //this one did not confirm path yet, so use entire path (sorry!) - encode_uint32(0x80000000 | ofs, &(packet_cache[1])); //offset to path and flag + encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); //offset to path and flag network_peer->put_packet(packet_cache.ptr(), ofs + path_len); } } @@ -492,57 +592,6 @@ void MultiplayerAPI::_server_disconnected() { emit_signal("server_disconnected"); } -bool _should_call_native(Node::RPCMode mode, bool is_master, bool &r_skip_rpc) { - - switch (mode) { - - case Node::RPC_MODE_DISABLED: { - //do nothing - } break; - case Node::RPC_MODE_REMOTE: { - //do nothing also, no need to call local - } break; - case Node::RPC_MODE_SYNC: { - //call it, sync always results in call - return true; - } break; - case Node::RPC_MODE_MASTER: { - if (is_master) - r_skip_rpc = true; //no other master so.. - return is_master; - } break; - case Node::RPC_MODE_SLAVE: { - return !is_master; - } break; - } - return false; -} - -bool _should_call_script(ScriptInstance::RPCMode mode, bool is_master, bool &r_skip_rpc) { - switch (mode) { - - case ScriptInstance::RPC_MODE_DISABLED: { - //do nothing - } break; - case ScriptInstance::RPC_MODE_REMOTE: { - //do nothing also, no need to call local - } break; - case ScriptInstance::RPC_MODE_SYNC: { - //call it, sync always results in call - return true; - } break; - case ScriptInstance::RPC_MODE_MASTER: { - if (is_master) - r_skip_rpc = true; //no other master so.. - return is_master; - } break; - case ScriptInstance::RPC_MODE_SLAVE: { - return !is_master; - } break; - } - return false; -} - void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) { ERR_FAIL_COND(!p_node->is_inside_tree()); @@ -557,17 +606,17 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { //check that send mode can use local call - const Map<StringName, Node::RPCMode>::Element *E = p_node->get_node_rpc_mode(p_method); + const Map<StringName, RPCMode>::Element *E = p_node->get_node_rpc_mode(p_method); if (E) { - call_local_native = _should_call_native(E->get(), is_master, skip_rpc); + call_local_native = _should_call_local(E->get(), is_master, skip_rpc); } if (call_local_native) { // done below } else if (p_node->get_script_instance()) { //attempt with script - ScriptInstance::RPCMode rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method); - call_local_script = _should_call_script(rpc_mode, is_master, skip_rpc); + RPCMode rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method); + call_local_script = _should_call_local(rpc_mode, is_master, skip_rpc); } } @@ -613,10 +662,10 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const bool set_local = false; - const Map<StringName, Node::RPCMode>::Element *E = p_node->get_node_rset_mode(p_property); + const Map<StringName, RPCMode>::Element *E = p_node->get_node_rset_mode(p_property); if (E) { - set_local = _should_call_native(E->get(), is_master, skip_rset); + set_local = _should_call_local(E->get(), is_master, skip_rset); } if (set_local) { @@ -630,9 +679,9 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const } } else if (p_node->get_script_instance()) { //attempt with script - ScriptInstance::RPCMode rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property); + RPCMode rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property); - set_local = _should_call_script(rpc_mode, is_master, skip_rset); + set_local = _should_call_local(rpc_mode, is_master, skip_rset); if (set_local) { @@ -655,7 +704,7 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const _send_rpc(p_node, p_peer_id, p_unreliable, true, p_property, &vptr, 1); } -Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to) { +Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to, NetworkedMultiplayerPeer::TransferMode p_mode) { ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA); ERR_FAIL_COND_V(!network_peer.is_valid(), ERR_UNCONFIGURED); @@ -663,9 +712,12 @@ Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to) { MAKE_ROOM(p_data.size() + 1); PoolVector<uint8_t>::Read r = p_data.read(); - packet_cache[0] = NETWORK_COMMAND_RAW; - memcpy(&packet_cache[1], &r[0], p_data.size()); + packet_cache.write[0] = NETWORK_COMMAND_RAW; + memcpy(&packet_cache.write[1], &r[0], p_data.size()); + network_peer->set_target_peer(p_to); + network_peer->set_transfer_mode(p_mode); + return network_peer->put_packet(packet_cache.ptr(), p_data.size() + 1); } @@ -721,7 +773,7 @@ Vector<int> MultiplayerAPI::get_network_connected_peers() const { void MultiplayerAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); - ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST)); + ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE)); ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer); ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer); ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id); @@ -748,6 +800,15 @@ void MultiplayerAPI::_bind_methods() { ADD_SIGNAL(MethodInfo("connected_to_server")); ADD_SIGNAL(MethodInfo("connection_failed")); ADD_SIGNAL(MethodInfo("server_disconnected")); + + BIND_ENUM_CONSTANT(RPC_MODE_DISABLED); + BIND_ENUM_CONSTANT(RPC_MODE_REMOTE); + BIND_ENUM_CONSTANT(RPC_MODE_SYNC); + BIND_ENUM_CONSTANT(RPC_MODE_MASTER); + BIND_ENUM_CONSTANT(RPC_MODE_SLAVE); + BIND_ENUM_CONSTANT(RPC_MODE_REMOTESYNC); + BIND_ENUM_CONSTANT(RPC_MODE_MASTERSYNC); + BIND_ENUM_CONSTANT(RPC_MODE_SLAVESYNC); } MultiplayerAPI::MultiplayerAPI() { diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h index 25f445004d..e47b1830e8 100644 --- a/core/io/multiplayer_api.h +++ b/core/io/multiplayer_api.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* multiplayer_api.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + #ifndef MULTIPLAYER_PROTOCOL_H #define MULTIPLAYER_PROTOCOL_H @@ -57,12 +87,24 @@ public: NETWORK_COMMAND_RAW, }; + enum RPCMode { + + RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default) + RPC_MODE_REMOTE, // Using rpc() on it will call method / set property in all remote peers + RPC_MODE_SYNC, // Using rpc() on it will call method / set property in all remote peers and locally + RPC_MODE_MASTER, // Using rpc() on it will call method on wherever the master is, be it local or remote + RPC_MODE_SLAVE, // Using rpc() on it will call method for all slaves + RPC_MODE_REMOTESYNC, // Same as RPC_MODE_SYNC, compatibility + RPC_MODE_MASTERSYNC, // Using rpc() on it will call method / set property in the master peer and locally + RPC_MODE_SLAVESYNC, // Using rpc() on it will call method / set property in all slave peers and locally + }; + void poll(); void clear(); void set_root_node(Node *p_node); void set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer); Ref<NetworkedMultiplayerPeer> get_network_peer() const; - Error send_bytes(PoolVector<uint8_t> p_data, int p_to = NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST); + Error send_bytes(PoolVector<uint8_t> p_data, int p_to = NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST, NetworkedMultiplayerPeer::TransferMode p_mode = NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); // Called by Node.rpc void rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount); @@ -87,4 +129,6 @@ public: ~MultiplayerAPI(); }; +VARIANT_ENUM_CAST(MultiplayerAPI::RPCMode); + #endif // MULTIPLAYER_PROTOCOL_H diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index b777a9f960..dc4997dfc2 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -173,7 +173,7 @@ Error PacketPeerStream::_poll_buffer() const { int read = 0; ERR_FAIL_COND_V(input_buffer.size() < ring_buffer.space_left(), ERR_UNAVAILABLE); - Error err = peer->get_partial_data(&input_buffer[0], ring_buffer.space_left(), read); + Error err = peer->get_partial_data(input_buffer.ptrw(), ring_buffer.space_left(), read); if (err) return err; if (read == 0) @@ -226,7 +226,7 @@ Error PacketPeerStream::get_packet(const uint8_t **r_buffer, int &r_buffer_size) ERR_FAIL_COND_V(input_buffer.size() < len, ERR_UNAVAILABLE); ring_buffer.read(lbuf, 4); //get rid of first 4 bytes - ring_buffer.read(&input_buffer[0], len); // read packet + ring_buffer.read(input_buffer.ptrw(), len); // read packet *r_buffer = &input_buffer[0]; r_buffer_size = len; @@ -247,8 +247,8 @@ Error PacketPeerStream::put_packet(const uint8_t *p_buffer, int p_buffer_size) { ERR_FAIL_COND_V(p_buffer_size < 0, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_buffer_size + 4 > output_buffer.size(), ERR_INVALID_PARAMETER); - encode_uint32(p_buffer_size, &output_buffer[0]); - uint8_t *dst = &output_buffer[4]; + encode_uint32(p_buffer_size, output_buffer.ptrw()); + uint8_t *dst = &output_buffer.write[4]; for (int i = 0; i < p_buffer_size; i++) dst[i] = p_buffer[i]; diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index b6377662de..2fd73db27d 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -120,7 +120,7 @@ Error PCKPacker::flush(bool p_verbose) { for (int i = 0; i < files.size(); i++) { file->store_pascal_string(files[i].path); - files[i].offset_offset = file->get_position(); + files.write[i].offset_offset = file->get_position(); file->store_64(0); // offset file->store_64(files[i].size); // size diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 0c626c197b..02c2c6ce1a 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -894,7 +894,7 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { for (uint32_t i = 0; i < string_table_size; i++) { StringName s = get_unicode_string(); - string_map[i] = s; + string_map.write[i] = s; } print_bl("strings: " + itos(string_table_size)); @@ -1834,7 +1834,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p save_order.resize(external_resources.size()); for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { - save_order[E->get()] = E->key(); + save_order.write[E->get()] = E->key(); } for (int i = 0; i < save_order.size(); i++) { diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 86bf77318e..0bb0fb48d4 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -557,7 +557,7 @@ void ResourceLoader::load_translation_remaps() { Vector<String> lang_remaps; lang_remaps.resize(langs.size()); for (int i = 0; i < langs.size(); i++) { - lang_remaps[i] = langs[i]; + lang_remaps.write[i] = langs[i]; } translation_remaps[String(E->get())] = lang_remaps; diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index 927b9f6366..3e0ee088c2 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -331,7 +331,7 @@ String StreamPeer::get_string(int p_bytes) { ERR_FAIL_COND_V(err != OK, String()); err = get_data((uint8_t *)&buf[0], p_bytes); ERR_FAIL_COND_V(err != OK, String()); - buf[p_bytes] = 0; + buf.write[p_bytes] = 0; return buf.ptr(); } String StreamPeer::get_utf8_string(int p_bytes) { diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp index 012ba78c6d..e7e9662d24 100644 --- a/core/io/stream_peer_ssl.cpp +++ b/core/io/stream_peer_ssl.cpp @@ -52,6 +52,14 @@ bool StreamPeerSSL::is_available() { return available; } +void StreamPeerSSL::set_blocking_handshake_enabled(bool p_enabled) { + blocking_handshake = p_enabled; +} + +bool StreamPeerSSL::is_blocking_handshake_enabled() const { + return blocking_handshake; +} + PoolByteArray StreamPeerSSL::get_project_cert_array() { PoolByteArray out; @@ -84,16 +92,21 @@ PoolByteArray StreamPeerSSL::get_project_cert_array() { void StreamPeerSSL::_bind_methods() { ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll); - ClassDB::bind_method(D_METHOD("accept_stream", "stream"), &StreamPeerSSL::accept_stream); + ClassDB::bind_method(D_METHOD("accept_stream", "base"), &StreamPeerSSL::accept_stream); ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String())); ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerSSL::get_status); ClassDB::bind_method(D_METHOD("disconnect_from_stream"), &StreamPeerSSL::disconnect_from_stream); + ClassDB::bind_method(D_METHOD("set_blocking_handshake_enabled", "enabled"), &StreamPeerSSL::set_blocking_handshake_enabled); + ClassDB::bind_method(D_METHOD("is_blocking_handshake_enabled"), &StreamPeerSSL::is_blocking_handshake_enabled); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking_handshake"), "set_blocking_handshake_enabled", "is_blocking_handshake_enabled"); BIND_ENUM_CONSTANT(STATUS_DISCONNECTED); BIND_ENUM_CONSTANT(STATUS_CONNECTED); - BIND_ENUM_CONSTANT(STATUS_ERROR_NO_CERTIFICATE); + BIND_ENUM_CONSTANT(STATUS_ERROR); BIND_ENUM_CONSTANT(STATUS_ERROR_HOSTNAME_MISMATCH); } StreamPeerSSL::StreamPeerSSL() { + blocking_handshake = true; } diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h index 77301a7c87..870704e875 100644 --- a/core/io/stream_peer_ssl.h +++ b/core/io/stream_peer_ssl.h @@ -49,14 +49,20 @@ protected: friend class Main; static bool initialize_certs; + bool blocking_handshake; + public: enum Status { STATUS_DISCONNECTED, + STATUS_HANDSHAKING, STATUS_CONNECTED, - STATUS_ERROR_NO_CERTIFICATE, + STATUS_ERROR, STATUS_ERROR_HOSTNAME_MISMATCH }; + void set_blocking_handshake_enabled(bool p_enabled); + bool is_blocking_handshake_enabled() const; + virtual void poll() = 0; virtual Error accept_stream(Ref<StreamPeer> p_base) = 0; virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String()) = 0; diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index e01e2a84c5..85c1fc5ddf 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -54,32 +54,25 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S int line = 1; bool skip_this = false; bool skip_next = false; + bool is_eof = false; - while (true) { + while (!is_eof) { - String l = f->get_line(); + String l = f->get_line().strip_edges(); + is_eof = f->eof_reached(); - if (f->eof_reached()) { + // If we reached last line and it's not a content line, break, otherwise let processing that last loop + if (is_eof && l.empty()) { - if (status == STATUS_READING_STRING) { - - if (msg_id != "") { - if (!skip_this) - translation->add_message(msg_id, msg_str); - } else if (config == "") - config = msg_str; - break; - - } else if (status == STATUS_NONE) + if (status == STATUS_READING_ID) { + memdelete(f); + ERR_EXPLAIN(p_path + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: "); + ERR_FAIL_V(RES()); + } else { break; - - memdelete(f); - ERR_EXPLAIN(p_path + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: "); - ERR_FAIL_V(RES()); + } } - l = l.strip_edges(); - if (l.begins_with("msgid")) { if (status == STATUS_READING_ID) { @@ -160,6 +153,15 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S f->close(); memdelete(f); + if (status == STATUS_READING_STRING) { + + if (msg_id != "") { + if (!skip_this) + translation->add_message(msg_id, msg_str); + } else if (config == "") + config = msg_str; + } + if (config == "") { ERR_EXPLAIN("No config found in file: " + p_path); ERR_FAIL_V(RES()); @@ -175,7 +177,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S String prop = c.substr(0, p).strip_edges(); String value = c.substr(p + 1, c.length()).strip_edges(); - if (prop == "X-Language") { + if (prop == "X-Language" || prop == "Language") { translation->set_locale(value); } } diff --git a/core/make_binders.py b/core/make_binders.py index 6a7602cd5d..4c61b90d99 100644 --- a/core/make_binders.py +++ b/core/make_binders.py @@ -1,6 +1,5 @@ # -*- coding: ibm850 -*- - template_typed = """ #ifdef TYPED_METHOD_BIND template<class T $ifret ,class R$ $ifargs ,$ $arg, class P@$> @@ -265,8 +264,13 @@ def run(target, source, env): else: text += t - with open(target[0].path, "w") as f: + with open(target[0], "w") as f: f.write(text) - with open(target[1].path, "w") as f: + with open(target[1], "w") as f: f.write(text_ext) + + +if __name__ == '__main__': + from platform_methods import subprocess_main + subprocess_main(globals()) diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index 6908d7831d..021391da83 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -96,11 +96,11 @@ void AStar::remove_point(int p_id) { Point *p = points[p_id]; - for (int i = 0; i < p->neighbours.size(); i++) { + for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) { - Segment s(p_id, p->neighbours[i]->id); + Segment s(p_id, E->get()->id); segments.erase(s); - p->neighbours[i]->neighbours.erase(p); + E->get()->neighbours.erase(p); } memdelete(p); @@ -115,10 +115,10 @@ void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) { Point *a = points[p_id]; Point *b = points[p_with_id]; - a->neighbours.push_back(b); + a->neighbours.insert(b); if (bidirectional) - b->neighbours.push_back(a); + b->neighbours.insert(a); Segment s(p_id, p_with_id); if (s.from == p_id) { @@ -168,8 +168,8 @@ PoolVector<int> AStar::get_point_connections(int p_id) { Point *p = points[p_id]; - for (int i = 0; i < p->neighbours.size(); i++) { - point_list.push_back(p->neighbours[i]->id); + for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) { + point_list.push_back(E->get()->id); } return point_list; @@ -242,9 +242,9 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { bool found_route = false; - for (int i = 0; i < begin_point->neighbours.size(); i++) { + for (Set<Point *>::Element *E = begin_point->neighbours.front(); E; E = E->next()) { - Point *n = begin_point->neighbours[i]; + Point *n = E->get(); n->prev_point = begin_point; n->distance = _compute_cost(begin_point->id, n->id) * n->weight_scale; n->last_pass = pass; @@ -283,12 +283,10 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { } Point *p = least_cost_point->self(); - // Open the neighbours for search - int es = p->neighbours.size(); - for (int i = 0; i < es; i++) { + for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) { - Point *e = p->neighbours[i]; + Point *e = E->get(); real_t distance = _compute_cost(p->id, e->id) * e->weight_scale + p->distance; diff --git a/core/math/a_star.h b/core/math/a_star.h index f89e17c7bb..8c1b5f64cb 100644 --- a/core/math/a_star.h +++ b/core/math/a_star.h @@ -54,7 +54,7 @@ class AStar : public Reference { real_t weight_scale; uint64_t last_pass; - Vector<Point *> neighbours; + Set<Point *> neighbours; // Used for pathfinding Point *prev_point; diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp index cff19f990c..e2e71dda92 100644 --- a/core/math/aabb.cpp +++ b/core/math/aabb.cpp @@ -245,7 +245,6 @@ Vector3 AABB::get_longest_axis() const { if (size.z > max_size) { axis = Vector3(0, 0, 1); - max_size = size.z; } return axis; @@ -262,7 +261,6 @@ int AABB::get_longest_axis_index() const { if (size.z > max_size) { axis = 2; - max_size = size.z; } return axis; @@ -280,7 +278,6 @@ Vector3 AABB::get_shortest_axis() const { if (size.z < max_size) { axis = Vector3(0, 0, 1); - max_size = size.z; } return axis; @@ -297,7 +294,6 @@ int AABB::get_shortest_axis_index() const { if (size.z < max_size) { axis = 2; - max_size = size.z; } return axis; diff --git a/core/math/aabb.h b/core/math/aabb.h index 39b8f403e7..cdb8eb48a3 100644 --- a/core/math/aabb.h +++ b/core/math/aabb.h @@ -76,6 +76,7 @@ public: _FORCE_INLINE_ bool smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real_t t0, real_t t1) const; _FORCE_INLINE_ bool intersects_convex_shape(const Plane *p_planes, int p_plane_count) const; + _FORCE_INLINE_ bool inside_convex_shape(const Plane *p_planes, int p_plane_count) const; bool intersects_plane(const Plane &p_plane) const; _FORCE_INLINE_ bool has_point(const Vector3 &p_point) const; @@ -207,6 +208,25 @@ bool AABB::intersects_convex_shape(const Plane *p_planes, int p_plane_count) con return true; } +bool AABB::inside_convex_shape(const Plane *p_planes, int p_plane_count) const { + + Vector3 half_extents = size * 0.5; + Vector3 ofs = position + half_extents; + + for (int i = 0; i < p_plane_count; i++) { + const Plane &p = p_planes[i]; + Vector3 point( + (p.normal.x < 0) ? -half_extents.x : half_extents.x, + (p.normal.y < 0) ? -half_extents.y : half_extents.y, + (p.normal.z < 0) ? -half_extents.z : half_extents.z); + point += ofs; + if (p.is_point_over(point)) + return false; + } + + return true; +} + bool AABB::has_point(const Vector3 &p_point) const { if (p_point.x < position.x) diff --git a/core/math/bsp_tree.cpp b/core/math/bsp_tree.cpp index b1424e1d78..24096de551 100644 --- a/core/math/bsp_tree.cpp +++ b/core/math/bsp_tree.cpp @@ -244,10 +244,8 @@ bool BSP_Tree::point_is_inside(const Vector3 &p_point) const { const Node *nodesptr = &nodes[0]; const Plane *planesptr = &planes[0]; - int plane_count = planes.size(); int idx = node_count - 1; - int steps = 0; while (true) { @@ -259,21 +257,19 @@ bool BSP_Tree::point_is_inside(const Vector3 &p_point) const { return true; } - uint16_t plane = nodesptr[idx].plane; #ifdef DEBUG_ENABLED - + int plane_count = planes.size(); + uint16_t plane = nodesptr[idx].plane; ERR_FAIL_INDEX_V(plane, plane_count, false); #endif + bool over = planesptr[nodesptr[idx].plane].is_point_over(p_point); idx = over ? nodes[idx].over : nodes[idx].under; #ifdef DEBUG_ENABLED - ERR_FAIL_COND_V(idx < MAX_NODES && idx >= node_count, false); #endif - - steps++; } return false; @@ -453,10 +449,10 @@ BSP_Tree::operator Variant() const { for (int i = 0; i < planes.size(); i++) { - plane_values[i * 4 + 0] = planes[i].normal.x; - plane_values[i * 4 + 1] = planes[i].normal.y; - plane_values[i * 4 + 2] = planes[i].normal.z; - plane_values[i * 4 + 3] = planes[i].d; + plane_values.write[i * 4 + 0] = planes[i].normal.x; + plane_values.write[i * 4 + 1] = planes[i].normal.y; + plane_values.write[i * 4 + 2] = planes[i].normal.z; + plane_values.write[i * 4 + 3] = planes[i].d; } d["planes"] = plane_values; @@ -502,10 +498,10 @@ BSP_Tree::BSP_Tree(const Variant &p_variant) { PoolVector<real_t>::Read r = src_planes.read(); for (int i = 0; i < plane_count / 4; i++) { - planes[i].normal.x = r[i * 4 + 0]; - planes[i].normal.y = r[i * 4 + 1]; - planes[i].normal.z = r[i * 4 + 2]; - planes[i].d = r[i * 4 + 3]; + planes.write[i].normal.x = r[i * 4 + 0]; + planes.write[i].normal.y = r[i * 4 + 1]; + planes.write[i].normal.z = r[i * 4 + 2]; + planes.write[i].d = r[i * 4 + 3]; } } @@ -524,9 +520,9 @@ BSP_Tree::BSP_Tree(const Variant &p_variant) { for (int i = 0; i < nodes.size(); i++) { - nodes[i].over = r[i * 3 + 0]; - nodes[i].under = r[i * 3 + 1]; - nodes[i].plane = r[i * 3 + 2]; + nodes.write[i].over = r[i * 3 + 0]; + nodes.write[i].under = r[i * 3 + 1]; + nodes.write[i].plane = r[i * 3 + 2]; } } diff --git a/core/math/delaunay.cpp b/core/math/delaunay.cpp new file mode 100644 index 0000000000..8cae92b7c0 --- /dev/null +++ b/core/math/delaunay.cpp @@ -0,0 +1 @@ +#include "delaunay.h" diff --git a/core/math/delaunay.h b/core/math/delaunay.h new file mode 100644 index 0000000000..13fbc0c6ae --- /dev/null +++ b/core/math/delaunay.h @@ -0,0 +1,145 @@ +#ifndef DELAUNAY_H +#define DELAUNAY_H + +#include "math_2d.h" + +class Delaunay2D { +public: + struct Triangle { + + int points[3]; + bool bad; + Triangle() { bad = false; } + Triangle(int p_a, int p_b, int p_c) { + points[0] = p_a; + points[1] = p_b; + points[2] = p_c; + bad = false; + } + }; + + struct Edge { + int edge[2]; + bool bad; + Edge() { bad = false; } + Edge(int p_a, int p_b) { + bad = false; + edge[0] = p_a; + edge[1] = p_b; + } + }; + + static bool circum_circle_contains(const Vector<Vector2> &p_vertices, const Triangle &p_triangle, int p_vertex) { + + Vector2 p1 = p_vertices[p_triangle.points[0]]; + Vector2 p2 = p_vertices[p_triangle.points[1]]; + Vector2 p3 = p_vertices[p_triangle.points[2]]; + + real_t ab = p1.x * p1.x + p1.y * p1.y; + real_t cd = p2.x * p2.x + p2.y * p2.y; + real_t ef = p3.x * p3.x + p3.y * p3.y; + + Vector2 circum( + (ab * (p3.y - p2.y) + cd * (p1.y - p3.y) + ef * (p2.y - p1.y)) / (p1.x * (p3.y - p2.y) + p2.x * (p1.y - p3.y) + p3.x * (p2.y - p1.y)), + (ab * (p3.x - p2.x) + cd * (p1.x - p3.x) + ef * (p2.x - p1.x)) / (p1.y * (p3.x - p2.x) + p2.y * (p1.x - p3.x) + p3.y * (p2.x - p1.x))); + + circum *= 0.5; + float r = p1.distance_squared_to(circum); + float d = p_vertices[p_vertex].distance_squared_to(circum); + return d <= r; + } + + static bool edge_compare(const Vector<Vector2> &p_vertices, const Edge &p_a, const Edge &p_b) { + if (p_vertices[p_a.edge[0]].distance_to(p_vertices[p_b.edge[0]]) < CMP_EPSILON && p_vertices[p_a.edge[1]].distance_to(p_vertices[p_b.edge[1]]) < CMP_EPSILON) { + return true; + } + + if (p_vertices[p_a.edge[0]].distance_to(p_vertices[p_b.edge[1]]) < CMP_EPSILON && p_vertices[p_a.edge[1]].distance_to(p_vertices[p_b.edge[0]]) < CMP_EPSILON) { + return true; + } + + return false; + } + + static Vector<Triangle> triangulate(const Vector<Vector2> &p_points) { + + Vector<Vector2> points = p_points; + Vector<Triangle> triangles; + + Rect2 rect; + for (int i = 0; i < p_points.size(); i++) { + if (i == 0) { + rect.position = p_points[i]; + } else { + rect.expand_to(p_points[i]); + } + } + + float delta_max = MAX(rect.size.width, rect.size.height); + Vector2 center = rect.position + rect.size * 0.5; + + points.push_back(Vector2(center.x - 20 * delta_max, center.y - delta_max)); + points.push_back(Vector2(center.x, center.y + 20 * delta_max)); + points.push_back(Vector2(center.x + 20 * delta_max, center.y - delta_max)); + + triangles.push_back(Triangle(p_points.size() + 0, p_points.size() + 1, p_points.size() + 2)); + + for (int i = 0; i < p_points.size(); i++) { + //std::cout << "Traitement du point " << *p << std::endl; + //std::cout << "_triangles contains " << _triangles.size() << " elements" << std::endl; + + Vector<Edge> polygon; + + for (int j = 0; j < triangles.size(); j++) { + if (circum_circle_contains(points, triangles[j], i)) { + triangles.write[j].bad = true; + polygon.push_back(Edge(triangles[j].points[0], triangles[j].points[1])); + polygon.push_back(Edge(triangles[j].points[1], triangles[j].points[2])); + polygon.push_back(Edge(triangles[j].points[2], triangles[j].points[0])); + } + } + + for (int j = 0; j < triangles.size(); j++) { + if (triangles[j].bad) { + triangles.remove(j); + j--; + } + } + + for (int j = 0; j < polygon.size(); j++) { + for (int k = j + 1; k < polygon.size(); k++) { + if (edge_compare(points, polygon[j], polygon[k])) { + polygon.write[j].bad = true; + polygon.write[k].bad = true; + } + } + } + + for (int j = 0; j < polygon.size(); j++) { + + if (polygon[j].bad) { + continue; + } + triangles.push_back(Triangle(polygon[j].edge[0], polygon[j].edge[1], i)); + } + } + + for (int i = 0; i < triangles.size(); i++) { + bool invalid = false; + for (int j = 0; j < 3; j++) { + if (triangles[i].points[j] >= p_points.size()) { + invalid = true; + break; + } + } + if (invalid) { + triangles.remove(i); + i--; + } + } + + return triangles; + } +}; + +#endif // DELAUNAY_H diff --git a/core/math/expression.cpp b/core/math/expression.cpp new file mode 100644 index 0000000000..a161dbddba --- /dev/null +++ b/core/math/expression.cpp @@ -0,0 +1,2136 @@ +#include "expression.h" + +#include "class_db.h" +#include "func_ref.h" +#include "io/marshalls.h" +#include "math_funcs.h" +#include "os/os.h" +#include "reference.h" +#include "variant_parser.h" + +const char *Expression::func_name[Expression::FUNC_MAX] = { + "sin", + "cos", + "tan", + "sinh", + "cosh", + "tanh", + "asin", + "acos", + "atan", + "atan2", + "sqrt", + "fmod", + "fposmod", + "floor", + "ceil", + "round", + "abs", + "sign", + "pow", + "log", + "exp", + "is_nan", + "is_inf", + "ease", + "decimals", + "stepify", + "lerp", + "inverse_lerp", + "range_lerp", + "dectime", + "randomize", + "randi", + "randf", + "rand_range", + "seed", + "rand_seed", + "deg2rad", + "rad2deg", + "linear2db", + "db2linear", + "polar2cartesian", + "cartesian2polar", + "wrapi", + "wrapf", + "max", + "min", + "clamp", + "nearest_po2", + "weakref", + "funcref", + "convert", + "typeof", + "type_exists", + "char", + "str", + "print", + "printerr", + "printraw", + "var2str", + "str2var", + "var2bytes", + "bytes2var", + "color_named", +}; + +Expression::BuiltinFunc Expression::find_function(const String &p_string) { + + for (int i = 0; i < FUNC_MAX; i++) { + if (p_string == func_name[i]) + return BuiltinFunc(i); + } + + return FUNC_MAX; +} + +String Expression::get_func_name(BuiltinFunc p_func) { + + ERR_FAIL_INDEX_V(p_func, FUNC_MAX, String()); + return func_name[p_func]; +} + +int Expression::get_func_argument_count(BuiltinFunc p_func) { + + switch (p_func) { + + case MATH_RANDOMIZE: + case MATH_RAND: + case MATH_RANDF: + return 0; + case MATH_SIN: + case MATH_COS: + case MATH_TAN: + case MATH_SINH: + case MATH_COSH: + case MATH_TANH: + case MATH_ASIN: + case MATH_ACOS: + case MATH_ATAN: + case MATH_SQRT: + case MATH_FLOOR: + case MATH_CEIL: + case MATH_ROUND: + case MATH_ABS: + case MATH_SIGN: + case MATH_LOG: + case MATH_EXP: + case MATH_ISNAN: + case MATH_ISINF: + case MATH_DECIMALS: + case MATH_SEED: + case MATH_RANDSEED: + case MATH_DEG2RAD: + case MATH_RAD2DEG: + case MATH_LINEAR2DB: + case MATH_DB2LINEAR: + case LOGIC_NEAREST_PO2: + case OBJ_WEAKREF: + case TYPE_OF: + case TEXT_CHAR: + case TEXT_STR: + case TEXT_PRINT: + case TEXT_PRINTERR: + case TEXT_PRINTRAW: + case VAR_TO_STR: + case STR_TO_VAR: + case VAR_TO_BYTES: + case BYTES_TO_VAR: + case TYPE_EXISTS: + return 1; + case MATH_ATAN2: + case MATH_FMOD: + case MATH_FPOSMOD: + case MATH_POW: + case MATH_EASE: + case MATH_STEPIFY: + case MATH_RANDOM: + case MATH_POLAR2CARTESIAN: + case MATH_CARTESIAN2POLAR: + case LOGIC_MAX: + case LOGIC_MIN: + case FUNC_FUNCREF: + case TYPE_CONVERT: + case COLORN: + return 2; + case MATH_LERP: + case MATH_INVERSE_LERP: + case MATH_DECTIME: + case MATH_WRAP: + case MATH_WRAPF: + case LOGIC_CLAMP: + return 3; + case MATH_RANGE_LERP: + return 5; + case FUNC_MAX: { + } + } + return 0; +} + +#define VALIDATE_ARG_NUM(m_arg) \ + if (!p_inputs[m_arg]->is_num()) { \ + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; \ + r_error.argument = m_arg; \ + r_error.expected = Variant::REAL; \ + return; \ + } + +void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant *r_return, Variant::CallError &r_error, String &r_error_str) { + + switch (p_func) { + case MATH_SIN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::sin((double)*p_inputs[0]); + } break; + case MATH_COS: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::cos((double)*p_inputs[0]); + } break; + case MATH_TAN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::tan((double)*p_inputs[0]); + } break; + case MATH_SINH: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::sinh((double)*p_inputs[0]); + } break; + case MATH_COSH: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::cosh((double)*p_inputs[0]); + } break; + case MATH_TANH: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::tanh((double)*p_inputs[0]); + } break; + case MATH_ASIN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::asin((double)*p_inputs[0]); + } break; + case MATH_ACOS: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::acos((double)*p_inputs[0]); + } break; + case MATH_ATAN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::atan((double)*p_inputs[0]); + } break; + case MATH_ATAN2: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::atan2((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_SQRT: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::sqrt((double)*p_inputs[0]); + } break; + case MATH_FMOD: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::fmod((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_FPOSMOD: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::fposmod((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_FLOOR: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::floor((double)*p_inputs[0]); + } break; + case MATH_CEIL: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::ceil((double)*p_inputs[0]); + } break; + case MATH_ROUND: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::round((double)*p_inputs[0]); + } break; + case MATH_ABS: { + + if (p_inputs[0]->get_type() == Variant::INT) { + + int64_t i = *p_inputs[0]; + *r_return = ABS(i); + } else if (p_inputs[0]->get_type() == Variant::REAL) { + + real_t r = *p_inputs[0]; + *r_return = Math::abs(r); + } else { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::REAL; + } + } break; + case MATH_SIGN: { + + if (p_inputs[0]->get_type() == Variant::INT) { + + int64_t i = *p_inputs[0]; + *r_return = i < 0 ? -1 : (i > 0 ? +1 : 0); + } else if (p_inputs[0]->get_type() == Variant::REAL) { + + real_t r = *p_inputs[0]; + *r_return = r < 0.0 ? -1.0 : (r > 0.0 ? +1.0 : 0.0); + } else { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::REAL; + } + } break; + case MATH_POW: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::pow((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_LOG: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::log((double)*p_inputs[0]); + } break; + case MATH_EXP: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::exp((double)*p_inputs[0]); + } break; + case MATH_ISNAN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::is_nan((double)*p_inputs[0]); + } break; + case MATH_ISINF: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::is_inf((double)*p_inputs[0]); + } break; + case MATH_EASE: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::ease((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_DECIMALS: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::step_decimals((double)*p_inputs[0]); + } break; + case MATH_STEPIFY: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::stepify((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_LERP: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case MATH_INVERSE_LERP: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::inverse_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case MATH_RANGE_LERP: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + VALIDATE_ARG_NUM(3); + VALIDATE_ARG_NUM(4); + *r_return = Math::range_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2], (double)*p_inputs[3], (double)*p_inputs[4]); + } break; + case MATH_DECTIME: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::dectime((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case MATH_RANDOMIZE: { + Math::randomize(); + + } break; + case MATH_RAND: { + *r_return = Math::rand(); + } break; + case MATH_RANDF: { + *r_return = Math::randf(); + } break; + case MATH_RANDOM: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::random((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_SEED: { + + VALIDATE_ARG_NUM(0); + uint64_t seed = *p_inputs[0]; + Math::seed(seed); + + } break; + case MATH_RANDSEED: { + + VALIDATE_ARG_NUM(0); + uint64_t seed = *p_inputs[0]; + int ret = Math::rand_from_seed(&seed); + Array reta; + reta.push_back(ret); + reta.push_back(seed); + *r_return = reta; + + } break; + case MATH_DEG2RAD: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::deg2rad((double)*p_inputs[0]); + } break; + case MATH_RAD2DEG: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::rad2deg((double)*p_inputs[0]); + } break; + case MATH_LINEAR2DB: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::linear2db((double)*p_inputs[0]); + } break; + case MATH_DB2LINEAR: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::db2linear((double)*p_inputs[0]); + } break; + case MATH_POLAR2CARTESIAN: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + double r = *p_inputs[0]; + double th = *p_inputs[1]; + *r_return = Vector2(r * Math::cos(th), r * Math::sin(th)); + } break; + case MATH_CARTESIAN2POLAR: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + double x = *p_inputs[0]; + double y = *p_inputs[1]; + *r_return = Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x)); + } break; + case MATH_WRAP: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::wrapi((int64_t)*p_inputs[0], (int64_t)*p_inputs[1], (int64_t)*p_inputs[2]); + } break; + case MATH_WRAPF: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::wrapf((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case LOGIC_MAX: { + + if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT) { + + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + *r_return = MAX(a, b); + } else { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; + + *r_return = MAX(a, b); + } + + } break; + case LOGIC_MIN: { + + if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT) { + + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + *r_return = MIN(a, b); + } else { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; + + *r_return = MIN(a, b); + } + } break; + case LOGIC_CLAMP: { + + if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT && p_inputs[2]->get_type() == Variant::INT) { + + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + int64_t c = *p_inputs[2]; + *r_return = CLAMP(a, b, c); + } else { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; + real_t c = *p_inputs[2]; + + *r_return = CLAMP(a, b, c); + } + } break; + case LOGIC_NEAREST_PO2: { + + VALIDATE_ARG_NUM(0); + int64_t num = *p_inputs[0]; + *r_return = next_power_of_2(num); + } break; + case OBJ_WEAKREF: { + + if (p_inputs[0]->get_type() != Variant::OBJECT) { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + + return; + } + + if (p_inputs[0]->is_ref()) { + + REF r = *p_inputs[0]; + if (!r.is_valid()) { + + return; + } + + Ref<WeakRef> wref = memnew(WeakRef); + wref->set_ref(r); + *r_return = wref; + } else { + Object *obj = *p_inputs[0]; + if (!obj) { + + return; + } + Ref<WeakRef> wref = memnew(WeakRef); + wref->set_obj(obj); + *r_return = wref; + } + + } break; + case FUNC_FUNCREF: { + + if (p_inputs[0]->get_type() != Variant::OBJECT) { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + + return; + } + if (p_inputs[1]->get_type() != Variant::STRING && p_inputs[1]->get_type() != Variant::NODE_PATH) { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::STRING; + + return; + } + + Ref<FuncRef> fr = memnew(FuncRef); + + fr->set_instance(*p_inputs[0]); + fr->set_function(*p_inputs[1]); + + *r_return = fr; + + } break; + case TYPE_CONVERT: { + + VALIDATE_ARG_NUM(1); + int type = *p_inputs[1]; + if (type < 0 || type >= Variant::VARIANT_MAX) { + + r_error_str = RTR("Invalid type argument to convert(), use TYPE_* constants."); + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::INT; + return; + + } else { + + *r_return = Variant::construct(Variant::Type(type), p_inputs, 1, r_error); + } + } break; + case TYPE_OF: { + + *r_return = p_inputs[0]->get_type(); + + } break; + case TYPE_EXISTS: { + + *r_return = ClassDB::class_exists(*p_inputs[0]); + + } break; + case TEXT_CHAR: { + + CharType result[2] = { *p_inputs[0], 0 }; + + *r_return = String(result); + + } break; + case TEXT_STR: { + + String str = *p_inputs[0]; + + *r_return = str; + + } break; + case TEXT_PRINT: { + + String str = *p_inputs[0]; + print_line(str); + + } break; + + case TEXT_PRINTERR: { + + String str = *p_inputs[0]; + + //str+="\n"; + print_error(str); + + } break; + case TEXT_PRINTRAW: { + String str = *p_inputs[0]; + + //str+="\n"; + OS::get_singleton()->print("%s", str.utf8().get_data()); + + } break; + case VAR_TO_STR: { + + String vars; + VariantWriter::write_to_string(*p_inputs[0], vars); + *r_return = vars; + } break; + case STR_TO_VAR: { + + if (p_inputs[0]->get_type() != Variant::STRING) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::STRING; + + return; + } + + VariantParser::StreamString ss; + ss.s = *p_inputs[0]; + + String errs; + int line; + Error err = VariantParser::parse(&ss, *r_return, errs, line); + + if (err != OK) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::STRING; + *r_return = "Parse error at line " + itos(line) + ": " + errs; + return; + } + + } break; + case VAR_TO_BYTES: { + + PoolByteArray barr; + int len; + Error err = encode_variant(*p_inputs[0], NULL, len); + if (err) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::NIL; + r_error_str = "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."; + return; + } + + barr.resize(len); + { + PoolByteArray::Write w = barr.write(); + encode_variant(*p_inputs[0], w.ptr(), len); + } + *r_return = barr; + } break; + case BYTES_TO_VAR: { + + if (p_inputs[0]->get_type() != Variant::POOL_BYTE_ARRAY) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::POOL_BYTE_ARRAY; + + return; + } + + PoolByteArray varr = *p_inputs[0]; + Variant ret; + { + PoolByteArray::Read r = varr.read(); + Error err = decode_variant(ret, r.ptr(), varr.size(), NULL); + if (err != OK) { + r_error_str = RTR("Not enough bytes for decoding bytes, or invalid format."); + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::POOL_BYTE_ARRAY; + return; + } + } + + *r_return = ret; + + } break; + case COLORN: { + + VALIDATE_ARG_NUM(1); + + Color color = Color::named(*p_inputs[0]); + color.a = *p_inputs[1]; + + *r_return = String(color); + + } break; + default: {} + } +} + +//////// + +Error Expression::_get_token(Token &r_token) { + + while (true) { +#define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++]) + + CharType cchar = GET_CHAR(); + if (cchar == 0) { + r_token.type = TK_EOF; + return OK; + } + + switch (cchar) { + + case 0: { + r_token.type = TK_EOF; + return OK; + } break; + case '{': { + + r_token.type = TK_CURLY_BRACKET_OPEN; + return OK; + }; + case '}': { + + r_token.type = TK_CURLY_BRACKET_CLOSE; + return OK; + }; + case '[': { + + r_token.type = TK_BRACKET_OPEN; + return OK; + }; + case ']': { + + r_token.type = TK_BRACKET_CLOSE; + return OK; + }; + case '(': { + + r_token.type = TK_PARENTHESIS_OPEN; + return OK; + }; + case ')': { + + r_token.type = TK_PARENTHESIS_CLOSE; + return OK; + }; + case ',': { + + r_token.type = TK_COMMA; + return OK; + }; + case ':': { + + r_token.type = TK_COLON; + return OK; + }; + case '.': { + + r_token.type = TK_PERIOD; + return OK; + }; + case '$': { + + r_token.type = TK_INPUT; + int index = 0; + do { + if (expression[str_ofs] < '0' || expression[str_ofs] > '9') { + _set_error("Expected number after '$'"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + index *= 10; + index += expression[str_ofs] - '0'; + str_ofs++; + + } while (expression[str_ofs] >= '0' && expression[str_ofs] <= '9'); + + r_token.value = index; + return OK; + }; + case '=': { + + cchar = GET_CHAR(); + if (cchar == '=') { + r_token.type = TK_OP_EQUAL; + } else { + _set_error("Expected '='"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + return OK; + }; + case '!': { + + if (expression[str_ofs] == '=') { + r_token.type = TK_OP_NOT_EQUAL; + str_ofs++; + } else { + r_token.type = TK_OP_NOT; + } + return OK; + }; + case '>': { + + if (expression[str_ofs] == '=') { + r_token.type = TK_OP_GREATER_EQUAL; + str_ofs++; + } else if (expression[str_ofs] == '>') { + r_token.type = TK_OP_SHIFT_RIGHT; + str_ofs++; + } else { + r_token.type = TK_OP_GREATER; + } + return OK; + }; + case '<': { + + if (expression[str_ofs] == '=') { + r_token.type = TK_OP_LESS_EQUAL; + str_ofs++; + } else if (expression[str_ofs] == '<') { + r_token.type = TK_OP_SHIFT_LEFT; + str_ofs++; + } else { + r_token.type = TK_OP_LESS; + } + return OK; + }; + case '+': { + r_token.type = TK_OP_ADD; + return OK; + }; + case '-': { + r_token.type = TK_OP_SUB; + return OK; + }; + case '/': { + r_token.type = TK_OP_DIV; + return OK; + }; + case '*': { + r_token.type = TK_OP_MUL; + return OK; + }; + case '%': { + r_token.type = TK_OP_MOD; + return OK; + }; + case '&': { + + if (expression[str_ofs] == '&') { + r_token.type = TK_OP_AND; + str_ofs++; + } else { + r_token.type = TK_OP_BIT_AND; + } + return OK; + }; + case '|': { + + if (expression[str_ofs] == '|') { + r_token.type = TK_OP_OR; + str_ofs++; + } else { + r_token.type = TK_OP_BIT_OR; + } + return OK; + }; + case '^': { + + r_token.type = TK_OP_BIT_XOR; + + return OK; + }; + case '~': { + + r_token.type = TK_OP_BIT_INVERT; + + return OK; + }; + case '"': { + + String str; + while (true) { + + CharType ch = GET_CHAR(); + + if (ch == 0) { + _set_error("Unterminated String"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } else if (ch == '"') { + break; + } else if (ch == '\\') { + //escaped characters... + + CharType next = GET_CHAR(); + if (next == 0) { + _set_error("Unterminated String"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + CharType res = 0; + + switch (next) { + + case 'b': res = 8; break; + case 't': res = 9; break; + case 'n': res = 10; break; + case 'f': res = 12; break; + case 'r': res = 13; break; + case 'u': { + //hexnumbarh - oct is deprecated + + for (int j = 0; j < 4; j++) { + CharType c = GET_CHAR(); + + if (c == 0) { + _set_error("Unterminated String"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + + _set_error("Malformed hex constant in string"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + CharType v; + if (c >= '0' && c <= '9') { + v = c - '0'; + } else if (c >= 'a' && c <= 'f') { + v = c - 'a'; + v += 10; + } else if (c >= 'A' && c <= 'F') { + v = c - 'A'; + v += 10; + } else { + ERR_PRINT("BUG"); + v = 0; + } + + res <<= 4; + res |= v; + } + + } break; + //case '\"': res='\"'; break; + //case '\\': res='\\'; break; + //case '/': res='/'; break; + default: { + res = next; + //r_err_str="Invalid escape sequence"; + //return ERR_PARSE_ERROR; + } break; + } + + str += res; + + } else { + str += ch; + } + } + + r_token.type = TK_CONSTANT; + r_token.value = str; + return OK; + + } break; + default: { + + if (cchar <= 32) { + break; + } + + if (cchar >= '0' && cchar <= '9') { + //a number + + String num; +#define READING_SIGN 0 +#define READING_INT 1 +#define READING_DEC 2 +#define READING_EXP 3 +#define READING_DONE 4 + int reading = READING_INT; + + CharType c = cchar; + bool exp_sign = false; + bool exp_beg = false; + bool is_float = false; + + while (true) { + + switch (reading) { + case READING_INT: { + + if (c >= '0' && c <= '9') { + //pass + } else if (c == '.') { + reading = READING_DEC; + is_float = true; + } else if (c == 'e') { + reading = READING_EXP; + } else { + reading = READING_DONE; + } + + } break; + case READING_DEC: { + + if (c >= '0' && c <= '9') { + + } else if (c == 'e') { + reading = READING_EXP; + + } else { + reading = READING_DONE; + } + + } break; + case READING_EXP: { + + if (c >= '0' && c <= '9') { + exp_beg = true; + + } else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) { + if (c == '-') + is_float = true; + exp_sign = true; + + } else { + reading = READING_DONE; + } + } break; + } + + if (reading == READING_DONE) + break; + num += String::chr(c); + c = GET_CHAR(); + } + + str_ofs--; + + r_token.type = TK_CONSTANT; + + if (is_float) + r_token.value = num.to_double(); + else + r_token.value = num.to_int(); + return OK; + + } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') { + + String id; + bool first = true; + + while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && cchar >= '0' && cchar <= '9')) { + + id += String::chr(cchar); + cchar = GET_CHAR(); + first = false; + } + + str_ofs--; //go back one + + if (id == "in") { + r_token.type = TK_OP_IN; + } else if (id == "null") { + r_token.type = TK_CONSTANT; + r_token.value = Variant(); + } else if (id == "true") { + r_token.type = TK_CONSTANT; + r_token.value = true; + } else if (id == "false") { + r_token.type = TK_CONSTANT; + r_token.value = false; + } else if (id == "PI") { + r_token.type = TK_CONSTANT; + r_token.value = Math_PI; + } else if (id == "TAU") { + r_token.type = TK_CONSTANT; + r_token.value = Math_TAU; + } else if (id == "INF") { + r_token.type = TK_CONSTANT; + r_token.value = Math_INF; + } else if (id == "NAN") { + r_token.type = TK_CONSTANT; + r_token.value = Math_NAN; + } else if (id == "not") { + r_token.type = TK_OP_NOT; + } else if (id == "or") { + r_token.type = TK_OP_OR; + } else if (id == "and") { + r_token.type = TK_OP_AND; + } else if (id == "self") { + r_token.type = TK_SELF; + } else { + + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (id == Variant::get_type_name(Variant::Type(i))) { + r_token.type = TK_BASIC_TYPE; + r_token.value = i; + return OK; + } + } + + BuiltinFunc bifunc = find_function(id); + if (bifunc != FUNC_MAX) { + r_token.type = TK_BUILTIN_FUNC; + r_token.value = bifunc; + return OK; + } + + r_token.type = TK_IDENTIFIER; + r_token.value = id; + } + + return OK; + } else { + _set_error("Unexpected character."); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + } + } + } + + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; +} + +const char *Expression::token_name[TK_MAX] = { + "CURLY BRACKET OPEN", + "CURLY BRACKET CLOSE", + "BRACKET OPEN", + "BRACKET CLOSE", + "PARENTHESIS OPEN", + "PARENTHESIS CLOSE", + "IDENTIFIER", + "BUILTIN FUNC", + "SELF", + "CONSTANT", + "BASIC TYPE", + "COLON", + "COMMA", + "PERIOD", + "OP IN", + "OP EQUAL", + "OP NOT EQUAL", + "OP LESS", + "OP LESS EQUAL", + "OP GREATER", + "OP GREATER EQUAL", + "OP AND", + "OP OR", + "OP NOT", + "OP ADD", + "OP SUB", + "OP MUL", + "OP DIV", + "OP MOD", + "OP SHIFT LEFT", + "OP SHIFT RIGHT", + "OP BIT AND", + "OP BIT OR", + "OP BIT XOR", + "OP BIT INVERT", + "OP INPUT", + "EOF", + "ERROR" +}; + +Expression::ENode *Expression::_parse_expression() { + + Vector<ExpressionNode> expression; + + while (true) { + //keep appending stuff to expression + ENode *expr = NULL; + + Token tk; + _get_token(tk); + if (error_set) + return NULL; + + switch (tk.type) { + case TK_CURLY_BRACKET_OPEN: { + //a dictionary + DictionaryNode *dn = alloc_node<DictionaryNode>(); + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_CURLY_BRACKET_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + dn->dict.push_back(expr); + + _get_token(tk); + if (tk.type != TK_COLON) { + _set_error("Expected ':'"); + return NULL; + } + + expr = _parse_expression(); + if (!expr) + return NULL; + + dn->dict.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_CURLY_BRACKET_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or '}'"); + } + } + + expr = dn; + } break; + case TK_BRACKET_OPEN: { + //an array + + ArrayNode *an = alloc_node<ArrayNode>(); + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_BRACKET_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + an->array.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_BRACKET_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ']'"); + } + } + + expr = an; + } break; + case TK_PARENTHESIS_OPEN: { + //a suexpression + ENode *e = _parse_expression(); + if (error_set) + return NULL; + _get_token(tk); + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')'"); + return NULL; + } + + expr = e; + + } break; + case TK_IDENTIFIER: { + + String identifier = tk.value; + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_OPEN) { + //function call + CallNode *func_call = alloc_node<CallNode>(); + func_call->method = identifier; + SelfNode *self_node = alloc_node<SelfNode>(); + func_call->base = self_node; + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + func_call->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + expr = func_call; + } else { + //named indexing + str_ofs = cofs; + + int input_index = -1; + for (int i = 0; i < input_names.size(); i++) { + if (input_names[i] == identifier) { + input_index = i; + break; + } + } + + if (input_index != -1) { + InputNode *input = alloc_node<InputNode>(); + input->index = input_index; + expr = input; + } else { + + NamedIndexNode *index = alloc_node<NamedIndexNode>(); + SelfNode *self_node = alloc_node<SelfNode>(); + index->base = self_node; + index->name = identifier; + expr = index; + } + } + } break; + case TK_INPUT: { + + InputNode *input = alloc_node<InputNode>(); + input->index = tk.value; + expr = input; + } break; + case TK_SELF: { + + SelfNode *self = alloc_node<SelfNode>(); + expr = self; + } break; + case TK_CONSTANT: { + ConstantNode *constant = alloc_node<ConstantNode>(); + constant->value = tk.value; + expr = constant; + } break; + case TK_BASIC_TYPE: { + //constructor.. + + Variant::Type bt = Variant::Type(int(tk.value)); + _get_token(tk); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '('"); + return NULL; + } + + ConstructorNode *constructor = alloc_node<ConstructorNode>(); + constructor->data_type = bt; + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + constructor->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + expr = constructor; + + } break; + case TK_BUILTIN_FUNC: { + //builtin function + + _get_token(tk); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '('"); + return NULL; + } + + BuiltinFuncNode *bifunc = alloc_node<BuiltinFuncNode>(); + bifunc->func = BuiltinFunc(int(tk.value)); + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + bifunc->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + int expected_args = get_func_argument_count(bifunc->func); + if (bifunc->arguments.size() != expected_args) { + _set_error("Builtin func '" + get_func_name(bifunc->func) + "' expects " + itos(expected_args) + " arguments."); + } + + expr = bifunc; + + } break; + case TK_OP_SUB: { + + ExpressionNode e; + e.is_op = true; + e.op = Variant::OP_NEGATE; + expression.push_back(e); + continue; + } break; + case TK_OP_NOT: { + + ExpressionNode e; + e.is_op = true; + e.op = Variant::OP_NOT; + expression.push_back(e); + continue; + } break; + + default: { + _set_error("Expected expression."); + return NULL; + } break; + } + + //before going to operators, must check indexing! + + while (true) { + int cofs2 = str_ofs; + _get_token(tk); + if (error_set) + return NULL; + + bool done = false; + + switch (tk.type) { + case TK_BRACKET_OPEN: { + //value indexing + + IndexNode *index = alloc_node<IndexNode>(); + index->base = expr; + + ENode *what = _parse_expression(); + if (!what) + return NULL; + + index->index = what; + + _get_token(tk); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']' at end of index."); + return NULL; + } + expr = index; + + } break; + case TK_PERIOD: { + //named indexing or function call + _get_token(tk); + if (tk.type != TK_IDENTIFIER) { + _set_error("Expected identifier after '.'"); + return NULL; + } + + StringName identifier = tk.value; + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_OPEN) { + //function call + CallNode *func_call = alloc_node<CallNode>(); + func_call->method = identifier; + func_call->base = expr; + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + func_call->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + expr = func_call; + } else { + //named indexing + str_ofs = cofs; + + NamedIndexNode *index = alloc_node<NamedIndexNode>(); + index->base = expr; + index->name = identifier; + expr = index; + } + + } break; + default: { + str_ofs = cofs2; + done = true; + } break; + } + + if (done) + break; + } + + //push expression + { + ExpressionNode e; + e.is_op = false; + e.node = expr; + expression.push_back(e); + } + + //ok finally look for an operator + + int cofs = str_ofs; + _get_token(tk); + if (error_set) + return NULL; + + Variant::Operator op = Variant::OP_MAX; + + switch (tk.type) { + case TK_OP_IN: op = Variant::OP_IN; break; + case TK_OP_EQUAL: op = Variant::OP_EQUAL; break; + case TK_OP_NOT_EQUAL: op = Variant::OP_NOT_EQUAL; break; + case TK_OP_LESS: op = Variant::OP_LESS; break; + case TK_OP_LESS_EQUAL: op = Variant::OP_LESS_EQUAL; break; + case TK_OP_GREATER: op = Variant::OP_GREATER; break; + case TK_OP_GREATER_EQUAL: op = Variant::OP_GREATER_EQUAL; break; + case TK_OP_AND: op = Variant::OP_AND; break; + case TK_OP_OR: op = Variant::OP_OR; break; + case TK_OP_NOT: op = Variant::OP_NOT; break; + case TK_OP_ADD: op = Variant::OP_ADD; break; + case TK_OP_SUB: op = Variant::OP_SUBTRACT; break; + case TK_OP_MUL: op = Variant::OP_MULTIPLY; break; + case TK_OP_DIV: op = Variant::OP_DIVIDE; break; + case TK_OP_MOD: op = Variant::OP_MODULE; break; + case TK_OP_SHIFT_LEFT: op = Variant::OP_SHIFT_LEFT; break; + case TK_OP_SHIFT_RIGHT: op = Variant::OP_SHIFT_RIGHT; break; + case TK_OP_BIT_AND: op = Variant::OP_BIT_AND; break; + case TK_OP_BIT_OR: op = Variant::OP_BIT_OR; break; + case TK_OP_BIT_XOR: op = Variant::OP_BIT_XOR; break; + case TK_OP_BIT_INVERT: op = Variant::OP_BIT_NEGATE; break; + default: {}; + } + + if (op == Variant::OP_MAX) { //stop appending stuff + str_ofs = cofs; + break; + } + + //push operator and go on + { + ExpressionNode e; + e.is_op = true; + e.op = op; + expression.push_back(e); + } + } + + /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */ + + while (expression.size() > 1) { + + int next_op = -1; + int min_priority = 0xFFFFF; + bool is_unary = false; + + for (int i = 0; i < expression.size(); i++) { + + if (!expression[i].is_op) { + + continue; + } + + int priority; + + bool unary = false; + + switch (expression[i].op) { + + case Variant::OP_BIT_NEGATE: + priority = 0; + unary = true; + break; + case Variant::OP_NEGATE: + priority = 1; + unary = true; + break; + + case Variant::OP_MULTIPLY: priority = 2; break; + case Variant::OP_DIVIDE: priority = 2; break; + case Variant::OP_MODULE: priority = 2; break; + + case Variant::OP_ADD: priority = 3; break; + case Variant::OP_SUBTRACT: priority = 3; break; + + case Variant::OP_SHIFT_LEFT: priority = 4; break; + case Variant::OP_SHIFT_RIGHT: priority = 4; break; + + case Variant::OP_BIT_AND: priority = 5; break; + case Variant::OP_BIT_XOR: priority = 6; break; + case Variant::OP_BIT_OR: priority = 7; break; + + case Variant::OP_LESS: priority = 8; break; + case Variant::OP_LESS_EQUAL: priority = 8; break; + case Variant::OP_GREATER: priority = 8; break; + case Variant::OP_GREATER_EQUAL: priority = 8; break; + + case Variant::OP_EQUAL: priority = 8; break; + case Variant::OP_NOT_EQUAL: priority = 8; break; + + case Variant::OP_IN: priority = 10; break; + + case Variant::OP_NOT: + priority = 11; + unary = true; + break; + case Variant::OP_AND: priority = 12; break; + case Variant::OP_OR: priority = 13; break; + + default: { + _set_error("Parser bug, invalid operator in expression: " + itos(expression[i].op)); + return NULL; + } + } + + if (priority < min_priority) { + // < is used for left to right (default) + // <= is used for right to left + + next_op = i; + min_priority = priority; + is_unary = unary; + } + } + + if (next_op == -1) { + + _set_error("Yet another parser bug...."); + ERR_FAIL_COND_V(next_op == -1, NULL); + } + + // OK! create operator.. + if (is_unary) { + + int expr_pos = next_op; + while (expression[expr_pos].is_op) { + + expr_pos++; + if (expr_pos == expression.size()) { + //can happen.. + _set_error("Unexpected end of expression..."); + return NULL; + } + } + + //consecutively do unary opeators + for (int i = expr_pos - 1; i >= next_op; i--) { + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = expression[i].op; + op->nodes[0] = expression[i + 1].node; + op->nodes[1] = NULL; + expression.write[i].is_op = false; + expression.write[i].node = op; + expression.remove(i + 1); + } + + } else { + + if (next_op < 1 || next_op >= (expression.size() - 1)) { + _set_error("Parser bug..."); + ERR_FAIL_V(NULL); + } + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = expression[next_op].op; + + if (expression[next_op - 1].is_op) { + + _set_error("Parser bug..."); + ERR_FAIL_V(NULL); + } + + if (expression[next_op + 1].is_op) { + // this is not invalid and can really appear + // but it becomes invalid anyway because no binary op + // can be followed by a unary op in a valid combination, + // due to how precedence works, unaries will always disappear first + + _set_error("Unexpected two consecutive operators."); + return NULL; + } + + op->nodes[0] = expression[next_op - 1].node; //expression goes as left + op->nodes[1] = expression[next_op + 1].node; //next expression goes as right + + //replace all 3 nodes by this operator and make it an expression + expression.write[next_op - 1].node = op; + expression.remove(next_op); + expression.remove(next_op); + } + } + + return expression[0].node; +} + +bool Expression::_compile_expression() { + + if (!expression_dirty) + return error_set; + + if (nodes) { + memdelete(nodes); + nodes = NULL; + root = NULL; + } + + error_str = String(); + error_set = false; + str_ofs = 0; + + root = _parse_expression(); + + if (error_set) { + root = NULL; + if (nodes) { + memdelete(nodes); + } + nodes = NULL; + return true; + } + + expression_dirty = false; + return false; +} + +bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str) { + + switch (p_node->type) { + case Expression::ENode::TYPE_INPUT: { + + const Expression::InputNode *in = static_cast<const Expression::InputNode *>(p_node); + if (in->index < 0 || in->index >= p_inputs.size()) { + r_error_str = vformat(RTR("Invalid input %i (not passed) in expression"), in->index); + return true; + } + r_ret = p_inputs[in->index]; + } break; + case Expression::ENode::TYPE_CONSTANT: { + + const Expression::ConstantNode *c = static_cast<const Expression::ConstantNode *>(p_node); + r_ret = c->value; + + } break; + case Expression::ENode::TYPE_SELF: { + + if (!p_instance) { + r_error_str = RTR("self can't be used because instance is null (not passed)"); + return true; + } + r_ret = p_instance; + } break; + case Expression::ENode::TYPE_OPERATOR: { + + const Expression::OperatorNode *op = static_cast<const Expression::OperatorNode *>(p_node); + + Variant a; + bool ret = _execute(p_inputs, p_instance, op->nodes[0], a, r_error_str); + if (ret) + return true; + + Variant b; + + if (op->nodes[1]) { + bool ret = _execute(p_inputs, p_instance, op->nodes[1], b, r_error_str); + if (ret) + return true; + } + + bool valid = true; + Variant::evaluate(op->op, a, b, r_ret, valid); + if (!valid) { + r_error_str = vformat(RTR("Invalid operands to operator %s, %s and %s."), Variant::get_operator_name(op->op), Variant::get_type_name(a.get_type()), Variant::get_type_name(b.get_type())); + return true; + } + + } break; + case Expression::ENode::TYPE_INDEX: { + + const Expression::IndexNode *index = static_cast<const Expression::IndexNode *>(p_node); + + Variant base; + bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str); + if (ret) + return true; + + Variant idx; + + ret = _execute(p_inputs, p_instance, index->index, idx, r_error_str); + if (ret) + return true; + + bool valid; + r_ret = base.get(idx, &valid); + if (!valid) { + r_error_str = vformat(RTR("Invalid index of type %s for base type %s"), Variant::get_type_name(idx.get_type()), Variant::get_type_name(base.get_type())); + return true; + } + + } break; + case Expression::ENode::TYPE_NAMED_INDEX: { + + const Expression::NamedIndexNode *index = static_cast<const Expression::NamedIndexNode *>(p_node); + + Variant base; + bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str); + if (ret) + return true; + + bool valid; + r_ret = base.get_named(index->name, &valid); + if (!valid) { + r_error_str = vformat(RTR("Invalid named index '%s' for base type "), String(index->name), Variant::get_type_name(base.get_type())); + return true; + } + + } break; + case Expression::ENode::TYPE_ARRAY: { + const Expression::ArrayNode *array = static_cast<const Expression::ArrayNode *>(p_node); + + Array arr; + arr.resize(array->array.size()); + for (int i = 0; i < array->array.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, array->array[i], value, r_error_str); + + if (ret) + return true; + arr[i] = value; + } + + r_ret = arr; + + } break; + case Expression::ENode::TYPE_DICTIONARY: { + const Expression::DictionaryNode *dictionary = static_cast<const Expression::DictionaryNode *>(p_node); + + Dictionary d; + for (int i = 0; i < dictionary->dict.size(); i += 2) { + + Variant key; + bool ret = _execute(p_inputs, p_instance, dictionary->dict[i + 0], key, r_error_str); + + if (ret) + return true; + + Variant value; + ret = _execute(p_inputs, p_instance, dictionary->dict[i + 1], value, r_error_str); + if (ret) + return true; + + d[key] = value; + } + + r_ret = d; + } break; + case Expression::ENode::TYPE_CONSTRUCTOR: { + + const Expression::ConstructorNode *constructor = static_cast<const Expression::ConstructorNode *>(p_node); + + Vector<Variant> arr; + Vector<const Variant *> argp; + arr.resize(constructor->arguments.size()); + argp.resize(constructor->arguments.size()); + + for (int i = 0; i < constructor->arguments.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, constructor->arguments[i], value, r_error_str); + + if (ret) + return true; + arr.write[i] = value; + argp.write[i] = &arr[i]; + } + + Variant::CallError ce; + r_ret = Variant::construct(constructor->data_type, (const Variant **)argp.ptr(), argp.size(), ce); + + if (ce.error != Variant::CallError::CALL_OK) { + r_error_str = vformat(RTR("Invalid arguments to construct '%s'"), Variant::get_type_name(constructor->data_type)); + return true; + } + + } break; + case Expression::ENode::TYPE_BUILTIN_FUNC: { + + const Expression::BuiltinFuncNode *bifunc = static_cast<const Expression::BuiltinFuncNode *>(p_node); + + Vector<Variant> arr; + Vector<const Variant *> argp; + arr.resize(bifunc->arguments.size()); + argp.resize(bifunc->arguments.size()); + + for (int i = 0; i < bifunc->arguments.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, bifunc->arguments[i], value, r_error_str); + if (ret) + return true; + arr.write[i] = value; + argp.write[i] = &arr[i]; + } + + Variant::CallError ce; + exec_func(bifunc->func, (const Variant **)argp.ptr(), &r_ret, ce, r_error_str); + + if (ce.error != Variant::CallError::CALL_OK) { + r_error_str = "Builtin Call Failed. " + r_error_str; + return true; + } + + } break; + case Expression::ENode::TYPE_CALL: { + + const Expression::CallNode *call = static_cast<const Expression::CallNode *>(p_node); + + Variant base; + bool ret = _execute(p_inputs, p_instance, call->base, base, r_error_str); + + if (ret) + return true; + + Vector<Variant> arr; + Vector<const Variant *> argp; + arr.resize(call->arguments.size()); + argp.resize(call->arguments.size()); + + for (int i = 0; i < call->arguments.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, call->arguments[i], value, r_error_str); + + if (ret) + return true; + arr.write[i] = value; + argp.write[i] = &arr[i]; + } + + Variant::CallError ce; + r_ret = base.call(call->method, (const Variant **)argp.ptr(), argp.size(), ce); + + if (ce.error != Variant::CallError::CALL_OK) { + r_error_str = vformat(RTR("On call to '%s':"), String(call->method)); + return true; + } + + } break; + } + return false; +} + +Error Expression::parse(const String &p_expression, const Vector<String> &p_input_names) { + + if (nodes) { + memdelete(nodes); + nodes = NULL; + root = NULL; + } + + error_str = String(); + error_set = false; + str_ofs = 0; + input_names = p_input_names; + + expression = p_expression; + root = _parse_expression(); + + if (error_set) { + root = NULL; + if (nodes) { + memdelete(nodes); + } + nodes = NULL; + return ERR_INVALID_PARAMETER; + } + + return OK; +} + +Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) { + + execution_error = false; + Variant output; + String error_txt; + bool err = _execute(p_inputs, p_base, root, output, error_txt); + if (err) { + execution_error = true; + error_str = error_txt; + if (p_show_error) { + ERR_EXPLAIN(error_str); + ERR_FAIL_V(Variant()); + } + } + + return output; +} + +bool Expression::has_execute_failed() const { + return execution_error; +} + +String Expression::get_error_text() const { + return error_str; +} + +void Expression::_bind_methods() { + + ClassDB::bind_method(D_METHOD("parse", "expression", "input_names"), &Expression::parse, DEFVAL(Vector<String>())); + ClassDB::bind_method(D_METHOD("execute", "inputs", "base_instance", "show_error"), &Expression::execute, DEFVAL(Array()), DEFVAL(Variant()), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("has_execute_failed"), &Expression::has_execute_failed); + ClassDB::bind_method(D_METHOD("get_error_text"), &Expression::get_error_text); +} + +Expression::Expression() { + output_type = Variant::NIL; + error_set = true; + root = NULL; + nodes = NULL; + sequenced = false; + execution_error = false; +} + +Expression::~Expression() { + + if (nodes) { + memdelete(nodes); + } +} diff --git a/core/math/expression.h b/core/math/expression.h new file mode 100644 index 0000000000..7a7639cf0b --- /dev/null +++ b/core/math/expression.h @@ -0,0 +1,325 @@ +#ifndef EXPRESSION_H +#define EXPRESSION_H + +#include "core/reference.h" + +class Expression : public Reference { + GDCLASS(Expression, Reference) +public: + enum BuiltinFunc { + MATH_SIN, + MATH_COS, + MATH_TAN, + MATH_SINH, + MATH_COSH, + MATH_TANH, + MATH_ASIN, + MATH_ACOS, + MATH_ATAN, + MATH_ATAN2, + MATH_SQRT, + MATH_FMOD, + MATH_FPOSMOD, + MATH_FLOOR, + MATH_CEIL, + MATH_ROUND, + MATH_ABS, + MATH_SIGN, + MATH_POW, + MATH_LOG, + MATH_EXP, + MATH_ISNAN, + MATH_ISINF, + MATH_EASE, + MATH_DECIMALS, + MATH_STEPIFY, + MATH_LERP, + MATH_INVERSE_LERP, + MATH_RANGE_LERP, + MATH_DECTIME, + MATH_RANDOMIZE, + MATH_RAND, + MATH_RANDF, + MATH_RANDOM, + MATH_SEED, + MATH_RANDSEED, + MATH_DEG2RAD, + MATH_RAD2DEG, + MATH_LINEAR2DB, + MATH_DB2LINEAR, + MATH_POLAR2CARTESIAN, + MATH_CARTESIAN2POLAR, + MATH_WRAP, + MATH_WRAPF, + LOGIC_MAX, + LOGIC_MIN, + LOGIC_CLAMP, + LOGIC_NEAREST_PO2, + OBJ_WEAKREF, + FUNC_FUNCREF, + TYPE_CONVERT, + TYPE_OF, + TYPE_EXISTS, + TEXT_CHAR, + TEXT_STR, + TEXT_PRINT, + TEXT_PRINTERR, + TEXT_PRINTRAW, + VAR_TO_STR, + STR_TO_VAR, + VAR_TO_BYTES, + BYTES_TO_VAR, + COLORN, + FUNC_MAX + }; + + static int get_func_argument_count(BuiltinFunc p_func); + static String get_func_name(BuiltinFunc p_func); + static void exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant *r_return, Variant::CallError &r_error, String &r_error_str); + static BuiltinFunc find_function(const String &p_string); + +private: + static const char *func_name[FUNC_MAX]; + + struct Input { + + Variant::Type type; + String name; + + Input() { type = Variant::NIL; } + }; + + Vector<Input> inputs; + Variant::Type output_type; + + String expression; + + bool sequenced; + int str_ofs; + bool expression_dirty; + + bool _compile_expression(); + + enum TokenType { + TK_CURLY_BRACKET_OPEN, + TK_CURLY_BRACKET_CLOSE, + TK_BRACKET_OPEN, + TK_BRACKET_CLOSE, + TK_PARENTHESIS_OPEN, + TK_PARENTHESIS_CLOSE, + TK_IDENTIFIER, + TK_BUILTIN_FUNC, + TK_SELF, + TK_CONSTANT, + TK_BASIC_TYPE, + TK_COLON, + TK_COMMA, + TK_PERIOD, + TK_OP_IN, + TK_OP_EQUAL, + TK_OP_NOT_EQUAL, + TK_OP_LESS, + TK_OP_LESS_EQUAL, + TK_OP_GREATER, + TK_OP_GREATER_EQUAL, + TK_OP_AND, + TK_OP_OR, + TK_OP_NOT, + TK_OP_ADD, + TK_OP_SUB, + TK_OP_MUL, + TK_OP_DIV, + TK_OP_MOD, + TK_OP_SHIFT_LEFT, + TK_OP_SHIFT_RIGHT, + TK_OP_BIT_AND, + TK_OP_BIT_OR, + TK_OP_BIT_XOR, + TK_OP_BIT_INVERT, + TK_INPUT, + TK_EOF, + TK_ERROR, + TK_MAX + }; + + static const char *token_name[TK_MAX]; + struct Token { + + TokenType type; + Variant value; + }; + + void _set_error(const String &p_err) { + if (error_set) + return; + error_str = p_err; + error_set = true; + } + + Error _get_token(Token &r_token); + + String error_str; + bool error_set; + + struct ENode { + + enum Type { + TYPE_INPUT, + TYPE_CONSTANT, + TYPE_SELF, + TYPE_OPERATOR, + TYPE_INDEX, + TYPE_NAMED_INDEX, + TYPE_ARRAY, + TYPE_DICTIONARY, + TYPE_CONSTRUCTOR, + TYPE_BUILTIN_FUNC, + TYPE_CALL + }; + + ENode *next; + + Type type; + + ENode() { next = NULL; } + virtual ~ENode() { + if (next) { + memdelete(next); + } + } + }; + + struct ExpressionNode { + + bool is_op; + union { + Variant::Operator op; + ENode *node; + }; + }; + + ENode *_parse_expression(); + + struct InputNode : public ENode { + + int index; + InputNode() { + type = TYPE_INPUT; + } + }; + + struct ConstantNode : public ENode { + + Variant value; + ConstantNode() { + type = TYPE_CONSTANT; + } + }; + + struct OperatorNode : public ENode { + + Variant::Operator op; + + ENode *nodes[2]; + + OperatorNode() { + type = TYPE_OPERATOR; + } + }; + + struct SelfNode : public ENode { + + SelfNode() { + type = TYPE_SELF; + } + }; + + struct IndexNode : public ENode { + ENode *base; + ENode *index; + + IndexNode() { + type = TYPE_INDEX; + } + }; + + struct NamedIndexNode : public ENode { + ENode *base; + StringName name; + + NamedIndexNode() { + type = TYPE_NAMED_INDEX; + } + }; + + struct ConstructorNode : public ENode { + Variant::Type data_type; + Vector<ENode *> arguments; + + ConstructorNode() { + type = TYPE_CONSTRUCTOR; + } + }; + + struct CallNode : public ENode { + ENode *base; + StringName method; + Vector<ENode *> arguments; + + CallNode() { + type = TYPE_CALL; + } + }; + + struct ArrayNode : public ENode { + Vector<ENode *> array; + ArrayNode() { + type = TYPE_ARRAY; + } + }; + + struct DictionaryNode : public ENode { + Vector<ENode *> dict; + DictionaryNode() { + type = TYPE_DICTIONARY; + } + }; + + struct BuiltinFuncNode : public ENode { + BuiltinFunc func; + Vector<ENode *> arguments; + BuiltinFuncNode() { + type = TYPE_BUILTIN_FUNC; + } + }; + + template <class T> + T *alloc_node() { + T *node = memnew(T); + node->next = nodes; + nodes = node; + return node; + } + + ENode *root; + ENode *nodes; + + Vector<String> input_names; + + bool execution_error; + bool _execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str); + +protected: + static void _bind_methods(); + +public: + Error parse(const String &p_expression, const Vector<String> &p_input_names = Vector<String>()); + Variant execute(Array p_inputs, Object *p_base = NULL, bool p_show_error = true); + bool has_execute_failed() const; + String get_error_text() const; + + Expression(); + ~Expression(); +}; + +#endif // EXPRESSION_H diff --git a/core/math/geometry.cpp b/core/math/geometry.cpp index 24f077a4ca..7ab28daf20 100644 --- a/core/math/geometry.cpp +++ b/core/math/geometry.cpp @@ -56,7 +56,7 @@ void Geometry::MeshData::optimize_vertices() { vtx_remap[idx] = ni; } - faces[i].indices[j] = vtx_remap[idx]; + faces.write[i].indices.write[j] = vtx_remap[idx]; } } @@ -74,8 +74,8 @@ void Geometry::MeshData::optimize_vertices() { vtx_remap[b] = ni; } - edges[i].a = vtx_remap[a]; - edges[i].b = vtx_remap[b]; + edges.write[i].a = vtx_remap[a]; + edges.write[i].b = vtx_remap[b]; } Vector<Vector3> new_vertices; @@ -84,7 +84,7 @@ void Geometry::MeshData::optimize_vertices() { for (int i = 0; i < vertices.size(); i++) { if (vtx_remap.has(i)) - new_vertices[vtx_remap[i]] = vertices[i]; + new_vertices.write[vtx_remap[i]] = vertices[i]; } vertices = new_vertices; } @@ -1014,8 +1014,8 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu Vector<_AtlasWorkRect> wrects; wrects.resize(p_rects.size()); for (int i = 0; i < p_rects.size(); i++) { - wrects[i].s = p_rects[i]; - wrects[i].idx = i; + wrects.write[i].s = p_rects[i]; + wrects.write[i].idx = i; } wrects.sort(); int widest = wrects[0].s.width; @@ -1033,7 +1033,7 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu Vector<int> hmax; hmax.resize(w); for (int j = 0; j < w; j++) - hmax[j] = 0; + hmax.write[j] = 0; //place them int ofs = 0; @@ -1052,8 +1052,8 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu from_y = hmax[ofs + k]; } - wrects[j].p.x = ofs; - wrects[j].p.y = from_y; + wrects.write[j].p.x = ofs; + wrects.write[j].p.y = from_y; int end_h = from_y + wrects[j].s.height; int end_w = ofs + wrects[j].s.width; if (ofs == 0) @@ -1061,7 +1061,7 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu for (int k = 0; k < wrects[j].s.width; k++) { - hmax[ofs + k] = end_h; + hmax.write[ofs + k] = end_h; } if (end_h > max_h) @@ -1101,7 +1101,7 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu for (int i = 0; i < p_rects.size(); i++) { - r_result[results[best].result[i].idx] = results[best].result[i].p; + r_result.write[results[best].result[i].idx] = results[best].result[i].p; } r_size = Size2(results[best].max_w, results[best].max_h); diff --git a/core/math/geometry.h b/core/math/geometry.h index be998aef0b..186a05fb37 100644 --- a/core/math/geometry.h +++ b/core/math/geometry.h @@ -890,14 +890,14 @@ public: for (int i = 0; i < n; ++i) { while (k >= 2 && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0) k--; - H[k++] = P[i]; + H.write[k++] = P[i]; } // Build upper hull for (int i = n - 2, t = k + 1; i >= 0; i--) { while (k >= t && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0) k--; - H[k++] = P[i]; + H.write[k++] = P[i]; } H.resize(k); diff --git a/core/math/math_2d.h b/core/math/math_2d.h index 611d47e3ff..25c39e5d7a 100644 --- a/core/math/math_2d.h +++ b/core/math/math_2d.h @@ -112,6 +112,7 @@ struct Vector2 { _FORCE_INLINE_ static Vector2 linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_t); _FORCE_INLINE_ Vector2 linear_interpolate(const Vector2 &p_b, real_t p_t) const; + _FORCE_INLINE_ Vector2 slerp(const Vector2 &p_b, real_t p_t) const; Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_t) const; Vector2 slide(const Vector2 &p_normal) const; @@ -263,6 +264,14 @@ Vector2 Vector2::linear_interpolate(const Vector2 &p_b, real_t p_t) const { return res; } +Vector2 Vector2::slerp(const Vector2 &p_b, real_t p_t) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(is_normalized() == false, Vector2()); +#endif + real_t theta = angle_to(p_b); + return rotated(theta * p_t); +} + Vector2 Vector2::linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_t) { Vector2 res = p_a; diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index 20001bb9a6..f0c0268f31 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -182,8 +182,22 @@ public: static _ALWAYS_INLINE_ float abs(float g) { return absf(g); } static _ALWAYS_INLINE_ int abs(int g) { return g > 0 ? g : -g; } - static _ALWAYS_INLINE_ double fposmod(double p_x, double p_y) { return (p_x >= 0) ? Math::fmod(p_x, p_y) : p_y - Math::fmod(-p_x, p_y); } - static _ALWAYS_INLINE_ float fposmod(float p_x, float p_y) { return (p_x >= 0) ? Math::fmod(p_x, p_y) : p_y - Math::fmod(-p_x, p_y); } + static _ALWAYS_INLINE_ double fposmod(double p_x, double p_y) { + double value = Math::fmod(p_x, p_y); + if ((value < 0 && p_y > 0) || (value > 0 && p_y < 0)) { + value += p_y; + } + value += 0.0; + return value; + } + static _ALWAYS_INLINE_ float fposmod(float p_x, float p_y) { + float value = Math::fmod(p_x, p_y); + if ((value < 0 && p_y > 0) || (value > 0 && p_y < 0)) { + value += p_y; + } + value += 0.0; + return value; + } static _ALWAYS_INLINE_ double deg2rad(double p_y) { return p_y * Math_PI / 180.0; } static _ALWAYS_INLINE_ float deg2rad(float p_y) { return p_y * Math_PI / 180.0; } diff --git a/core/math/matrix3.cpp b/core/math/matrix3.cpp index b0b05d1ec8..7db41756ed 100644 --- a/core/math/matrix3.cpp +++ b/core/math/matrix3.cpp @@ -242,18 +242,11 @@ void Basis::scale_local(const Vector3 &p_scale) { Basis Basis::scaled_local(const Vector3 &p_scale) const { Basis b; - b.set_scale(p_scale); + b.set_diagonal(p_scale); return (*this) * b; } -void Basis::set_scale(const Vector3 &p_scale) { - - set_axis(0, get_axis(0).normalized() * p_scale.x); - set_axis(1, get_axis(1).normalized() * p_scale.y); - set_axis(2, get_axis(2).normalized() * p_scale.z); -} - Vector3 Basis::get_scale_abs() const { return Vector3( @@ -356,8 +349,7 @@ void Basis::rotate(const Quat &p_quat) { *this = rotated(p_quat); } -// TODO: rename this to get_rotation_euler -Vector3 Basis::get_rotation() const { +Vector3 Basis::get_rotation_euler() const { // Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S, // and returns the Euler angles corresponding to the rotation part, complementing get_scale(). // See the comment in get_scale() for further information. @@ -371,6 +363,20 @@ Vector3 Basis::get_rotation() const { return m.get_euler(); } +Quat Basis::get_rotation_quat() const { + // Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S, + // and returns the Euler angles corresponding to the rotation part, complementing get_scale(). + // See the comment in get_scale() for further information. + Basis m = orthonormalized(); + real_t det = m.determinant(); + if (det < 0) { + // Ensure that the determinant is 1, such that result is a proper rotation matrix which can be represented by Euler angles. + m.scale(Vector3(-1, -1, -1)); + } + + return m.get_quat(); +} + void Basis::get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const { // Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S, // and returns the Euler angles corresponding to the rotation part, complementing get_scale(). @@ -591,10 +597,9 @@ Basis::operator String() const { } Quat Basis::get_quat() const { - //commenting this check because precision issues cause it to fail when it shouldn't - //#ifdef MATH_CHECKS - //ERR_FAIL_COND_V(is_rotation() == false, Quat()); - //#endif +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(is_rotation() == false, Quat()); +#endif real_t trace = elements[0][0] + elements[1][1] + elements[2][2]; real_t temp[4]; @@ -826,3 +831,16 @@ void Basis::set_diagonal(const Vector3 p_diag) { elements[2][1] = 0; elements[2][2] = p_diag.z; } + +Basis Basis::slerp(const Basis &target, const real_t &t) const { +// TODO: implement this directly without using quaternions to make it more efficient +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(is_rotation() == false, Basis()); + ERR_FAIL_COND_V(target.is_rotation() == false, Basis()); +#endif + + Quat from(*this); + Quat to(target); + + return Basis(from.slerp(to, t)); +} diff --git a/core/math/matrix3.h b/core/math/matrix3.h index fd383fc673..9ff1a97dc9 100644 --- a/core/math/matrix3.h +++ b/core/math/matrix3.h @@ -84,9 +84,11 @@ public: void rotate(const Quat &p_quat); Basis rotated(const Quat &p_quat) const; - Vector3 get_rotation() const; + Vector3 get_rotation_euler() const; void get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const; void get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) const; + Quat get_rotation_quat() const; + Vector3 get_rotation() const { return get_rotation_euler(); }; Vector3 rotref_posscale_decomposition(Basis &rotref) const; @@ -110,7 +112,6 @@ public: void scale_local(const Vector3 &p_scale); Basis scaled_local(const Vector3 &p_scale) const; - void set_scale(const Vector3 &p_scale); Vector3 get_scale() const; Vector3 get_scale_abs() const; Vector3 get_scale_local() const; @@ -155,6 +156,8 @@ public: bool is_diagonal() const; bool is_rotation() const; + Basis slerp(const Basis &target, const real_t &t) const; + operator String() const; /* create / set */ @@ -228,10 +231,13 @@ public: operator Quat() const { return get_quat(); } Basis(const Quat &p_quat) { set_quat(p_quat); }; + Basis(const Quat &p_quat, const Vector3 &p_scale) { set_quat_scale(p_quat, p_scale); } + Basis(const Vector3 &p_euler) { set_euler(p_euler); } + Basis(const Vector3 &p_euler, const Vector3 &p_scale) { set_euler_scale(p_euler, p_scale); } + Basis(const Vector3 &p_axis, real_t p_phi) { set_axis_angle(p_axis, p_phi); } Basis(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) { set_axis_angle_scale(p_axis, p_phi, p_scale); } - Basis(const Quat &p_quat, const Vector3 &p_scale) { set_quat_scale(p_quat, p_scale); } _FORCE_INLINE_ Basis(const Vector3 &row0, const Vector3 &row1, const Vector3 &row2) { elements[0] = row0; diff --git a/core/math/quat.cpp b/core/math/quat.cpp index 4f61401ac7..67c9048a41 100644 --- a/core/math/quat.cpp +++ b/core/math/quat.cpp @@ -98,6 +98,9 @@ void Quat::set_euler_yxz(const Vector3 &p_euler) { // and similar for other axes. // This implementation uses YXZ convention (Z is the first rotation). Vector3 Quat::get_euler_yxz() const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(is_normalized() == false, Vector3(0, 0, 0)); +#endif Basis m(*this); return m.get_euler_yxz(); } @@ -135,11 +138,17 @@ bool Quat::is_normalized() const { } Quat Quat::inverse() const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(is_normalized() == false, Quat()); +#endif return Quat(-x, -y, -z, w); } Quat Quat::slerp(const Quat &q, const real_t &t) const { - +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(is_normalized() == false, Quat()); + ERR_FAIL_COND_V(q.is_normalized() == false, Quat()); +#endif Quat to1; real_t omega, cosom, sinom, scale0, scale1; @@ -183,7 +192,10 @@ Quat Quat::slerp(const Quat &q, const real_t &t) const { } Quat Quat::slerpni(const Quat &q, const real_t &t) const { - +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(is_normalized() == false, Quat()); + ERR_FAIL_COND_V(q.is_normalized() == false, Quat()); +#endif const Quat &from = *this; real_t dot = from.dot(q); @@ -202,7 +214,10 @@ Quat Quat::slerpni(const Quat &q, const real_t &t) const { } Quat Quat::cubic_slerp(const Quat &q, const Quat &prep, const Quat &postq, const real_t &t) const { - +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(is_normalized() == false, Quat()); + ERR_FAIL_COND_V(q.is_normalized() == false, Quat()); +#endif //the only way to do slerp :| real_t t2 = (1.0 - t) * t * 2; Quat sp = this->slerp(q, t); @@ -215,7 +230,10 @@ Quat::operator String() const { return String::num(x) + ", " + String::num(y) + ", " + String::num(z) + ", " + String::num(w); } -Quat::Quat(const Vector3 &axis, const real_t &angle) { +void Quat::set_axis_angle(const Vector3 &axis, const real_t &angle) { +#ifdef MATH_CHECKS + ERR_FAIL_COND(axis.is_normalized() == false); +#endif real_t d = axis.length(); if (d == 0) set(0, 0, 0, 0); diff --git a/core/math/quat.h b/core/math/quat.h index ebc924504b..6dc8d66f60 100644 --- a/core/math/quat.h +++ b/core/math/quat.h @@ -64,11 +64,13 @@ public: Quat slerpni(const Quat &q, const real_t &t) const; Quat cubic_slerp(const Quat &q, const Quat &prep, const Quat &postq, const real_t &t) const; + void set_axis_angle(const Vector3 &axis, const real_t &angle); _FORCE_INLINE_ void get_axis_angle(Vector3 &r_axis, real_t &r_angle) const { r_angle = 2 * Math::acos(w); - r_axis.x = x / Math::sqrt(1 - w * w); - r_axis.y = y / Math::sqrt(1 - w * w); - r_axis.z = z / Math::sqrt(1 - w * w); + real_t r = ((real_t)1) / Math::sqrt(1 - w * w); + r_axis.x = x * r; + r_axis.y = y * r; + r_axis.z = z * r; } void operator*=(const Quat &q); @@ -82,10 +84,12 @@ public: } _FORCE_INLINE_ Vector3 xform(const Vector3 &v) const { - - Quat q = *this * v; - q *= this->inverse(); - return Vector3(q.x, q.y, q.z); +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(is_normalized() == false, v); +#endif + Vector3 u(x, y, z); + Vector3 uv = u.cross(v); + return v + ((uv * w) + u.cross(uv)) * ((real_t)2); } _FORCE_INLINE_ void operator+=(const Quat &q); @@ -115,7 +119,15 @@ public: z = p_z; w = p_w; } - Quat(const Vector3 &axis, const real_t &angle); + Quat(const Vector3 &axis, const real_t &angle) { set_axis_angle(axis, angle); } + + Quat(const Vector3 &euler) { set_euler(euler); } + Quat(const Quat &q) { + x = q.x; + y = q.y; + z = q.z; + w = q.w; + } Quat(const Vector3 &v0, const Vector3 &v1) // shortest arc { diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index fc90417413..cb923d264e 100644 --- a/core/math/quick_hull.cpp +++ b/core/math/quick_hull.cpp @@ -61,10 +61,10 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me Vector3 sp = p_points[i].snapped(Vector3(0.0001, 0.0001, 0.0001)); if (valid_cache.has(sp)) { - valid_points[i] = false; + valid_points.write[i] = false; //print_line("INVALIDATED: "+itos(i)); } else { - valid_points[i] = true; + valid_points.write[i] = true; valid_cache.insert(sp); } } @@ -452,7 +452,7 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me int idx = 0; for (List<Geometry::MeshData::Face>::Element *E = ret_faces.front(); E; E = E->next()) { - r_mesh.faces[idx++] = E->get(); + r_mesh.faces.write[idx++] = E->get(); } r_mesh.edges.resize(ret_edges.size()); idx = 0; @@ -461,7 +461,7 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me Geometry::MeshData::Edge e; e.a = E->key().vertices[0]; e.b = E->key().vertices[1]; - r_mesh.edges[idx++] = e; + r_mesh.edges.write[idx++] = e; } r_mesh.vertices = p_points; diff --git a/core/math/transform.cpp b/core/math/transform.cpp index 7cd186ca60..976e0f174e 100644 --- a/core/math/transform.cpp +++ b/core/math/transform.cpp @@ -120,19 +120,18 @@ Transform Transform::interpolate_with(const Transform &p_transform, real_t p_c) /* not sure if very "efficient" but good enough? */ Vector3 src_scale = basis.get_scale(); - Quat src_rot = basis.orthonormalized(); + Quat src_rot = basis.get_rotation_quat(); Vector3 src_loc = origin; Vector3 dst_scale = p_transform.basis.get_scale(); - Quat dst_rot = p_transform.basis; + Quat dst_rot = p_transform.basis.get_rotation_quat(); Vector3 dst_loc = p_transform.origin; - Transform dst; //this could be made faster by using a single function in Basis.. - dst.basis = src_rot.slerp(dst_rot, p_c).normalized(); - dst.basis.set_scale(src_scale.linear_interpolate(dst_scale, p_c)); - dst.origin = src_loc.linear_interpolate(dst_loc, p_c); + Transform interp; + interp.basis.set_quat_scale(src_rot.slerp(dst_rot, p_c).normalized(), src_scale.linear_interpolate(dst_scale, p_c)); + interp.origin = src_loc.linear_interpolate(dst_loc, p_c); - return dst; + return interp; } void Transform::scale(const Vector3 &p_scale) { diff --git a/core/math/triangle_mesh.cpp b/core/math/triangle_mesh.cpp index edd4ad3441..5475f733c3 100644 --- a/core/math/triangle_mesh.cpp +++ b/core/math/triangle_mesh.cpp @@ -88,6 +88,26 @@ int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, in return index; } +void TriangleMesh::get_indices(PoolVector<int> *r_triangles_indices) const { + + if (!valid) + return; + + const int triangles_num = triangles.size(); + + // Parse vertices indices + PoolVector<Triangle>::Read triangles_read = triangles.read(); + + r_triangles_indices->resize(triangles_num * 3); + PoolVector<int>::Write r_indices_write = r_triangles_indices->write(); + + for (int i = 0; i < triangles_num; ++i) { + r_indices_write[3 * i + 0] = triangles_read[i].indices[0]; + r_indices_write[3 * i + 1] = triangles_read[i].indices[1]; + r_indices_write[3 * i + 2] = triangles_read[i].indices[2]; + } +} + void TriangleMesh::create(const PoolVector<Vector3> &p_faces) { valid = false; @@ -490,6 +510,222 @@ bool TriangleMesh::intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, V return inters; } +bool TriangleMesh::intersect_convex_shape(const Plane *p_planes, int p_plane_count) const { + uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth); + + //p_fully_inside = true; + + enum { + TEST_AABB_BIT = 0, + VISIT_LEFT_BIT = 1, + VISIT_RIGHT_BIT = 2, + VISIT_DONE_BIT = 3, + VISITED_BIT_SHIFT = 29, + NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1, + VISITED_BIT_MASK = ~NODE_IDX_MASK, + + }; + + int level = 0; + + PoolVector<Triangle>::Read trianglesr = triangles.read(); + PoolVector<Vector3>::Read verticesr = vertices.read(); + PoolVector<BVH>::Read bvhr = bvh.read(); + + const Triangle *triangleptr = trianglesr.ptr(); + const Vector3 *vertexptr = verticesr.ptr(); + int pos = bvh.size() - 1; + const BVH *bvhptr = bvhr.ptr(); + + stack[0] = pos; + while (true) { + + uint32_t node = stack[level] & NODE_IDX_MASK; + const BVH &b = bvhptr[node]; + bool done = false; + + switch (stack[level] >> VISITED_BIT_SHIFT) { + case TEST_AABB_BIT: { + + bool valid = b.aabb.intersects_convex_shape(p_planes, p_plane_count); + if (!valid) { + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + + } else { + + if (b.face_index >= 0) { + + const Triangle &s = triangleptr[b.face_index]; + + for (int j = 0; j < 3; ++j) { + const Vector3 &point = vertexptr[s.indices[j]]; + const Vector3 &next_point = vertexptr[s.indices[(j + 1) % 3]]; + Vector3 res; + bool over = true; + for (int i = 0; i < p_plane_count; i++) { + const Plane &p = p_planes[i]; + + if (p.intersects_segment(point, next_point, &res)) { + bool inisde = true; + for (int k = 0; k < p_plane_count; k++) { + if (k == i) continue; + const Plane &pp = p_planes[k]; + if (pp.is_point_over(res)) { + inisde = false; + break; + } + } + if (inisde) return true; + } + + if (p.is_point_over(point)) { + over = false; + break; + } + } + if (over) return true; + } + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + + } else { + + stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node; + } + } + continue; + } + case VISIT_LEFT_BIT: { + + stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node; + stack[level + 1] = b.left | TEST_AABB_BIT; + level++; + continue; + } + case VISIT_RIGHT_BIT: { + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + stack[level + 1] = b.right | TEST_AABB_BIT; + level++; + continue; + } + case VISIT_DONE_BIT: { + + if (level == 0) { + done = true; + break; + } else + level--; + continue; + } + } + + if (done) + break; + } + + return false; +} + +bool TriangleMesh::inside_convex_shape(const Plane *p_planes, int p_plane_count, Vector3 p_scale) const { + uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth); + + enum { + TEST_AABB_BIT = 0, + VISIT_LEFT_BIT = 1, + VISIT_RIGHT_BIT = 2, + VISIT_DONE_BIT = 3, + VISITED_BIT_SHIFT = 29, + NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1, + VISITED_BIT_MASK = ~NODE_IDX_MASK, + + }; + + int level = 0; + + PoolVector<Triangle>::Read trianglesr = triangles.read(); + PoolVector<Vector3>::Read verticesr = vertices.read(); + PoolVector<BVH>::Read bvhr = bvh.read(); + + Transform scale(Basis().scaled(p_scale)); + + const Triangle *triangleptr = trianglesr.ptr(); + const Vector3 *vertexptr = verticesr.ptr(); + int pos = bvh.size() - 1; + const BVH *bvhptr = bvhr.ptr(); + + stack[0] = pos; + while (true) { + + uint32_t node = stack[level] & NODE_IDX_MASK; + const BVH &b = bvhptr[node]; + bool done = false; + + switch (stack[level] >> VISITED_BIT_SHIFT) { + case TEST_AABB_BIT: { + + bool intersects = scale.xform(b.aabb).intersects_convex_shape(p_planes, p_plane_count); + if (!intersects) return false; + + bool inside = scale.xform(b.aabb).inside_convex_shape(p_planes, p_plane_count); + if (inside) { + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + + } else { + + if (b.face_index >= 0) { + const Triangle &s = triangleptr[b.face_index]; + for (int j = 0; j < 3; ++j) { + Vector3 point = scale.xform(vertexptr[s.indices[j]]); + for (int i = 0; i < p_plane_count; i++) { + const Plane &p = p_planes[i]; + if (p.is_point_over(point)) return false; + } + } + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + + } else { + + stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node; + } + } + continue; + } + case VISIT_LEFT_BIT: { + + stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node; + stack[level + 1] = b.left | TEST_AABB_BIT; + level++; + continue; + } + case VISIT_RIGHT_BIT: { + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + stack[level + 1] = b.right | TEST_AABB_BIT; + level++; + continue; + } + case VISIT_DONE_BIT: { + + if (level == 0) { + done = true; + break; + } else + level--; + continue; + } + } + + if (done) + break; + } + + return true; +} + bool TriangleMesh::is_valid() const { return valid; diff --git a/core/math/triangle_mesh.h b/core/math/triangle_mesh.h index 9f145f2afb..bf793fc50f 100644 --- a/core/math/triangle_mesh.h +++ b/core/math/triangle_mesh.h @@ -89,9 +89,15 @@ public: bool is_valid() const; bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal) const; bool intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, Vector3 &r_point, Vector3 &r_normal) const; + bool intersect_convex_shape(const Plane *p_planes, int p_plane_count) const; + bool inside_convex_shape(const Plane *p_planes, int p_plane_count, Vector3 p_scale = Vector3(1, 1, 1)) const; Vector3 get_area_normal(const AABB &p_aabb) const; PoolVector<Face3> get_faces() const; + PoolVector<Triangle> get_triangles() const { return triangles; } + PoolVector<Vector3> get_vertices() const { return vertices; } + void get_indices(PoolVector<int> *p_triangles_indices) const; + void create(const PoolVector<Vector3> &p_faces); TriangleMesh(); }; diff --git a/core/math/triangulate.cpp b/core/math/triangulate.cpp index 563ba7268f..0edc0ea039 100644 --- a/core/math/triangulate.cpp +++ b/core/math/triangulate.cpp @@ -128,10 +128,10 @@ bool Triangulate::triangulate(const Vector<Vector2> &contour, Vector<int> &resul if (0.0 < get_area(contour)) for (int v = 0; v < n; v++) - V[v] = v; + V.write[v] = v; else for (int v = 0; v < n; v++) - V[v] = (n - 1) - v; + V.write[v] = (n - 1) - v; bool relaxed = false; @@ -182,7 +182,7 @@ bool Triangulate::triangulate(const Vector<Vector2> &contour, Vector<int> &resul /* remove v from remaining polygon */ for (s = v, t = v + 1; t < nv; s++, t++) - V[s] = V[t]; + V.write[s] = V[t]; nv--; diff --git a/core/math/vector3.h b/core/math/vector3.h index 3bbfd7627c..433adf09ee 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -91,6 +91,7 @@ struct Vector3 { /* Static Methods between 2 vector3s */ _FORCE_INLINE_ Vector3 linear_interpolate(const Vector3 &p_b, real_t p_t) const; + _FORCE_INLINE_ Vector3 slerp(const Vector3 &p_b, real_t p_t) const; Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_t) const; Vector3 cubic_interpolaten(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_t) const; @@ -218,6 +219,15 @@ Vector3 Vector3::linear_interpolate(const Vector3 &p_b, real_t p_t) const { z + (p_t * (p_b.z - z))); } +Vector3 Vector3::slerp(const Vector3 &p_b, real_t p_t) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(is_normalized() == false, Vector3()); +#endif + + real_t theta = angle_to(p_b); + return rotated(cross(p_b), theta * p_t); +} + real_t Vector3::distance_to(const Vector3 &p_b) const { return (p_b - *this).length(); diff --git a/core/message_queue.cpp b/core/message_queue.cpp index 25ee6eafae..3adaad868a 100644 --- a/core/message_queue.cpp +++ b/core/message_queue.cpp @@ -342,7 +342,7 @@ MessageQueue::MessageQueue() { buffer_end = 0; buffer_max_used = 0; - buffer_size = GLOBAL_DEF("memory/limits/message_queue/max_size_kb", DEFAULT_QUEUE_SIZE_KB); + buffer_size = GLOBAL_DEF_RST("memory/limits/message_queue/max_size_kb", DEFAULT_QUEUE_SIZE_KB); buffer_size *= 1024; buffer = memnew_arr(uint8_t, buffer_size); } diff --git a/core/method_bind.h b/core/method_bind.h index 41b500c401..7ee687ee40 100644 --- a/core/method_bind.h +++ b/core/method_bind.h @@ -354,7 +354,7 @@ public: for (int i = 0; i < p_info.arguments.size(); i++) { at[i + 1] = p_info.arguments[i].type; - names[i] = p_info.arguments[i].name; + names.write[i] = p_info.arguments[i].name; } set_argument_names(names); diff --git a/core/method_ptrcall.h b/core/method_ptrcall.h index 2007c3def5..2f6dcb3178 100644 --- a/core/method_ptrcall.h +++ b/core/method_ptrcall.h @@ -180,7 +180,7 @@ struct PtrToArg<const T *> { { \ PoolVector<m_type>::Read r = dvs->read(); \ for (int i = 0; i < len; i++) { \ - ret[i] = r[i]; \ + ret.write[i] = r[i]; \ } \ } \ return ret; \ @@ -207,13 +207,57 @@ struct PtrToArg<const T *> { { \ PoolVector<m_type>::Read r = dvs->read(); \ for (int i = 0; i < len; i++) { \ - ret[i] = r[i]; \ + ret.write[i] = r[i]; \ } \ } \ return ret; \ } \ } +#define MAKE_VECARG_ALT(m_type, m_type_alt) \ + template <> \ + struct PtrToArg<Vector<m_type_alt> > { \ + _FORCE_INLINE_ static Vector<m_type_alt> convert(const void *p_ptr) { \ + const PoolVector<m_type> *dvs = reinterpret_cast<const PoolVector<m_type> *>(p_ptr); \ + Vector<m_type_alt> ret; \ + int len = dvs->size(); \ + ret.resize(len); \ + { \ + PoolVector<m_type>::Read r = dvs->read(); \ + for (int i = 0; i < len; i++) { \ + ret.write[i] = r[i]; \ + } \ + } \ + return ret; \ + } \ + _FORCE_INLINE_ static void encode(Vector<m_type_alt> p_vec, void *p_ptr) { \ + PoolVector<m_type> *dv = reinterpret_cast<PoolVector<m_type> *>(p_ptr); \ + int len = p_vec.size(); \ + dv->resize(len); \ + { \ + PoolVector<m_type>::Write w = dv->write(); \ + for (int i = 0; i < len; i++) { \ + w[i] = p_vec[i]; \ + } \ + } \ + } \ + }; \ + template <> \ + struct PtrToArg<const Vector<m_type_alt> &> { \ + _FORCE_INLINE_ static Vector<m_type_alt> convert(const void *p_ptr) { \ + const PoolVector<m_type> *dvs = reinterpret_cast<const PoolVector<m_type> *>(p_ptr); \ + Vector<m_type_alt> ret; \ + int len = dvs->size(); \ + ret.resize(len); \ + { \ + PoolVector<m_type>::Read r = dvs->read(); \ + for (int i = 0; i < len; i++) { \ + ret.write[i] = r[i]; \ + } \ + } \ + return ret; \ + } \ + } MAKE_VECARG(String); MAKE_VECARG(uint8_t); MAKE_VECARG(int); @@ -221,6 +265,7 @@ MAKE_VECARG(float); MAKE_VECARG(Vector2); MAKE_VECARG(Vector3); MAKE_VECARG(Color); +MAKE_VECARG_ALT(String, StringName); //for stuff that gets converted to Array vectors #define MAKE_VECARR(m_type) \ @@ -232,7 +277,7 @@ MAKE_VECARG(Color); int len = arr->size(); \ ret.resize(len); \ for (int i = 0; i < len; i++) { \ - ret[i] = (*arr)[i]; \ + ret.write[i] = (*arr)[i]; \ } \ return ret; \ } \ @@ -253,7 +298,7 @@ MAKE_VECARG(Color); int len = arr->size(); \ ret.resize(len); \ for (int i = 0; i < len; i++) { \ - ret[i] = (*arr)[i]; \ + ret.write[i] = (*arr)[i]; \ } \ return ret; \ } \ diff --git a/core/node_path.cpp b/core/node_path.cpp index 64983fc091..7d4116fa1e 100644 --- a/core/node_path.cpp +++ b/core/node_path.cpp @@ -32,10 +32,7 @@ #include "print_string.h" -uint32_t NodePath::hash() const { - - if (!data) - return 0; +void NodePath::_update_hash_cache() const { uint32_t h = data->absolute ? 1 : 0; int pc = data->path.size(); @@ -49,13 +46,15 @@ uint32_t NodePath::hash() const { h = h ^ ssn[i].hash(); } - return h; + data->hash_cache_valid = true; + data->hash_cache = h; } void NodePath::prepend_period() { if (data->path.size() && data->path[0].operator String() != ".") { data->path.insert(0, "."); + data->hash_cache_valid = false; } } @@ -114,21 +113,33 @@ bool NodePath::operator==(const NodePath &p_path) const { if (data->absolute != p_path.data->absolute) return false; - if (data->path.size() != p_path.data->path.size()) + int path_size = data->path.size(); + + if (path_size != p_path.data->path.size()) { return false; + } + + int subpath_size = data->subpath.size(); - if (data->subpath.size() != p_path.data->subpath.size()) + if (subpath_size != p_path.data->subpath.size()) { return false; + } - for (int i = 0; i < data->path.size(); i++) { + const StringName *l_path_ptr = data->path.ptr(); + const StringName *r_path_ptr = p_path.data->path.ptr(); + + for (int i = 0; i < path_size; i++) { - if (data->path[i] != p_path.data->path[i]) + if (l_path_ptr[i] != r_path_ptr[i]) return false; } - for (int i = 0; i < data->subpath.size(); i++) { + const StringName *l_subpath_ptr = data->subpath.ptr(); + const StringName *r_subpath_ptr = p_path.data->subpath.ptr(); + + for (int i = 0; i < subpath_size; i++) { - if (data->subpath[i] != p_path.data->subpath[i]) + if (l_subpath_ptr[i] != r_subpath_ptr[i]) return false; } @@ -286,6 +297,7 @@ NodePath::NodePath(const Vector<StringName> &p_path, bool p_absolute) { data->absolute = p_absolute; data->path = p_path; data->has_slashes = true; + data->hash_cache_valid = false; } NodePath::NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p_subpath, bool p_absolute) { @@ -301,6 +313,7 @@ NodePath::NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p data->path = p_path; data->subpath = p_subpath; data->has_slashes = true; + data->hash_cache_valid = false; } void NodePath::simplify() { @@ -324,6 +337,7 @@ void NodePath::simplify() { } } } + data->hash_cache_valid = false; } NodePath NodePath::simplified() const { @@ -396,6 +410,7 @@ NodePath::NodePath(const String &p_path) { data->absolute = absolute ? true : false; data->has_slashes = has_slashes; data->subpath = subpath; + data->hash_cache_valid = false; if (slices == 0) return; @@ -412,7 +427,7 @@ NodePath::NodePath(const String &p_path) { String name = path.substr(from, i - from); ERR_FAIL_INDEX(slice, data->path.size()); - data->path[slice++] = name; + data->path.write[slice++] = name; } from = i + 1; last_is_slash = true; diff --git a/core/node_path.h b/core/node_path.h index 288f39721f..71235029af 100644 --- a/core/node_path.h +++ b/core/node_path.h @@ -47,11 +47,15 @@ class NodePath { StringName concatenated_subpath; bool absolute; bool has_slashes; + mutable bool hash_cache_valid; + mutable uint32_t hash_cache; }; - Data *data; + mutable Data *data; void unref(); + void _update_hash_cache() const; + public: _FORCE_INLINE_ StringName get_sname() const { @@ -78,7 +82,14 @@ public: NodePath get_parent() const; - uint32_t hash() const; + _FORCE_INLINE_ uint32_t hash() const { + if (!data) + return 0; + if (!data->hash_cache_valid) { + _update_hash_cache(); + } + return data->hash_cache; + } operator String() const; bool is_empty() const; diff --git a/core/object.cpp b/core/object.cpp index 239700a4ab..a0c64feb09 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -601,8 +601,12 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons _get_property_listv(p_list, p_reversed); - if (!is_class("Script")) // can still be set, but this is for userfriendlyness + if (!is_class("Script")) { // can still be set, but this is for userfriendlyness +#ifdef TOOLS_ENABLED + p_list->push_back(PropertyInfo(Variant::NIL, "Script", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); +#endif p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NONZERO)); + } #ifdef TOOLS_ENABLED if (editor_section_folding.size()) { p_list->push_back(PropertyInfo(Variant::ARRAY, CoreStringNames::get_singleton()->_sections_unfolded, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); @@ -814,8 +818,8 @@ Variant Object::callv(const StringName &p_method, const Array &p_args) { argptrs.resize(p_args.size()); for (int i = 0; i < p_args.size(); i++) { - args[i] = p_args[i]; - argptrs[i] = &args[i]; + args.write[i] = p_args[i]; + argptrs.write[i] = &args[i]; } Variant::CallError ce; @@ -1178,10 +1182,10 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int bind_mem.resize(p_argcount + c.binds.size()); for (int j = 0; j < p_argcount; j++) { - bind_mem[j] = p_args[j]; + bind_mem.write[j] = p_args[j]; } for (int j = 0; j < c.binds.size(); j++) { - bind_mem[p_argcount + j] = &c.binds[j]; + bind_mem.write[p_argcount + j] = &c.binds[j]; } args = (const Variant **)bind_mem.ptr(); @@ -1205,7 +1209,15 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int } } - if (c.flags & CONNECT_ONESHOT) { + bool disconnect = c.flags & CONNECT_ONESHOT; +#ifdef TOOLS_ENABLED + if (disconnect && (c.flags & CONNECT_PERSIST) && Engine::get_singleton()->is_editor_hint()) { + //this signal was connected from the editor, and is being edited. just dont disconnect for now + disconnect = false; + } +#endif + if (disconnect) { + _ObjectSignalDisconnectData dd; dd.signal = p_name; dd.target = target; @@ -1677,6 +1689,7 @@ void Object::_bind_methods() { #ifdef TOOLS_ENABLED MethodInfo miget("_get", PropertyInfo(Variant::STRING, "property")); miget.return_val.name = "Variant"; + miget.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; BIND_VMETHOD(miget); MethodInfo plget("_get_property_list"); diff --git a/core/object.h b/core/object.h index 7963a43fd6..8dc3426d1d 100644 --- a/core/object.h +++ b/core/object.h @@ -31,6 +31,7 @@ #ifndef OBJECT_H #define OBJECT_H +#include "hash_map.h" #include "list.h" #include "map.h" #include "os/rw_lock.h" @@ -85,6 +86,7 @@ enum PropertyHint { PROPERTY_HINT_PROPERTY_OF_INSTANCE, ///< a property of an instance PROPERTY_HINT_PROPERTY_OF_SCRIPT, ///< a property of a script & base PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send + PROPERTY_HINT_NODE_PATH_VALID_TYPES, PROPERTY_HINT_MAX, // When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit }; @@ -450,7 +452,7 @@ private: Signal() { lock = 0; } }; - HashMap<StringName, Signal, StringNameHasher> signal_map; + HashMap<StringName, Signal> signal_map; List<Connection> connections; #ifdef DEBUG_ENABLED SafeRefCount _lock_index; diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp index 96ebb79da5..a7730e5efe 100644 --- a/core/os/file_access.cpp +++ b/core/os/file_access.cpp @@ -262,15 +262,14 @@ String FileAccess::get_token() const { while (!eof_reached()) { if (c <= ' ') { - if (!token.empty()) + if (token.length()) break; } else { - token.push_back(c); + token += c; } c = get_8(); } - token.push_back(0); return String::utf8(token.get_data()); } @@ -293,7 +292,7 @@ class CharBuffer { for (int i = 0; i < written; i++) { - vector[i] = stack_buffer[i]; + vector.write[i] = stack_buffer[i]; } } diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp index 4ebb821a2f..12c6ef7d3b 100644 --- a/core/os/input_event.cpp +++ b/core/os/input_event.cpp @@ -509,6 +509,12 @@ String InputEventMouseButton::as_text() const { case BUTTON_WHEEL_RIGHT: button_index_string = "BUTTON_WHEEL_RIGHT"; break; + case BUTTON_XBUTTON1: + button_index_string = "BUTTON_XBUTTON1"; + break; + case BUTTON_XBUTTON2: + button_index_string = "BUTTON_XBUTTON2"; + break; default: button_index_string = itos(get_button_index()); break; @@ -601,6 +607,12 @@ String InputEventMouseMotion::as_text() const { case BUTTON_MASK_RIGHT: button_mask_string = "BUTTON_MASK_RIGHT"; break; + case BUTTON_MASK_XBUTTON1: + button_mask_string = "BUTTON_MASK_XBUTTON1"; + break; + case BUTTON_MASK_XBUTTON2: + button_mask_string = "BUTTON_MASK_XBUTTON2"; + break; default: button_mask_string = itos(get_button_mask()); break; @@ -937,6 +949,14 @@ bool InputEventAction::is_pressed() const { return pressed; } +bool InputEventAction::shortcut_match(const Ref<InputEvent> &p_event) const { + Ref<InputEventKey> event = p_event; + if (event.is_null()) + return false; + + return event->is_action(action); +} + bool InputEventAction::is_action(const StringName &p_action) const { return action == p_action; @@ -1068,3 +1088,122 @@ InputEventPanGesture::InputEventPanGesture() { delta = Vector2(0, 0); } +///////////////////////////// + +void InputEventMIDI::set_channel(const int p_channel) { + + channel = p_channel; +} + +int InputEventMIDI::get_channel() const { + return channel; +} + +void InputEventMIDI::set_message(const int p_message) { + + message = p_message; +} + +int InputEventMIDI::get_message() const { + return message; +} + +void InputEventMIDI::set_pitch(const int p_pitch) { + + pitch = p_pitch; +} + +int InputEventMIDI::get_pitch() const { + return pitch; +} + +void InputEventMIDI::set_velocity(const int p_velocity) { + + velocity = p_velocity; +} + +int InputEventMIDI::get_velocity() const { + return velocity; +} + +void InputEventMIDI::set_instrument(const int p_instrument) { + + instrument = p_instrument; +} + +int InputEventMIDI::get_instrument() const { + return instrument; +} + +void InputEventMIDI::set_pressure(const int p_pressure) { + + pressure = p_pressure; +} + +int InputEventMIDI::get_pressure() const { + return pressure; +} + +void InputEventMIDI::set_controller_number(const int p_controller_number) { + + controller_number = p_controller_number; +} + +int InputEventMIDI::get_controller_number() const { + return controller_number; +} + +void InputEventMIDI::set_controller_value(const int p_controller_value) { + + controller_value = p_controller_value; +} + +int InputEventMIDI::get_controller_value() const { + return controller_value; +} + +String InputEventMIDI::as_text() const { + + return "InputEventMIDI : channel=(" + itos(get_channel()) + "), message=(" + itos(get_message()) + ")"; +} + +void InputEventMIDI::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_channel", "channel"), &InputEventMIDI::set_channel); + ClassDB::bind_method(D_METHOD("get_channel"), &InputEventMIDI::get_channel); + ClassDB::bind_method(D_METHOD("set_message", "message"), &InputEventMIDI::set_message); + ClassDB::bind_method(D_METHOD("get_message"), &InputEventMIDI::get_message); + ClassDB::bind_method(D_METHOD("set_pitch", "pitch"), &InputEventMIDI::set_pitch); + ClassDB::bind_method(D_METHOD("get_pitch"), &InputEventMIDI::get_pitch); + ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &InputEventMIDI::set_velocity); + ClassDB::bind_method(D_METHOD("get_velocity"), &InputEventMIDI::get_velocity); + ClassDB::bind_method(D_METHOD("set_instrument", "instrument"), &InputEventMIDI::set_instrument); + ClassDB::bind_method(D_METHOD("get_instrument"), &InputEventMIDI::get_instrument); + ClassDB::bind_method(D_METHOD("set_pressure", "pressure"), &InputEventMIDI::set_pressure); + ClassDB::bind_method(D_METHOD("get_pressure"), &InputEventMIDI::get_pressure); + ClassDB::bind_method(D_METHOD("set_controller_number", "controller_number"), &InputEventMIDI::set_controller_number); + ClassDB::bind_method(D_METHOD("get_controller_number"), &InputEventMIDI::get_controller_number); + ClassDB::bind_method(D_METHOD("set_controller_value", "controller_value"), &InputEventMIDI::set_controller_value); + ClassDB::bind_method(D_METHOD("get_controller_value"), &InputEventMIDI::get_controller_value); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "channel"), "set_channel", "get_channel"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "message"), "set_message", "get_message"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "pitch"), "set_pitch", "get_pitch"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "velocity"), "set_velocity", "get_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "instrument"), "set_instrument", "get_instrument"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "pressure"), "set_pressure", "get_pressure"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_number"), "set_controller_number", "get_controller_number"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_value"), "set_controller_value", "get_controller_value"); +} + +InputEventMIDI::InputEventMIDI() { + + channel = 0; + message = 0; + pitch = 0; + velocity = 0; + instrument = 0; + pressure = 0; + controller_number = 0; + controller_value = 0; +} diff --git a/core/os/input_event.h b/core/os/input_event.h index 037649ed60..07df81488b 100644 --- a/core/os/input_event.h +++ b/core/os/input_event.h @@ -53,10 +53,13 @@ enum ButtonList { BUTTON_WHEEL_DOWN = 5, BUTTON_WHEEL_LEFT = 6, BUTTON_WHEEL_RIGHT = 7, + BUTTON_XBUTTON1 = 8, + BUTTON_XBUTTON2 = 9, BUTTON_MASK_LEFT = (1 << (BUTTON_LEFT - 1)), BUTTON_MASK_RIGHT = (1 << (BUTTON_RIGHT - 1)), BUTTON_MASK_MIDDLE = (1 << (BUTTON_MIDDLE - 1)), - + BUTTON_MASK_XBUTTON1 = (1 << (BUTTON_XBUTTON1 - 1)), + BUTTON_MASK_XBUTTON2 = (1 << (BUTTON_XBUTTON2 - 1)) }; enum JoystickList { @@ -137,6 +140,16 @@ enum JoystickList { JOY_ANALOG_R2 = JOY_AXIS_7, }; +enum MidiMessageList { + MIDI_MESSAGE_NOTE_OFF = 0x8, + MIDI_MESSAGE_NOTE_ON = 0x9, + MIDI_MESSAGE_AFTERTOUCH = 0xA, + MIDI_MESSAGE_CONTROL_CHANGE = 0xB, + MIDI_MESSAGE_PROGRAM_CHANGE = 0xC, + MIDI_MESSAGE_CHANNEL_PRESSURE = 0xD, + MIDI_MESSAGE_PITCH_BEND = 0xE, +}; + /** * Input Modifier Status * for keyboard/mouse events. @@ -467,6 +480,7 @@ public: virtual bool is_action(const StringName &p_action) const; + virtual bool shortcut_match(const Ref<InputEvent> &p_event) const; virtual bool is_action_type() const { return true; } virtual String as_text() const; @@ -522,4 +536,50 @@ public: InputEventPanGesture(); }; + +class InputEventMIDI : public InputEvent { + GDCLASS(InputEventMIDI, InputEvent) + + int channel; + int message; + int pitch; + int velocity; + int instrument; + int pressure; + int controller_number; + int controller_value; + +protected: + static void _bind_methods(); + +public: + void set_channel(const int p_channel); + int get_channel() const; + + void set_message(const int p_message); + int get_message() const; + + void set_pitch(const int p_pitch); + int get_pitch() const; + + void set_velocity(const int p_velocity); + int get_velocity() const; + + void set_instrument(const int p_instrument); + int get_instrument() const; + + void set_pressure(const int p_pressure); + int get_pressure() const; + + void set_controller_number(const int p_controller_number); + int get_controller_number() const; + + void set_controller_value(const int p_controller_value); + int get_controller_value() const; + + virtual String as_text() const; + + InputEventMIDI(); +}; + #endif diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp index 916c86613e..c51801e3e2 100644 --- a/core/os/main_loop.cpp +++ b/core/os/main_loop.cpp @@ -58,6 +58,7 @@ void MainLoop::_bind_methods() { BIND_CONSTANT(NOTIFICATION_OS_MEMORY_WARNING); BIND_CONSTANT(NOTIFICATION_TRANSLATION_CHANGED); BIND_CONSTANT(NOTIFICATION_WM_ABOUT); + BIND_CONSTANT(NOTIFICATION_CRASH); }; void MainLoop::set_init_script(const Ref<Script> &p_init_script) { diff --git a/core/os/main_loop.h b/core/os/main_loop.h index 546e4e280c..f96e46141e 100644 --- a/core/os/main_loop.h +++ b/core/os/main_loop.h @@ -62,6 +62,7 @@ public: // fixes this issue. NOTIFICATION_TRANSLATION_CHANGED = 90, NOTIFICATION_WM_ABOUT = 91, + NOTIFICATION_CRASH = 92, }; virtual void input_event(const Ref<InputEvent> &p_event); diff --git a/core/os/midi_driver.cpp b/core/os/midi_driver.cpp new file mode 100644 index 0000000000..7b4f84473c --- /dev/null +++ b/core/os/midi_driver.cpp @@ -0,0 +1,107 @@ +/*************************************************************************/ +/* midi_driver.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "midi_driver.h" + +#include "main/input_default.h" +#include "os/os.h" + +MIDIDriver *MIDIDriver::singleton = NULL; +MIDIDriver *MIDIDriver::get_singleton() { + + return singleton; +} + +void MIDIDriver::set_singleton() { + + singleton = this; +} + +void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length) { + + Ref<InputEventMIDI> event; + event.instance(); + + if (length >= 1) { + event->set_channel(data[0] & 0xF); + event->set_message(data[0] >> 4); + } + + switch (event->get_message()) { + case MIDI_MESSAGE_AFTERTOUCH: + if (length >= 3) { + event->set_pitch(data[1]); + event->set_pressure(data[2]); + } + break; + + case MIDI_MESSAGE_CONTROL_CHANGE: + if (length >= 3) { + event->set_controller_number(data[1]); + event->set_controller_value(data[2]); + } + break; + + case MIDI_MESSAGE_NOTE_ON: + case MIDI_MESSAGE_NOTE_OFF: + case MIDI_MESSAGE_PITCH_BEND: + if (length >= 3) { + event->set_pitch(data[1]); + event->set_velocity(data[2]); + } + break; + + case MIDI_MESSAGE_PROGRAM_CHANGE: + if (length >= 2) { + event->set_instrument(data[1]); + } + break; + + case MIDI_MESSAGE_CHANNEL_PRESSURE: + if (length >= 2) { + event->set_pressure(data[1]); + } + break; + } + + InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); + id->parse_input_event(event); +} + +PoolStringArray MIDIDriver::get_connected_inputs() { + + PoolStringArray list; + return list; +} + +MIDIDriver::MIDIDriver() { + + set_singleton(); +} diff --git a/core/os/midi_driver.h b/core/os/midi_driver.h new file mode 100644 index 0000000000..1a3a67a411 --- /dev/null +++ b/core/os/midi_driver.h @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* midi_driver.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef MIDI_DRIVER_H +#define MIDI_DRIVER_H + +#include "core/variant.h" +#include "typedefs.h" +/** + * Multi-Platform abstraction for accessing to MIDI. + */ + +class MIDIDriver { + + static MIDIDriver *singleton; + +public: + static MIDIDriver *get_singleton(); + void set_singleton(); + + virtual Error open() = 0; + virtual void close() = 0; + + virtual PoolStringArray get_connected_inputs(); + + static void receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length); + + MIDIDriver(); + virtual ~MIDIDriver() {} +}; + +#endif diff --git a/core/os/os.cpp b/core/os/os.cpp index 5eed10e30c..97dae05919 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -33,6 +33,7 @@ #include "dir_access.h" #include "input.h" #include "os/file_access.h" +#include "os/midi_driver.h" #include "project_settings.h" #include "servers/audio_server.h" #include "version_generated.gen.h" @@ -576,6 +577,13 @@ bool OS::has_feature(const String &p_feature) { if (p_feature == "release") return true; #endif +#ifdef TOOLS_ENABLED + if (p_feature == "editor") + return true; +#else + if (p_feature == "standalone") + return true; +#endif if (sizeof(void *) == 8 && p_feature == "64") { return true; @@ -614,6 +622,9 @@ bool OS::has_feature(const String &p_feature) { if (_check_internal_feature_support(p_feature)) return true; + if (ProjectSettings::get_singleton()->has_custom_feature(p_feature)) + return true; + return false; } @@ -656,9 +667,32 @@ const char *OS::get_audio_driver_name(int p_driver) const { return AudioDriverManager::get_driver(p_driver)->get_name(); } +void OS::set_restart_on_exit(bool p_restart, const List<String> &p_restart_arguments) { + restart_on_exit = p_restart; + restart_commandline = p_restart_arguments; +} + +bool OS::is_restart_on_exit_set() const { + return restart_on_exit; +} + +List<String> OS::get_restart_on_exit_arguments() const { + return restart_commandline; +} + +PoolStringArray OS::get_connected_midi_inputs() { + + if (MIDIDriver::get_singleton()) + return MIDIDriver::get_singleton()->get_connected_inputs(); + + PoolStringArray list; + return list; +} + OS::OS() { void *volatile stack_bottom; + restart_on_exit = false; last_error = NULL; singleton = this; _keep_screen_on = true; // set default value to true, because this had been true before godot 2.0. diff --git a/core/os/os.h b/core/os/os.h index b36f94060c..dd783408e8 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -74,6 +74,9 @@ class OS { CompositeLogger *_logger; + bool restart_on_exit; + List<String> restart_commandline; + protected: void _set_logger(CompositeLogger *p_logger); @@ -182,10 +185,12 @@ public: virtual int get_video_driver_count() const; virtual const char *get_video_driver_name(int p_driver) const; - + virtual int get_current_video_driver() const = 0; virtual int get_audio_driver_count() const; virtual const char *get_audio_driver_name(int p_driver) const; + virtual PoolStringArray get_connected_midi_inputs(); + virtual int get_screen_count() const { return 1; } virtual int get_current_screen() const { return 0; } virtual void set_current_screen(int p_screen) {} @@ -232,6 +237,7 @@ public: virtual Size2 get_layered_buffer_size() { return Size2(0, 0); } virtual void swap_layered_buffer() {} + virtual void set_ime_active(const bool p_active) {} virtual void set_ime_position(const Point2 &p_pos) {} virtual void set_ime_intermediate_text_callback(ImeCallback p_callback, void *p_inp) {} @@ -495,6 +501,11 @@ public: bool is_layered_allowed() const { return _allow_layered; } bool is_hidpi_allowed() const { return _allow_hidpi; } + + void set_restart_on_exit(bool p_restart, const List<String> &p_restart_arguments); + bool is_restart_on_exit_set() const; + List<String> get_restart_on_exit_arguments() const; + OS(); virtual ~OS(); }; diff --git a/core/os/threaded_array_processor.h b/core/os/threaded_array_processor.h index e0fb589767..3ff7db2a44 100644 --- a/core/os/threaded_array_processor.h +++ b/core/os/threaded_array_processor.h @@ -80,7 +80,7 @@ void thread_process_array(uint32_t p_elements, C *p_instance, M p_method, U p_us threads.resize(OS::get_singleton()->get_processor_count()); for (int i = 0; i < threads.size(); i++) { - threads[i] = Thread::create(process_array_thread<ThreadArrayProcessData<C, U> >, &data); + threads.write[i] = Thread::create(process_array_thread<ThreadArrayProcessData<C, U> >, &data); } for (int i = 0; i < threads.size(); i++) { diff --git a/core/packed_data_container.cpp b/core/packed_data_container.cpp index eaccdba9bf..45e060fa4a 100644 --- a/core/packed_data_container.cpp +++ b/core/packed_data_container.cpp @@ -251,7 +251,7 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd int len; encode_variant(p_data, NULL, len); tmpdata.resize(tmpdata.size() + len); - encode_variant(p_data, &tmpdata[pos], len); + encode_variant(p_data, &tmpdata.write[pos], len); return pos; } break; @@ -268,8 +268,8 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd uint32_t pos = tmpdata.size(); int len = d.size(); tmpdata.resize(tmpdata.size() + len * 12 + 8); - encode_uint32(TYPE_DICT, &tmpdata[pos + 0]); - encode_uint32(len, &tmpdata[pos + 4]); + encode_uint32(TYPE_DICT, &tmpdata.write[pos + 0]); + encode_uint32(len, &tmpdata.write[pos + 4]); List<Variant> keys; d.get_key_list(&keys); @@ -288,11 +288,11 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd int idx = 0; for (List<DictKey>::Element *E = sortk.front(); E; E = E->next()) { - encode_uint32(E->get().hash, &tmpdata[pos + 8 + idx * 12 + 0]); + encode_uint32(E->get().hash, &tmpdata.write[pos + 8 + idx * 12 + 0]); uint32_t ofs = _pack(E->get().key, tmpdata, string_cache); - encode_uint32(ofs, &tmpdata[pos + 8 + idx * 12 + 4]); + encode_uint32(ofs, &tmpdata.write[pos + 8 + idx * 12 + 4]); ofs = _pack(d[E->get().key], tmpdata, string_cache); - encode_uint32(ofs, &tmpdata[pos + 8 + idx * 12 + 8]); + encode_uint32(ofs, &tmpdata.write[pos + 8 + idx * 12 + 8]); idx++; } @@ -306,13 +306,13 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd uint32_t pos = tmpdata.size(); int len = a.size(); tmpdata.resize(tmpdata.size() + len * 4 + 8); - encode_uint32(TYPE_ARRAY, &tmpdata[pos + 0]); - encode_uint32(len, &tmpdata[pos + 4]); + encode_uint32(TYPE_ARRAY, &tmpdata.write[pos + 0]); + encode_uint32(len, &tmpdata.write[pos + 4]); for (int i = 0; i < len; i++) { uint32_t ofs = _pack(a[i], tmpdata, string_cache); - encode_uint32(ofs, &tmpdata[pos + 8 + i * 4]); + encode_uint32(ofs, &tmpdata.write[pos + 8 + i * 4]); } return pos; diff --git a/core/pool_allocator.cpp b/core/pool_allocator.cpp index 017586b92a..8952314212 100644 --- a/core/pool_allocator.cpp +++ b/core/pool_allocator.cpp @@ -359,7 +359,7 @@ Error PoolAllocator::resize(ID p_mem, int p_new_size) { //p_new_size = align(p_new_size) int _free = free_mem; // - static_area_size; - if ((_free + aligned(e->len)) - alloc_size < 0) { + if (uint32_t(_free + aligned(e->len)) < alloc_size) { mt_unlock(); ERR_FAIL_V(ERR_OUT_OF_MEMORY); }; diff --git a/core/project_settings.cpp b/core/project_settings.cpp index ac4a4b7d15..146b4870e8 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -105,6 +105,11 @@ void ProjectSettings::set_initial_value(const String &p_name, const Variant &p_v ERR_FAIL_COND(!props.has(p_name)); props[p_name].initial = p_value; } +void ProjectSettings::set_restart_if_changed(const String &p_name, bool p_restart) { + + ERR_FAIL_COND(!props.has(p_name)); + props[p_name].restart_if_changed = p_restart; +} String ProjectSettings::globalize_path(const String &p_path) const { @@ -137,7 +142,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { else { if (p_name == CoreStringNames::get_singleton()->_custom_features) { - Vector<String> custom_feature_array = p_value; + Vector<String> custom_feature_array = String(p_value).split(","); for (int i = 0; i < custom_feature_array.size(); i++) { custom_features.insert(custom_feature_array[i]); @@ -225,6 +230,9 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const { else vc.flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE; + if (v->restart_if_changed) { + vc.flags |= PROPERTY_USAGE_RESTART_IF_CHANGED; + } vclist.insert(vc); } @@ -515,7 +523,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; @@ -615,7 +627,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str Vector<uint8_t> buff; buff.resize(len); - err = encode_variant(p_custom_features, &buff[0], len); + err = encode_variant(p_custom_features, buff.ptrw(), len); if (err != OK) { memdelete(file); ERR_FAIL_V(err); @@ -652,7 +664,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str Vector<uint8_t> buff; buff.resize(len); - err = encode_variant(value, &buff[0], len); + err = encode_variant(value, buff.ptrw(), len); if (err != OK) memdelete(file); ERR_FAIL_COND_V(err != OK, ERR_INVALID_DATA); @@ -813,7 +825,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust return OK; } -Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default) { +Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed) { Variant ret; if (!ProjectSettings::get_singleton()->has_setting(p_var)) { @@ -823,6 +835,7 @@ Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default) { ProjectSettings::get_singleton()->set_initial_value(p_var, p_default); ProjectSettings::get_singleton()->set_builtin_order(p_var); + ProjectSettings::get_singleton()->set_restart_if_changed(p_var, p_restart_if_changed); return ret; } @@ -870,6 +883,10 @@ void ProjectSettings::set_custom_property_info(const String &p_prop, const Prope custom_prop_info[p_prop].name = p_prop; } +const Map<StringName, PropertyInfo> &ProjectSettings::get_custom_property_info() const { + return custom_prop_info; +} + void ProjectSettings::set_disable_feature_overrides(bool p_disable) { disable_feature_overrides = p_disable; @@ -904,6 +921,10 @@ Variant ProjectSettings::get_setting(const String &p_setting) const { return get(p_setting); } +bool ProjectSettings::has_custom_feature(const String &p_feature) const { + return custom_features.has(p_feature); +} + void ProjectSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("has_setting", "name"), &ProjectSettings::has_setting); @@ -938,7 +959,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("application/config/name", ""); GLOBAL_DEF("application/run/main_scene", ""); - custom_prop_info["application/run/main_scene"] = PropertyInfo(Variant::STRING, "application/run/main_scene", PROPERTY_HINT_FILE, "tscn,scn,res"); + custom_prop_info["application/run/main_scene"] = PropertyInfo(Variant::STRING, "application/run/main_scene", PROPERTY_HINT_FILE, "*.tscn,*.scn,*.res"); GLOBAL_DEF("application/run/disable_stdout", false); GLOBAL_DEF("application/run/disable_stderr", false); GLOBAL_DEF("application/config/use_custom_user_dir", false); @@ -1068,7 +1089,6 @@ ProjectSettings::ProjectSettings() { custom_prop_info["rendering/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/threads/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded"); custom_prop_info["physics/2d/thread_model"] = PropertyInfo(Variant::INT, "physics/2d/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded"); custom_prop_info["rendering/quality/intended_usage/framebuffer_allocation"] = PropertyInfo(Variant::INT, "rendering/quality/intended_usage/framebuffer_allocation", PROPERTY_HINT_ENUM, "2D,2D Without Sampling,3D,3D Without Effects"); - GLOBAL_DEF("rendering/quality/intended_usage/framebuffer_mode", 2); GLOBAL_DEF("debug/settings/profiler/max_functions", 16384); diff --git a/core/project_settings.h b/core/project_settings.h index b01e7855aa..75ebc5acc8 100644 --- a/core/project_settings.h +++ b/core/project_settings.h @@ -59,18 +59,21 @@ protected: Variant initial; bool hide_from_editor; bool overridden; + bool restart_if_changed; VariantContainer() : order(0), persist(false), hide_from_editor(false), - overridden(false) { + overridden(false), + restart_if_changed(false) { } VariantContainer(const Variant &p_variant, int p_order, bool p_persist = false) : order(p_order), persist(p_persist), variant(p_variant), hide_from_editor(false), - overridden(false) { + overridden(false), + restart_if_changed(false) { } }; @@ -120,6 +123,7 @@ public: String globalize_path(const String &p_path) const; void set_initial_value(const String &p_name, const Variant &p_value); + void set_restart_if_changed(const String &p_name, bool p_restart); bool property_can_revert(const String &p_name); Variant property_get_revert(const String &p_name); @@ -137,6 +141,7 @@ public: Error save_custom(const String &p_path = "", const CustomMap &p_custom = CustomMap(), const Vector<String> &p_custom_features = Vector<String>(), bool p_merge_with_current = true); Error save(); void set_custom_property_info(const String &p_prop, const PropertyInfo &p_info); + const Map<StringName, PropertyInfo> &get_custom_property_info() const; Vector<String> get_optimizer_presets() const; @@ -150,13 +155,16 @@ public: void set_registering_order(bool p_enable); + bool has_custom_feature(const String &p_feature) const; + ProjectSettings(); ~ProjectSettings(); }; //not a macro any longer -Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default); +Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed = false); #define GLOBAL_DEF(m_var, m_value) _GLOBAL_DEF(m_var, m_value) +#define GLOBAL_DEF_RST(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true) #define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var) #endif diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 2a611ccf6a..859015f44b 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -41,6 +41,7 @@ #include "input_map.h" #include "io/config_file.h" #include "io/http_client.h" +#include "io/image_loader.h" #include "io/marshalls.h" #include "io/multiplayer_api.h" #include "io/networked_multiplayer_peer.h" @@ -53,6 +54,7 @@ #include "io/tcp_server.h" #include "io/translation_loader_po.h" #include "math/a_star.h" +#include "math/expression.h" #include "math/triangle_mesh.h" #include "os/input.h" #include "os/main_loop.h" @@ -60,11 +62,14 @@ #include "path_remap.h" #include "project_settings.h" #include "translation.h" + #include "undo_redo.h" static ResourceFormatSaverBinary *resource_saver_binary = NULL; static ResourceFormatLoaderBinary *resource_loader_binary = NULL; static ResourceFormatImporter *resource_format_importer = NULL; +static ResourceFormatLoaderImage *resource_format_image = NULL; + static _ResourceLoader *_resource_loader = NULL; static _ResourceSaver *_resource_saver = NULL; static _OS *_os = NULL; @@ -111,6 +116,9 @@ void register_core_types() { resource_format_importer = memnew(ResourceFormatImporter); ResourceLoader::add_resource_format_loader(resource_format_importer); + resource_format_image = memnew(ResourceFormatLoaderImage); + ResourceLoader::add_resource_format_loader(resource_format_image); + ClassDB::register_class<Object>(); ClassDB::register_virtual_class<Script>(); @@ -191,7 +199,7 @@ void register_core_types() { void register_core_settings() { //since in register core types, globals may not e present - GLOBAL_DEF("network/limits/packet_peer_stream/max_buffer_po2", (16)); + GLOBAL_DEF_RST("network/limits/packet_peer_stream/max_buffer_po2", (16)); } void register_core_singletons() { @@ -209,6 +217,7 @@ void register_core_singletons() { ClassDB::register_virtual_class<Input>(); ClassDB::register_class<InputMap>(); ClassDB::register_class<_JSON>(); + ClassDB::register_class<Expression>(); Engine::get_singleton()->add_singleton(Engine::Singleton("ProjectSettings", ProjectSettings::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("IP", IP::get_singleton())); @@ -237,6 +246,8 @@ void unregister_core_types() { memdelete(_geometry); + if (resource_format_image) + memdelete(resource_format_image); if (resource_saver_binary) memdelete(resource_saver_binary); if (resource_loader_binary) diff --git a/core/resource.cpp b/core/resource.cpp index 179333aa14..87ff4d3c2a 100644 --- a/core/resource.cpp +++ b/core/resource.cpp @@ -187,7 +187,6 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Res void Resource::configure_for_local_scene(Node *p_for_scene, Map<Ref<Resource>, Ref<Resource> > &remap_cache) { - print_line("configure for local: " + get_class()); List<PropertyInfo> plist; get_property_list(&plist); @@ -226,15 +225,20 @@ Ref<Resource> Resource::duplicate(bool p_subresources) const { if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) continue; - Variant p = get(E->get().name).duplicate(true); - if (p.get_type() == Variant::OBJECT && p_subresources) { + Variant p = get(E->get().name); + + if ((p.get_type() == Variant::DICTIONARY || p.get_type() == Variant::ARRAY)) { + p = p.duplicate(p_subresources); //does not make a long of sense but should work? + } else if (p.get_type() == Variant::OBJECT && (p_subresources || (E->get().usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE))) { RES sr = p; - if (sr.is_valid()) - p = sr->duplicate(true); - } + if (sr.is_valid()) { + r->set(E->get().name, sr->duplicate(p_subresources)); + } + } else { - r->set(E->get().name, p); + r->set(E->get().name, p); + } } return Ref<Resource>(r); @@ -288,7 +292,7 @@ uint32_t Resource::hash_edited_version() const { for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - if (E->get().type == Variant::OBJECT && E->get().hint == PROPERTY_HINT_RESOURCE_TYPE) { + if (E->get().usage & PROPERTY_USAGE_STORAGE && E->get().type == Variant::OBJECT && E->get().hint == PROPERTY_HINT_RESOURCE_TYPE) { RES res = get(E->get().name); if (res.is_valid()) { hash = hash_djb2_one_32(res->hash_edited_version(), hash); diff --git a/core/ring_buffer.h b/core/ring_buffer.h index de4757612a..00628a4ab3 100644 --- a/core/ring_buffer.h +++ b/core/ring_buffer.h @@ -137,7 +137,7 @@ public: Error write(const T &p_v) { ERR_FAIL_COND_V(space_left() < 1, FAILED); - data[inc(write_pos, 1)] = p_v; + data.write[inc(write_pos, 1)] = p_v; return OK; }; @@ -156,7 +156,7 @@ public: int total = end - pos; for (int i = 0; i < total; i++) { - data[pos + i] = p_buf[src++]; + data.write[pos + i] = p_buf[src++]; }; to_write -= total; pos = 0; @@ -196,7 +196,7 @@ public: data.resize(1 << p_power); if (old_size < new_size && read_pos > write_pos) { for (int i = 0; i < write_pos; i++) { - data[(old_size + i) & mask] = data[i]; + data.write[(old_size + i) & mask] = data[i]; }; write_pos = (old_size + write_pos) & mask; } else { diff --git a/core/safe_refcount.cpp b/core/safe_refcount.cpp index 3b203f6977..692ff722f3 100644 --- a/core/safe_refcount.cpp +++ b/core/safe_refcount.cpp @@ -57,113 +57,113 @@ return m_val; \ } -_ALWAYS_INLINE_ uint32_t _atomic_conditional_increment_impl(register uint32_t *pw){ +_ALWAYS_INLINE_ uint32_t _atomic_conditional_increment_impl(volatile uint32_t *pw){ ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONG, InterlockedCompareExchange, uint32_t) } -_ALWAYS_INLINE_ uint32_t _atomic_decrement_impl(register uint32_t *pw) { +_ALWAYS_INLINE_ uint32_t _atomic_decrement_impl(volatile uint32_t *pw) { return InterlockedDecrement((LONG volatile *)pw); } -_ALWAYS_INLINE_ uint32_t _atomic_increment_impl(register uint32_t *pw) { +_ALWAYS_INLINE_ uint32_t _atomic_increment_impl(volatile uint32_t *pw) { return InterlockedIncrement((LONG volatile *)pw); } -_ALWAYS_INLINE_ uint32_t _atomic_sub_impl(register uint32_t *pw, register uint32_t val) { +_ALWAYS_INLINE_ uint32_t _atomic_sub_impl(volatile uint32_t *pw, volatile uint32_t val) { return InterlockedExchangeAdd((LONG volatile *)pw, -(int32_t)val) - val; } -_ALWAYS_INLINE_ uint32_t _atomic_add_impl(register uint32_t *pw, register uint32_t val) { +_ALWAYS_INLINE_ uint32_t _atomic_add_impl(volatile uint32_t *pw, volatile uint32_t val) { return InterlockedAdd((LONG volatile *)pw, val); } -_ALWAYS_INLINE_ uint32_t _atomic_exchange_if_greater_impl(register uint32_t *pw, register uint32_t val){ +_ALWAYS_INLINE_ uint32_t _atomic_exchange_if_greater_impl(volatile uint32_t *pw, volatile uint32_t val){ ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONG, InterlockedCompareExchange, uint32_t) } -_ALWAYS_INLINE_ uint64_t _atomic_conditional_increment_impl(register uint64_t *pw){ +_ALWAYS_INLINE_ uint64_t _atomic_conditional_increment_impl(volatile uint64_t *pw){ ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONGLONG, InterlockedCompareExchange64, uint64_t) } -_ALWAYS_INLINE_ uint64_t _atomic_decrement_impl(register uint64_t *pw) { +_ALWAYS_INLINE_ uint64_t _atomic_decrement_impl(volatile uint64_t *pw) { return InterlockedDecrement64((LONGLONG volatile *)pw); } -_ALWAYS_INLINE_ uint64_t _atomic_increment_impl(register uint64_t *pw) { +_ALWAYS_INLINE_ uint64_t _atomic_increment_impl(volatile uint64_t *pw) { return InterlockedIncrement64((LONGLONG volatile *)pw); } -_ALWAYS_INLINE_ uint64_t _atomic_sub_impl(register uint64_t *pw, register uint64_t val) { +_ALWAYS_INLINE_ uint64_t _atomic_sub_impl(volatile uint64_t *pw, volatile uint64_t val) { return InterlockedExchangeAdd64((LONGLONG volatile *)pw, -(int64_t)val) - val; } -_ALWAYS_INLINE_ uint64_t _atomic_add_impl(register uint64_t *pw, register uint64_t val) { +_ALWAYS_INLINE_ uint64_t _atomic_add_impl(volatile uint64_t *pw, volatile uint64_t val) { return InterlockedAdd64((LONGLONG volatile *)pw, val); } -_ALWAYS_INLINE_ uint64_t _atomic_exchange_if_greater_impl(register uint64_t *pw, register uint64_t val){ +_ALWAYS_INLINE_ uint64_t _atomic_exchange_if_greater_impl(volatile uint64_t *pw, volatile uint64_t val){ ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONGLONG, InterlockedCompareExchange64, uint64_t) } // The actual advertised functions; they'll call the right implementation -uint32_t atomic_conditional_increment(register uint32_t *pw) { +uint32_t atomic_conditional_increment(volatile uint32_t *pw) { return _atomic_conditional_increment_impl(pw); } -uint32_t atomic_decrement(register uint32_t *pw) { +uint32_t atomic_decrement(volatile uint32_t *pw) { return _atomic_decrement_impl(pw); } -uint32_t atomic_increment(register uint32_t *pw) { +uint32_t atomic_increment(volatile uint32_t *pw) { return _atomic_increment_impl(pw); } -uint32_t atomic_sub(register uint32_t *pw, register uint32_t val) { +uint32_t atomic_sub(volatile uint32_t *pw, volatile uint32_t val) { return _atomic_sub_impl(pw, val); } -uint32_t atomic_add(register uint32_t *pw, register uint32_t val) { +uint32_t atomic_add(volatile uint32_t *pw, volatile uint32_t val) { return _atomic_add_impl(pw, val); } -uint32_t atomic_exchange_if_greater(register uint32_t *pw, register uint32_t val) { +uint32_t atomic_exchange_if_greater(volatile uint32_t *pw, volatile uint32_t val) { return _atomic_exchange_if_greater_impl(pw, val); } -uint64_t atomic_conditional_increment(register uint64_t *pw) { +uint64_t atomic_conditional_increment(volatile uint64_t *pw) { return _atomic_conditional_increment_impl(pw); } -uint64_t atomic_decrement(register uint64_t *pw) { +uint64_t atomic_decrement(volatile uint64_t *pw) { return _atomic_decrement_impl(pw); } -uint64_t atomic_increment(register uint64_t *pw) { +uint64_t atomic_increment(volatile uint64_t *pw) { return _atomic_increment_impl(pw); } -uint64_t atomic_sub(register uint64_t *pw, register uint64_t val) { +uint64_t atomic_sub(volatile uint64_t *pw, volatile uint64_t val) { return _atomic_sub_impl(pw, val); } -uint64_t atomic_add(register uint64_t *pw, register uint64_t val) { +uint64_t atomic_add(volatile uint64_t *pw, volatile uint64_t val) { return _atomic_add_impl(pw, val); } -uint64_t atomic_exchange_if_greater(register uint64_t *pw, register uint64_t val) { +uint64_t atomic_exchange_if_greater(volatile uint64_t *pw, volatile uint64_t val) { return _atomic_exchange_if_greater_impl(pw, val); } #endif diff --git a/core/safe_refcount.h b/core/safe_refcount.h index eff209c2db..36bcf5e576 100644 --- a/core/safe_refcount.h +++ b/core/safe_refcount.h @@ -44,7 +44,7 @@ /* Bogus implementation unaware of multiprocessing */ template <class T> -static _ALWAYS_INLINE_ T atomic_conditional_increment(register T *pw) { +static _ALWAYS_INLINE_ T atomic_conditional_increment(volatile T *pw) { if (*pw == 0) return 0; @@ -55,7 +55,7 @@ static _ALWAYS_INLINE_ T atomic_conditional_increment(register T *pw) { } template <class T> -static _ALWAYS_INLINE_ T atomic_decrement(register T *pw) { +static _ALWAYS_INLINE_ T atomic_decrement(volatile T *pw) { (*pw)--; @@ -63,7 +63,7 @@ static _ALWAYS_INLINE_ T atomic_decrement(register T *pw) { } template <class T> -static _ALWAYS_INLINE_ T atomic_increment(register T *pw) { +static _ALWAYS_INLINE_ T atomic_increment(volatile T *pw) { (*pw)++; @@ -71,7 +71,7 @@ static _ALWAYS_INLINE_ T atomic_increment(register T *pw) { } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_sub(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_sub(volatile T *pw, volatile V val) { (*pw) -= val; @@ -79,7 +79,7 @@ static _ALWAYS_INLINE_ T atomic_sub(register T *pw, register V val) { } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_add(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_add(volatile T *pw, volatile V val) { (*pw) += val; @@ -87,7 +87,7 @@ static _ALWAYS_INLINE_ T atomic_add(register T *pw, register V val) { } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_exchange_if_greater(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_exchange_if_greater(volatile T *pw, volatile V val) { if (val > *pw) *pw = val; @@ -103,7 +103,7 @@ static _ALWAYS_INLINE_ T atomic_exchange_if_greater(register T *pw, register V v // Clang states it supports GCC atomic builtins. template <class T> -static _ALWAYS_INLINE_ T atomic_conditional_increment(register T *pw) { +static _ALWAYS_INLINE_ T atomic_conditional_increment(volatile T *pw) { while (true) { T tmp = static_cast<T const volatile &>(*pw); @@ -115,31 +115,31 @@ static _ALWAYS_INLINE_ T atomic_conditional_increment(register T *pw) { } template <class T> -static _ALWAYS_INLINE_ T atomic_decrement(register T *pw) { +static _ALWAYS_INLINE_ T atomic_decrement(volatile T *pw) { return __sync_sub_and_fetch(pw, 1); } template <class T> -static _ALWAYS_INLINE_ T atomic_increment(register T *pw) { +static _ALWAYS_INLINE_ T atomic_increment(volatile T *pw) { return __sync_add_and_fetch(pw, 1); } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_sub(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_sub(volatile T *pw, volatile V val) { return __sync_sub_and_fetch(pw, val); } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_add(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_add(volatile T *pw, volatile V val) { return __sync_add_and_fetch(pw, val); } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_exchange_if_greater(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_exchange_if_greater(volatile T *pw, volatile V val) { while (true) { T tmp = static_cast<T const volatile &>(*pw); @@ -153,19 +153,19 @@ static _ALWAYS_INLINE_ T atomic_exchange_if_greater(register T *pw, register V v #elif defined(_MSC_VER) // For MSVC use a separate compilation unit to prevent windows.h from polluting // the global namespace. -uint32_t atomic_conditional_increment(register uint32_t *pw); -uint32_t atomic_decrement(register uint32_t *pw); -uint32_t atomic_increment(register uint32_t *pw); -uint32_t atomic_sub(register uint32_t *pw, register uint32_t val); -uint32_t atomic_add(register uint32_t *pw, register uint32_t val); -uint32_t atomic_exchange_if_greater(register uint32_t *pw, register uint32_t val); - -uint64_t atomic_conditional_increment(register uint64_t *pw); -uint64_t atomic_decrement(register uint64_t *pw); -uint64_t atomic_increment(register uint64_t *pw); -uint64_t atomic_sub(register uint64_t *pw, register uint64_t val); -uint64_t atomic_add(register uint64_t *pw, register uint64_t val); -uint64_t atomic_exchange_if_greater(register uint64_t *pw, register uint64_t val); +uint32_t atomic_conditional_increment(volatile uint32_t *pw); +uint32_t atomic_decrement(volatile uint32_t *pw); +uint32_t atomic_increment(volatile uint32_t *pw); +uint32_t atomic_sub(volatile uint32_t *pw, volatile uint32_t val); +uint32_t atomic_add(volatile uint32_t *pw, volatile uint32_t val); +uint32_t atomic_exchange_if_greater(volatile uint32_t *pw, volatile uint32_t val); + +uint64_t atomic_conditional_increment(volatile uint64_t *pw); +uint64_t atomic_decrement(volatile uint64_t *pw); +uint64_t atomic_increment(volatile uint64_t *pw); +uint64_t atomic_sub(volatile uint64_t *pw, volatile uint64_t val); +uint64_t atomic_add(volatile uint64_t *pw, volatile uint64_t val); +uint64_t atomic_exchange_if_greater(volatile uint64_t *pw, volatile uint64_t val); #else //no threads supported? diff --git a/core/script_debugger_local.cpp b/core/script_debugger_local.cpp index 55d7270473..6949b5802b 100644 --- a/core/script_debugger_local.cpp +++ b/core/script_debugger_local.cpp @@ -325,7 +325,7 @@ void ScriptDebuggerLocal::idle_poll() { int ofs = 0; for (int i = 0; i < ScriptServer::get_language_count(); i++) { - ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&pinfo[ofs], pinfo.size() - ofs); + ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&pinfo.write[ofs], pinfo.size() - ofs); } SortArray<ScriptLanguage::ProfilingInfo, _ScriptDebuggerLocalProfileInfoSort> sort; @@ -377,7 +377,7 @@ void ScriptDebuggerLocal::profiling_end() { int ofs = 0; for (int i = 0; i < ScriptServer::get_language_count(); i++) { - ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&pinfo[ofs], pinfo.size() - ofs); + ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&pinfo.write[ofs], pinfo.size() - ofs); } SortArray<ScriptLanguage::ProfilingInfo, _ScriptDebuggerLocalProfileInfoSort> sort; diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp index 75bcedbbc8..c5daaeea47 100644 --- a/core/script_debugger_remote.cpp +++ b/core/script_debugger_remote.cpp @@ -169,6 +169,10 @@ void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue) ERR_FAIL(); } + if (allow_focus_steal_pid) { + OS::get_singleton()->enable_for_stealing_focus(allow_focus_steal_pid); + } + packet_peer_stream->put_var("debug_enter"); packet_peer_stream->put_var(2); packet_peer_stream->put_var(p_can_continue); @@ -567,22 +571,46 @@ void ScriptDebuggerRemote::_send_object_id(ObjectID p_id) { if (ScriptInstance *si = obj->get_script_instance()) { if (!si->get_script().is_null()) { - Set<StringName> members; - si->get_script()->get_members(&members); - for (Set<StringName>::Element *E = members.front(); E; E = E->next()) { + typedef Map<const Script *, Set<StringName> > ScriptMemberMap; + typedef Map<const Script *, Map<StringName, Variant> > ScriptConstantsMap; + + ScriptMemberMap members; + members[si->get_script().ptr()] = Set<StringName>(); + si->get_script()->get_members(&(members[si->get_script().ptr()])); + + ScriptConstantsMap constants; + constants[si->get_script().ptr()] = Map<StringName, Variant>(); + si->get_script()->get_constants(&(constants[si->get_script().ptr()])); + + Ref<Script> base = si->get_script()->get_base_script(); + while (base.is_valid()) { + + members[base.ptr()] = Set<StringName>(); + base->get_members(&(members[base.ptr()])); + + constants[base.ptr()] = Map<StringName, Variant>(); + base->get_constants(&(constants[base.ptr()])); + + base = base->get_base_script(); + } - Variant m; - if (si->get(E->get(), m)) { - PropertyInfo pi(m.get_type(), String("Members/") + E->get()); - properties.push_back(PropertyDesc(pi, m)); + for (ScriptMemberMap::Element *sm = members.front(); sm; sm = sm->next()) { + for (Set<StringName>::Element *E = sm->get().front(); E; E = E->next()) { + Variant m; + if (si->get(E->get(), m)) { + String script_path = sm->key() == si->get_script().ptr() ? "" : sm->key()->get_path().get_file() + "/"; + PropertyInfo pi(m.get_type(), "Members/" + script_path + E->get()); + properties.push_back(PropertyDesc(pi, m)); + } } } - Map<StringName, Variant> constants; - si->get_script()->get_constants(&constants); - for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) { - PropertyInfo pi(E->value().get_type(), (String("Constants/") + E->key())); - properties.push_back(PropertyDesc(pi, E->value())); + for (ScriptConstantsMap::Element *sc = constants.front(); sc; sc = sc->next()) { + for (Map<StringName, Variant>::Element *E = sc->get().front(); E; E = E->next()) { + String script_path = sc->key() == si->get_script().ptr() ? "" : sc->key()->get_path().get_file() + "/"; + PropertyInfo pi(E->value().get_type(), "Constants/" + script_path + E->key()); + properties.push_back(PropertyDesc(pi, E->value())); + } } } } @@ -658,8 +686,10 @@ void ScriptDebuggerRemote::_set_object_property(ObjectID p_id, const String &p_p return; String prop_name = p_property; - if (p_property.begins_with("Members/")) - prop_name = p_property.substr(8, p_property.length()); + if (p_property.begins_with("Members/")) { + Vector<String> ss = p_property.split("/"); + prop_name = ss[ss.size() - 1]; + } obj->set(prop_name, p_value); } @@ -753,13 +783,13 @@ void ScriptDebuggerRemote::_send_profiling_data(bool p_for_frame) { for (int i = 0; i < ScriptServer::get_language_count(); i++) { if (p_for_frame) - ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&profile_info[ofs], profile_info.size() - ofs); + ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&profile_info.write[ofs], profile_info.size() - ofs); else - ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&profile_info[ofs], profile_info.size() - ofs); + ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&profile_info.write[ofs], profile_info.size() - ofs); } for (int i = 0; i < ofs; i++) { - profile_info_ptrs[i] = &profile_info[i]; + profile_info_ptrs.write[i] = &profile_info.write[i]; } SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa; @@ -1024,7 +1054,7 @@ void ScriptDebuggerRemote::add_profiling_frame_data(const StringName &p_name, co if (idx == -1) { profile_frame_data.push_back(fd); } else { - profile_frame_data[idx] = fd; + profile_frame_data.write[idx] = fd; } } @@ -1044,6 +1074,10 @@ void ScriptDebuggerRemote::profiling_set_frame_times(float p_frame_time, float p physics_frame_time = p_physics_frame_time; } +void ScriptDebuggerRemote::set_allow_focus_steal_pid(OS::ProcessID p_pid) { + allow_focus_steal_pid = p_pid; +} + ScriptDebuggerRemote::ResourceUsageFunc ScriptDebuggerRemote::resource_usage_func = NULL; ScriptDebuggerRemote::ScriptDebuggerRemote() : @@ -1065,6 +1099,7 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() : n_errors_dropped(0), last_msec(0), msec_count(0), + allow_focus_steal_pid(0), locking(false), poll_every(0), request_scene_tree(NULL), diff --git a/core/script_debugger_remote.h b/core/script_debugger_remote.h index cc12d978d6..b68fc4f9c9 100644 --- a/core/script_debugger_remote.h +++ b/core/script_debugger_remote.h @@ -34,6 +34,7 @@ #include "io/packet_peer.h" #include "io/stream_peer_tcp.h" #include "list.h" +#include "os/os.h" #include "script_language.h" class ScriptDebuggerRemote : public ScriptDebugger { @@ -98,6 +99,8 @@ class ScriptDebuggerRemote : public ScriptDebugger { uint64_t last_msec; uint64_t msec_count; + OS::ProcessID allow_focus_steal_pid; + bool locking; //hack to avoid a deadloop static void _print_handler(void *p_this, const String &p_string, bool p_error); @@ -171,6 +174,8 @@ public: virtual void profiling_end(); virtual void profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time); + void set_allow_focus_steal_pid(OS::ProcessID p_pid); + ScriptDebuggerRemote(); ~ScriptDebuggerRemote(); }; diff --git a/core/script_language.cpp b/core/script_language.cpp index 1dab58e29e..37ba3cfc62 100644 --- a/core/script_language.cpp +++ b/core/script_language.cpp @@ -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; @@ -347,6 +424,20 @@ Variant::Type PlaceHolderScriptInstance::get_property_type(const StringName &p_n return Variant::NIL; } +void PlaceHolderScriptInstance::get_method_list(List<MethodInfo> *p_list) const { + + if (script.is_valid()) { + script->get_script_method_list(p_list); + } +} +bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const { + + if (script.is_valid()) { + return script->has_method(p_method); + } + return false; +} + void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, const Map<StringName, Variant> &p_values) { Set<StringName> new_values; diff --git a/core/script_language.h b/core/script_language.h index b4c55cac9e..4e81b9b626 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -31,6 +31,7 @@ #ifndef SCRIPT_LANGUAGE_H #define SCRIPT_LANGUAGE_H +#include "io/multiplayer_api.h" #include "map.h" #include "pair.h" #include "resource.h" @@ -53,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; @@ -69,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(); }; @@ -157,16 +176,8 @@ public: virtual bool is_placeholder() const { return false; } - enum RPCMode { - RPC_MODE_DISABLED, - RPC_MODE_REMOTE, - RPC_MODE_SYNC, - RPC_MODE_MASTER, - RPC_MODE_SLAVE, - }; - - virtual RPCMode get_rpc_mode(const StringName &p_method) const = 0; - virtual RPCMode get_rset_mode(const StringName &p_variable) const = 0; + virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const = 0; + virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const = 0; virtual ScriptLanguage *get_language() = 0; virtual ~ScriptInstance(); @@ -202,7 +213,7 @@ public: virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const = 0; virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {} virtual bool is_using_templates() { return false; } - virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL) const = 0; + virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, Set<int> *r_safe_lines = NULL) const = 0; virtual String validate_path(const String &p_path) const { return ""; } virtual Script *create_script() const = 0; virtual bool has_named_classes() const = 0; @@ -292,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]; @@ -311,8 +325,8 @@ public: virtual void get_property_list(List<PropertyInfo> *p_properties) const; virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = NULL) const; - virtual void get_method_list(List<MethodInfo> *p_list) const {} - virtual bool has_method(const StringName &p_method) const { return false; } + virtual void get_method_list(List<MethodInfo> *p_list) const; + virtual bool has_method(const StringName &p_method) const; virtual Variant call(const StringName &p_method, VARIANT_ARG_LIST) { return Variant(); } virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) { r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; @@ -332,8 +346,8 @@ public: virtual bool is_placeholder() const { return true; } - virtual RPCMode get_rpc_mode(const StringName &p_method) const { return RPC_MODE_DISABLED; } - virtual RPCMode get_rset_mode(const StringName &p_variable) const { return RPC_MODE_DISABLED; } + virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const { return MultiplayerAPI::RPC_MODE_DISABLED; } + virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const { return MultiplayerAPI::RPC_MODE_DISABLED; } PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner); ~PlaceHolderScriptInstance(); diff --git a/core/sort.h b/core/sort.h index a6780309d8..97983829e1 100644 --- a/core/sort.h +++ b/core/sort.h @@ -36,13 +36,25 @@ @author ,,, <red@lunatea> */ +#define ERR_BAD_COMPARE(cond) \ + if (unlikely(cond)) { \ + ERR_PRINT("bad comparison function; sorting will be broken"); \ + break; \ + } + template <class T> struct _DefaultComparator { - inline bool operator()(const T &a, const T &b) const { return (a < b); } + _FORCE_INLINE_ bool operator()(const T &a, const T &b) const { return (a < b); } }; -template <class T, class Comparator = _DefaultComparator<T> > +#ifdef DEBUG_ENABLED +#define SORT_ARRAY_VALIDATE_ENABLED true +#else +#define SORT_ARRAY_VALIDATE_ENABLED false +#endif + +template <class T, class Comparator = _DefaultComparator<T>, bool Validate = SORT_ARRAY_VALIDATE_ENABLED> class SortArray { enum { @@ -164,12 +176,23 @@ public: inline int partitioner(int p_first, int p_last, T p_pivot, T *p_array) const { + const int unmodified_first = p_first; + const int unmodified_last = p_last; + while (true) { - while (compare(p_array[p_first], p_pivot)) + while (compare(p_array[p_first], p_pivot)) { + if (Validate) { + ERR_BAD_COMPARE(p_first == unmodified_last - 1) + } p_first++; + } p_last--; - while (compare(p_pivot, p_array[p_last])) + while (compare(p_pivot, p_array[p_last])) { + if (Validate) { + ERR_BAD_COMPARE(p_last == unmodified_first) + } p_last--; + } if (!(p_first < p_last)) return p_first; @@ -238,6 +261,9 @@ public: int next = p_last - 1; while (compare(p_value, p_array[next])) { + if (Validate) { + ERR_BAD_COMPARE(next == 0) + } p_array[p_last] = p_array[next]; p_last = next; next--; diff --git a/core/string_buffer.h b/core/string_buffer.h index 7e9b151bea..5d3be0ecf1 100644 --- a/core/string_buffer.h +++ b/core/string_buffer.h @@ -42,7 +42,7 @@ class StringBuffer { int string_length; _FORCE_INLINE_ CharType *current_buffer_ptr() { - return static_cast<Vector<CharType> &>(buffer).empty() ? short_buffer : buffer.ptrw(); + return static_cast<String &>(buffer).empty() ? short_buffer : buffer.ptrw(); } public: diff --git a/core/string_db.h b/core/string_db.h index 01d1ca4033..965385b136 100644 --- a/core/string_db.h +++ b/core/string_db.h @@ -31,7 +31,6 @@ #ifndef STRING_DB_H #define STRING_DB_H -#include "hash_map.h" #include "os/mutex.h" #include "safe_refcount.h" #include "ustring.h" @@ -168,11 +167,6 @@ public: ~StringName(); }; -struct StringNameHasher { - - static _FORCE_INLINE_ uint32_t hash(const StringName &p_string) { return p_string.hash(); } -}; - StringName _scs_create(const char *p_chr); #endif diff --git a/core/translation.cpp b/core/translation.cpp index aaa4de5912..78115c3749 100644 --- a/core/translation.cpp +++ b/core/translation.cpp @@ -1171,13 +1171,11 @@ void TranslationServer::_bind_methods() { void TranslationServer::load_translations() { String locale = get_locale(); - bool found = _load_translations("locale/translations"); //all + _load_translations("locale/translations"); //all + _load_translations("locale/translations_" + locale.substr(0, 2)); - if (_load_translations("locale/translations_" + locale.substr(0, 2))) - found = true; if (locale.substr(0, 2) != locale) { - if (_load_translations("locale/translations_" + locale)) - found = true; + _load_translations("locale/translations_" + locale); } } diff --git a/core/type_info.h b/core/type_info.h index c1af4fac69..bf497f1e5f 100644 --- a/core/type_info.h +++ b/core/type_info.h @@ -194,6 +194,7 @@ MAKE_TEMPLATE_TYPE_INFO(Vector, Color, Variant::POOL_COLOR_ARRAY) MAKE_TEMPLATE_TYPE_INFO(Vector, Variant, Variant::ARRAY) MAKE_TEMPLATE_TYPE_INFO(Vector, RID, Variant::ARRAY) MAKE_TEMPLATE_TYPE_INFO(Vector, Plane, Variant::ARRAY) +MAKE_TEMPLATE_TYPE_INFO(Vector, StringName, Variant::POOL_STRING_ARRAY) MAKE_TEMPLATE_TYPE_INFO(PoolVector, Plane, Variant::ARRAY) MAKE_TEMPLATE_TYPE_INFO(PoolVector, Face3, Variant::POOL_VECTOR3_ARRAY) diff --git a/core/typedefs.h b/core/typedefs.h index 4758a5408d..57afa3bdb4 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -58,12 +58,8 @@ #endif #ifndef _FORCE_INLINE_ -#ifdef DEBUG_ENABLED -#define _FORCE_INLINE_ inline -#else #define _FORCE_INLINE_ _ALWAYS_INLINE_ #endif -#endif //custom, gcc-safe offsetof, because gcc complains a lot. template <class T> @@ -74,7 +70,7 @@ T *_nullptr() { #define OFFSET_OF(st, m) \ ((size_t)((char *)&(_nullptr<st>()->m) - (char *)0)) - /** +/** * Some platforms (devices) not define NULL */ @@ -82,7 +78,7 @@ T *_nullptr() { #define NULL 0 #endif - /** +/** * Windows defines a lot of badly stuff we'll never ever use. undefine it. */ @@ -97,6 +93,7 @@ T *_nullptr() { #undef CLAMP // override standard definition #undef Error #undef OK +#undef CONNECT_DEFERRED // override from Windows SDK, clashes with Object enum #endif #include "int_types.h" @@ -104,7 +101,7 @@ T *_nullptr() { #include "error_list.h" #include "error_macros.h" - /** Generic ABS function, for math uses please use Math::abs */ +/** Generic ABS function, for math uses please use Math::abs */ #ifndef ABS #define ABS(m_v) ((m_v < 0) ? (-(m_v)) : (m_v)) @@ -266,7 +263,7 @@ static inline uint64_t BSWAP64(uint64_t x) { template <class T> struct Comparator { - inline bool operator()(const T &p_a, const T &p_b) const { return (p_a < p_b); } + _ALWAYS_INLINE_ bool operator()(const T &p_a, const T &p_b) const { return (p_a < p_b); } }; void _global_lock(); diff --git a/core/undo_redo.cpp b/core/undo_redo.cpp index b3f9dd818d..3d90608dd7 100644 --- a/core/undo_redo.cpp +++ b/core/undo_redo.cpp @@ -39,7 +39,7 @@ void UndoRedo::_discard_redo() { for (int i = current_action + 1; i < actions.size(); i++) { - for (List<Operation>::Element *E = actions[i].do_ops.front(); E; E = E->next()) { + for (List<Operation>::Element *E = actions.write[i].do_ops.front(); E; E = E->next()) { if (E->get().type == Operation::TYPE_REFERENCE) { @@ -70,7 +70,7 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { if (p_mode == MERGE_ENDS) { // Clear all do ops from last action, and delete all object references - List<Operation>::Element *E = actions[current_action + 1].do_ops.front(); + List<Operation>::Element *E = actions.write[current_action + 1].do_ops.front(); while (E) { @@ -83,11 +83,11 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { } E = E->next(); - actions[current_action + 1].do_ops.pop_front(); + actions.write[current_action + 1].do_ops.pop_front(); } } - actions[actions.size() - 1].last_tick = ticks; + actions.write[actions.size() - 1].last_tick = ticks; merge_mode = p_mode; @@ -122,7 +122,7 @@ void UndoRedo::add_do_method(Object *p_object, const String &p_method, VARIANT_A for (int i = 0; i < VARIANT_ARG_MAX; i++) { do_op.args[i] = *argptr[i]; } - actions[current_action + 1].do_ops.push_back(do_op); + actions.write[current_action + 1].do_ops.push_back(do_op); } void UndoRedo::add_undo_method(Object *p_object, const String &p_method, VARIANT_ARG_DECLARE) { @@ -147,7 +147,7 @@ void UndoRedo::add_undo_method(Object *p_object, const String &p_method, VARIANT for (int i = 0; i < VARIANT_ARG_MAX; i++) { undo_op.args[i] = *argptr[i]; } - actions[current_action + 1].undo_ops.push_back(undo_op); + actions.write[current_action + 1].undo_ops.push_back(undo_op); } void UndoRedo::add_do_property(Object *p_object, const String &p_property, const Variant &p_value) { @@ -162,7 +162,7 @@ void UndoRedo::add_do_property(Object *p_object, const String &p_property, const do_op.type = Operation::TYPE_PROPERTY; do_op.name = p_property; do_op.args[0] = p_value; - actions[current_action + 1].do_ops.push_back(do_op); + actions.write[current_action + 1].do_ops.push_back(do_op); } void UndoRedo::add_undo_property(Object *p_object, const String &p_property, const Variant &p_value) { @@ -182,7 +182,7 @@ void UndoRedo::add_undo_property(Object *p_object, const String &p_property, con undo_op.type = Operation::TYPE_PROPERTY; undo_op.name = p_property; undo_op.args[0] = p_value; - actions[current_action + 1].undo_ops.push_back(undo_op); + actions.write[current_action + 1].undo_ops.push_back(undo_op); } void UndoRedo::add_do_reference(Object *p_object) { @@ -195,7 +195,7 @@ void UndoRedo::add_do_reference(Object *p_object) { do_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object)); do_op.type = Operation::TYPE_REFERENCE; - actions[current_action + 1].do_ops.push_back(do_op); + actions.write[current_action + 1].do_ops.push_back(do_op); } void UndoRedo::add_undo_reference(Object *p_object) { @@ -213,7 +213,7 @@ void UndoRedo::add_undo_reference(Object *p_object) { undo_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object)); undo_op.type = Operation::TYPE_REFERENCE; - actions[current_action + 1].undo_ops.push_back(undo_op); + actions.write[current_action + 1].undo_ops.push_back(undo_op); } void UndoRedo::_pop_history_tail() { @@ -223,7 +223,7 @@ void UndoRedo::_pop_history_tail() { if (!actions.size()) return; - for (List<Operation>::Element *E = actions[0].undo_ops.front(); E; E = E->next()) { + for (List<Operation>::Element *E = actions.write[0].undo_ops.front(); E; E = E->next()) { if (E->get().type == Operation::TYPE_REFERENCE) { @@ -299,26 +299,30 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) { } } -void UndoRedo::redo() { +bool UndoRedo::redo() { - ERR_FAIL_COND(action_level > 0); + ERR_FAIL_COND_V(action_level > 0, false); if ((current_action + 1) >= actions.size()) - return; //nothing to redo + return false; //nothing to redo current_action++; - _process_operation_list(actions[current_action].do_ops.front()); + _process_operation_list(actions.write[current_action].do_ops.front()); version++; + + return true; } -void UndoRedo::undo() { +bool UndoRedo::undo() { - ERR_FAIL_COND(action_level > 0); + ERR_FAIL_COND_V(action_level > 0, false); if (current_action < 0) - return; //nothing to redo - _process_operation_list(actions[current_action].undo_ops.front()); + return false; //nothing to redo + _process_operation_list(actions.write[current_action].undo_ops.front()); current_action--; version--; + + return true; } void UndoRedo::clear_history() { diff --git a/core/undo_redo.h b/core/undo_redo.h index a373296b73..3a17c78851 100644 --- a/core/undo_redo.h +++ b/core/undo_redo.h @@ -109,8 +109,8 @@ public: void commit_action(); - void redo(); - void undo(); + bool redo(); + bool undo(); String get_current_action_name() const; void clear_history(); diff --git a/core/ustring.cpp b/core/ustring.cpp index 85b7a16e6a..84613610a9 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -102,6 +102,15 @@ bool CharString::operator<(const CharString &p_right) const { return is_str_less(get_data(), p_right.get_data()); } +CharString &CharString::operator+=(char p_char) { + + resize(size() ? size() + 1 : 2); + set(length(), 0); + set(length() - 1, p_char); + + return *this; +} + const char *CharString::get_data() const { if (size()) @@ -161,14 +170,21 @@ void String::copy_from(const CharType *p_cstr, int p_clip_to) { return; } - resize(len + 1); - set(len, 0); + copy_from_unchecked(p_cstr, len); +} + +// assumes the following have already been validated: +// p_char != NULL +// p_length > 0 +// p_length <= p_char strlen +void String::copy_from_unchecked(const CharType *p_char, int p_length) { + resize(p_length + 1); + set(p_length, 0); CharType *dst = &operator[](0); - for (int i = 0; i < len; i++) { - - dst[i] = p_cstr[i]; + for (int i = 0; i < p_length; i++) { + dst[i] = p_char[i]; } } @@ -753,6 +769,42 @@ Vector<String> String::split(const String &p_splitter, bool p_allow_empty, int p return ret; } +Vector<String> String::rsplit(const String &p_splitter, bool p_allow_empty, int p_maxsplit) const { + + Vector<String> ret; + const int len = length(); + int remaining_len = len; + + while (true) { + + if (remaining_len < p_splitter.length() || (p_maxsplit > 0 && p_maxsplit == ret.size())) { + // no room for another splitter or hit max splits, push what's left and we're done + if (p_allow_empty || remaining_len > 0) { + ret.push_back(substr(0, remaining_len)); + } + break; + } + + int left_edge = rfind(p_splitter, remaining_len - p_splitter.length()); + + if (left_edge < 0) { + // no more splitters, we're done + ret.push_back(substr(0, remaining_len)); + break; + } + + int substr_start = left_edge + p_splitter.length(); + if (p_allow_empty || substr_start < remaining_len) { + ret.push_back(substr(substr_start, remaining_len - substr_start)); + } + + remaining_len = left_edge; + } + + ret.invert(); + return ret; +} + Vector<float> String::split_floats(const String &p_splitter, bool p_allow_empty) const { Vector<float> ret; @@ -885,8 +937,8 @@ String String::to_upper() const { for (int i = 0; i < upper.size(); i++) { - const char s = upper[i]; - const char t = _find_upper(s); + const CharType s = upper[i]; + const CharType t = _find_upper(s); if (s != t) // avoid copy on write upper[i] = t; } @@ -900,8 +952,8 @@ String String::to_lower() const { for (int i = 0; i < lower.size(); i++) { - const char s = lower[i]; - const char t = _find_lower(s); + const CharType s = lower[i]; + const CharType t = _find_lower(s); if (s != t) // avoid copy on write lower[i] = t; } @@ -1535,6 +1587,7 @@ String::String(const char *p_str) { copy_from(p_str); } + String::String(const CharType *p_str, int p_clip_to_len) { copy_from(p_str, p_clip_to_len); @@ -2154,7 +2207,7 @@ Vector<uint8_t> String::md5_buffer() const { Vector<uint8_t> ret; ret.resize(16); for (int i = 0; i < 16; i++) { - ret[i] = ctx.digest[i]; + ret.write[i] = ctx.digest[i]; }; return ret; @@ -2171,7 +2224,7 @@ Vector<uint8_t> String::sha256_buffer() const { Vector<uint8_t> ret; ret.resize(32); for (int i = 0; i < 32; i++) { - ret[i] = hash[i]; + ret.write[i] = hash[i]; } return ret; @@ -2210,7 +2263,9 @@ String String::substr(int p_from, int p_chars) const { return String(*this); } - return String(&c_str()[p_from], p_chars); + String s = String(); + s.copy_from_unchecked(&c_str()[p_from], p_chars); + return s; } int String::find_last(const String &p_str) const { @@ -2628,7 +2683,7 @@ Vector<String> String::bigrams() const { } b.resize(n_pairs); for (int i = 0; i < n_pairs; i++) { - b[i] = substr(i, 2); + b.write[i] = substr(i, 2); } return b; } @@ -2723,7 +2778,7 @@ String String::format(const Variant &values, String placeholder) const { val = val.substr(1, val.length() - 2); } - new_string = new_string.replacen(placeholder.replace("_", key), val); + new_string = new_string.replace(placeholder.replace("_", key), val); } else { ERR_PRINT(String("STRING.format Inner Array size != 2 ").ascii().get_data()); } @@ -2736,7 +2791,7 @@ String String::format(const Variant &values, String placeholder) const { val = val.substr(1, val.length() - 2); } - new_string = new_string.replacen(placeholder.replace("_", i_as_str), val); + new_string = new_string.replace(placeholder.replace("_", i_as_str), val); } } } else if (values.get_type() == Variant::DICTIONARY) { @@ -2756,7 +2811,7 @@ String String::format(const Variant &values, String placeholder) const { val = val.substr(1, val.length() - 2); } - new_string = new_string.replacen(placeholder.replace("_", key), val); + new_string = new_string.replace(placeholder.replace("_", key), val); } } else { ERR_PRINT(String("Invalid type: use Array or Dictionary.").ascii().get_data()); @@ -2987,14 +3042,14 @@ String String::strip_escapes() const { return substr(beg, end - beg); } -String String::lstrip(const Vector<CharType> &p_chars) const { +String String::lstrip(const String &p_chars) const { int len = length(); int beg; for (beg = 0; beg < len; beg++) { - if (p_chars.find(operator[](beg)) == -1) + if (p_chars.find(&ptr()[beg]) == -1) break; } @@ -3004,14 +3059,14 @@ String String::lstrip(const Vector<CharType> &p_chars) const { return substr(beg, len - beg); } -String String::rstrip(const Vector<CharType> &p_chars) const { +String String::rstrip(const String &p_chars) const { int len = length(); int end; for (end = len - 1; end >= 0; end--) { - if (p_chars.find(operator[](end)) == -1) + if (p_chars.find(&ptr()[end]) == -1) break; } @@ -3823,10 +3878,10 @@ String String::percent_decode() const { c += d; i += 2; } - pe.push_back(c); + pe += c; } - pe.push_back(0); + pe += '0'; return String::utf8(pe.ptr()); } diff --git a/core/ustring.h b/core/ustring.h index 1ed694bb80..3b4405833c 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -32,6 +32,7 @@ #define RSTRING_H #include "array.h" +#include "cowdata.h" #include "typedefs.h" #include "vector.h" @@ -39,9 +40,27 @@ @author red <red@killy> */ -class CharString : public Vector<char> { +class CharString { + + CowData<char> _cowdata; + public: + _FORCE_INLINE_ char *ptrw() { return _cowdata.ptrw(); } + _FORCE_INLINE_ const char *ptr() const { return _cowdata.ptr(); } + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + Error resize(int p_size) { return _cowdata.resize(p_size); } + + _FORCE_INLINE_ char get(int p_index) { return _cowdata.get(p_index); } + _FORCE_INLINE_ const char get(int p_index) const { return _cowdata.get(p_index); } + _FORCE_INLINE_ void set(int p_index, const char &p_elem) { _cowdata.set(p_index, p_elem); } + _FORCE_INLINE_ char &operator[](int p_index) { return _cowdata.get_m(p_index); } + _FORCE_INLINE_ const char &operator[](int p_index) const { return _cowdata.get(p_index); } + + _FORCE_INLINE_ CharString() {} + _FORCE_INLINE_ CharString(const CharString &p_str) { _cowdata._ref(p_str._cowdata); } + bool operator<(const CharString &p_right) const; + CharString &operator+=(char p_char); int length() const { return size() ? size() - 1 : 0; } const char *get_data() const; operator const char *() { return get_data(); }; @@ -60,11 +79,14 @@ struct StrRange { } }; -class String : public Vector<CharType> { +class String { + + CowData<CharType> _cowdata; void copy_from(const char *p_cstr); void copy_from(const CharType *p_cstr, int p_clip_to = -1); void copy_from(const CharType &p_char); + void copy_from_unchecked(const CharType *p_char, int p_length); bool _base_is_subsequence_of(const String &p_string, bool case_insensitive) const; public: @@ -73,6 +95,21 @@ public: npos = -1 ///<for "some" compatibility with std::string (npos is a huge value in std::string) }; + _FORCE_INLINE_ CharType *ptrw() { return _cowdata.ptrw(); } + _FORCE_INLINE_ const CharType *ptr() const { return _cowdata.ptr(); } + + void remove(int p_index) { _cowdata.remove(p_index); } + + _FORCE_INLINE_ void clear() { resize(0); } + + _FORCE_INLINE_ CharType get(int p_index) { return _cowdata.get(p_index); } + _FORCE_INLINE_ const CharType get(int p_index) const { return _cowdata.get(p_index); } + _FORCE_INLINE_ void set(int p_index, const CharType &p_elem) { _cowdata.set(p_index, p_elem); } + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + Error resize(int p_size) { return _cowdata.resize(p_size); } + _FORCE_INLINE_ CharType &operator[](int p_index) { return _cowdata.get_m(p_index); } + _FORCE_INLINE_ const CharType &operator[](int p_index) const { return _cowdata.get(p_index); } + bool operator==(const String &p_str) const; bool operator!=(const String &p_str) const; String operator+(const String &p_str) const; @@ -172,6 +209,7 @@ public: String get_slicec(CharType p_splitter, int p_slice) const; Vector<String> split(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const; + Vector<String> rsplit(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const; Vector<String> split_spaces() const; Vector<float> split_floats(const String &p_splitter, bool p_allow_empty = true) const; Vector<float> split_floats_mk(const Vector<String> &p_splitters, bool p_allow_empty = true) const; @@ -190,8 +228,8 @@ public: String dedent() const; String strip_edges(bool left = true, bool right = true) const; String strip_escapes() const; - String lstrip(const Vector<CharType> &p_chars) const; - String rstrip(const Vector<CharType> &p_chars) const; + String lstrip(const String &p_chars) const; + String rstrip(const String &p_chars) const; String get_extension() const; String get_basename() const; String plus_file(const String &p_file) const; @@ -252,9 +290,10 @@ public: * The constructors must not depend on other overloads */ /* String(CharType p_char);*/ - inline String() {} - inline String(const String &p_str) : - Vector(p_str) {} + + _FORCE_INLINE_ String() {} + _FORCE_INLINE_ String(const String &p_str) { _cowdata._ref(p_str._cowdata); } + String(const char *p_str); String(const CharType *p_str, int p_clip_to_len = -1); String(const StrRange &p_range); diff --git a/core/variant.cpp b/core/variant.cpp index a6df95e310..e4be5520bc 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -1878,7 +1878,7 @@ Variant::operator Vector<RID>() const { Vector<RID> rids; rids.resize(va.size()); for (int i = 0; i < rids.size(); i++) - rids[i] = va[i]; + rids.write[i] = va[i]; return rids; } @@ -1891,7 +1891,7 @@ Variant::operator Vector<Vector2>() const { return Vector<Vector2>(); to.resize(len); PoolVector<Vector2>::Read r = from.read(); - Vector2 *w = &to[0]; + Vector2 *w = to.ptrw(); for (int i = 0; i < len; i++) { w[i] = r[i]; @@ -1945,7 +1945,7 @@ Variant::operator Vector<Plane>() const { planes.resize(va_size); for (int i = 0; i < va_size; i++) - planes[i] = va[i]; + planes.write[i] = va[i]; return planes; } @@ -1958,7 +1958,7 @@ Variant::operator Vector<Variant>() const { to.resize(len); for (int i = 0; i < len; i++) { - to[i] = from[i]; + to.write[i] = from[i]; } return to; } @@ -1971,7 +1971,7 @@ Variant::operator Vector<uint8_t>() const { to.resize(len); for (int i = 0; i < len; i++) { - to[i] = from[i]; + to.write[i] = from[i]; } return to; } @@ -1983,7 +1983,7 @@ Variant::operator Vector<int>() const { to.resize(len); for (int i = 0; i < len; i++) { - to[i] = from[i]; + to.write[i] = from[i]; } return to; } @@ -1995,7 +1995,7 @@ Variant::operator Vector<real_t>() const { to.resize(len); for (int i = 0; i < len; i++) { - to[i] = from[i]; + to.write[i] = from[i]; } return to; } @@ -2008,10 +2008,23 @@ Variant::operator Vector<String>() const { to.resize(len); for (int i = 0; i < len; i++) { - to[i] = from[i]; + to.write[i] = from[i]; } return to; } +Variant::operator Vector<StringName>() const { + + PoolVector<String> from = operator PoolVector<String>(); + Vector<StringName> to; + int len = from.size(); + to.resize(len); + for (int i = 0; i < len; i++) { + + to.write[i] = from[i]; + } + return to; +} + Variant::operator Vector<Vector3>() const { PoolVector<Vector3> from = operator PoolVector<Vector3>(); @@ -2021,7 +2034,7 @@ Variant::operator Vector<Vector3>() const { return Vector<Vector3>(); to.resize(len); PoolVector<Vector3>::Read r = from.read(); - Vector3 *w = &to[0]; + Vector3 *w = to.ptrw(); for (int i = 0; i < len; i++) { w[i] = r[i]; @@ -2037,7 +2050,7 @@ Variant::operator Vector<Color>() const { return Vector<Color>(); to.resize(len); PoolVector<Color>::Read r = from.read(); - Color *w = &to[0]; + Color *w = to.ptrw(); for (int i = 0; i < len; i++) { w[i] = r[i]; @@ -2444,6 +2457,17 @@ Variant::Variant(const Vector<String> &p_array) { *this = v; } +Variant::Variant(const Vector<StringName> &p_array) { + + type = NIL; + PoolVector<String> v; + int len = p_array.size(); + v.resize(len); + for (int i = 0; i < len; i++) + v.set(i, p_array[i]); + *this = v; +} + Variant::Variant(const Vector<Vector3> &p_array) { type = NIL; diff --git a/core/variant.h b/core/variant.h index f227e4bfdb..b48a0b3e73 100644 --- a/core/variant.h +++ b/core/variant.h @@ -216,6 +216,7 @@ public: operator Vector<int>() const; operator Vector<real_t>() const; operator Vector<String>() const; + operator Vector<StringName>() const; operator Vector<Vector3>() const; operator Vector<Color>() const; operator Vector<RID>() const; @@ -280,6 +281,7 @@ public: Variant(const Vector<int> &p_int_array); Variant(const Vector<real_t> &p_real_array); Variant(const Vector<String> &p_string_array); + Variant(const Vector<StringName> &p_string_array); Variant(const Vector<Vector3> &p_vector3_array); Variant(const Vector<Color> &p_color_array); Variant(const Vector<Plane> &p_array); // helper @@ -396,9 +398,9 @@ public: void static_assign(const Variant &p_variant); static void get_constructor_list(Variant::Type p_type, List<MethodInfo> *p_list); - static void get_numeric_constants_for_type(Variant::Type p_type, List<StringName> *p_constants); - static bool has_numeric_constant(Variant::Type p_type, const StringName &p_value); - static int get_numeric_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid = NULL); + static void get_constants_for_type(Variant::Type p_type, List<StringName> *p_constants); + static bool has_constant(Variant::Type p_type, const StringName &p_value); + static Variant get_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid = NULL); typedef String (*ObjectDeConstruct)(const Variant &p_object, void *ud); typedef void (*ObjectConstruct)(const String &p_text, void *ud, Variant &r_value); diff --git a/core/variant_call.cpp b/core/variant_call.cpp index bd1cde5a82..19308ff683 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -30,6 +30,7 @@ #include "variant.h" +#include "core/color_names.inc" #include "core_string_names.h" #include "io/compression.h" #include "object.h" @@ -64,7 +65,7 @@ struct _VariantCall { if (arg_count == 0) return true; - Variant::Type *tptr = &arg_types[0]; + const Variant::Type *tptr = &arg_types[0]; for (int i = 0; i < arg_count; i++) { @@ -257,6 +258,7 @@ struct _VariantCall { VCALL_LOCALMEM2R(String, insert); VCALL_LOCALMEM0R(String, capitalize); VCALL_LOCALMEM3R(String, split); + VCALL_LOCALMEM3R(String, rsplit); VCALL_LOCALMEM2R(String, split_floats); VCALL_LOCALMEM0R(String, to_upper); VCALL_LOCALMEM0R(String, to_lower); @@ -340,6 +342,7 @@ struct _VariantCall { VCALL_LOCALMEM1R(Vector2, angle_to); VCALL_LOCALMEM1R(Vector2, angle_to_point); VCALL_LOCALMEM2R(Vector2, linear_interpolate); + VCALL_LOCALMEM2R(Vector2, slerp); VCALL_LOCALMEM4R(Vector2, cubic_interpolate); VCALL_LOCALMEM1R(Vector2, rotated); VCALL_LOCALMEM0R(Vector2, tangent); @@ -380,6 +383,7 @@ struct _VariantCall { VCALL_LOCALMEM1R(Vector3, snapped); VCALL_LOCALMEM2R(Vector3, rotated); VCALL_LOCALMEM2R(Vector3, linear_interpolate); + VCALL_LOCALMEM2R(Vector3, slerp); VCALL_LOCALMEM4R(Vector3, cubic_interpolate); VCALL_LOCALMEM1R(Vector3, dot); VCALL_LOCALMEM1R(Vector3, cross); @@ -439,9 +443,16 @@ struct _VariantCall { VCALL_LOCALMEM2R(Quat, slerp); VCALL_LOCALMEM2R(Quat, slerpni); VCALL_LOCALMEM4R(Quat, cubic_slerp); + VCALL_LOCALMEM0R(Quat, get_euler); + VCALL_LOCALMEM1(Quat, set_euler); + VCALL_LOCALMEM2(Quat, set_axis_angle); - VCALL_LOCALMEM0R(Color, to_rgba32); VCALL_LOCALMEM0R(Color, to_argb32); + VCALL_LOCALMEM0R(Color, to_abgr32); + VCALL_LOCALMEM0R(Color, to_rgba32); + VCALL_LOCALMEM0R(Color, to_argb64); + VCALL_LOCALMEM0R(Color, to_abgr64); + VCALL_LOCALMEM0R(Color, to_rgba64); VCALL_LOCALMEM0R(Color, gray); VCALL_LOCALMEM0R(Color, inverted); VCALL_LOCALMEM0R(Color, contrasted); @@ -757,6 +768,7 @@ struct _VariantCall { VCALL_PTR1R(Basis, xform_inv); VCALL_PTR0R(Basis, get_orthogonal_index); VCALL_PTR0R(Basis, orthonormalized); + VCALL_PTR2R(Basis, slerp); VCALL_PTR0R(Transform, inverse); VCALL_PTR0R(Transform, affine_inverse); @@ -876,6 +888,11 @@ struct _VariantCall { r_ret = Quat(((Vector3)(*p_args[0])), ((float)(*p_args[1]))); } + static void Quat_init3(Variant &r_ret, const Variant **p_args) { + + r_ret = Quat(((Vector3)(*p_args[0]))); + } + static void Color_init1(Variant &r_ret, const Variant **p_args) { r_ret = Color(*p_args[0], *p_args[1], *p_args[2], *p_args[3]); @@ -975,6 +992,7 @@ struct _VariantCall { #ifdef DEBUG_ENABLED List<StringName> value_ordered; #endif + Map<StringName, Variant> variant_value; }; static ConstantData *constant_data; @@ -986,6 +1004,11 @@ struct _VariantCall { constant_data[p_type].value_ordered.push_back(p_constant_name); #endif } + + static void add_variant_constant(int p_type, StringName p_constant_name, const Variant &p_constant_value) { + + constant_data[p_type].variant_value[p_constant_name] = p_constant_value; + } }; _VariantCall::TypeFunc *_VariantCall::type_funcs = NULL; @@ -1150,7 +1173,7 @@ Variant Variant::construct(const Variant::Type p_type, const Variant **p_args, i case RECT2: return (Rect2(*p_args[0])); case VECTOR3: return (Vector3(*p_args[0])); case PLANE: return (Plane(*p_args[0])); - case QUAT: return (Quat(*p_args[0])); + case QUAT: return (p_args[0]->operator Quat()); case AABB: return (::AABB(*p_args[0])); // 10 case BASIS: return (Basis(p_args[0]->operator Basis())); @@ -1338,7 +1361,7 @@ void Variant::get_constructor_list(Variant::Type p_type, List<MethodInfo> *p_lis } } -void Variant::get_numeric_constants_for_type(Variant::Type p_type, List<StringName> *p_constants) { +void Variant::get_constants_for_type(Variant::Type p_type, List<StringName> *p_constants) { ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX); @@ -1354,16 +1377,21 @@ void Variant::get_numeric_constants_for_type(Variant::Type p_type, List<StringNa p_constants->push_back(E->key()); #endif } + + for (Map<StringName, Variant>::Element *E = cd.variant_value.front(); E; E = E->next()) { + + p_constants->push_back(E->key()); + } } -bool Variant::has_numeric_constant(Variant::Type p_type, const StringName &p_value) { +bool Variant::has_constant(Variant::Type p_type, const StringName &p_value) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); _VariantCall::ConstantData &cd = _VariantCall::constant_data[p_type]; - return cd.value.has(p_value); + return cd.value.has(p_value) || cd.variant_value.has(p_value); } -int Variant::get_numeric_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid) { +Variant Variant::get_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid) { if (r_valid) *r_valid = false; @@ -1373,7 +1401,14 @@ int Variant::get_numeric_constant_value(Variant::Type p_type, const StringName & Map<StringName, int>::Element *E = cd.value.find(p_value); if (!E) { - return -1; + Map<StringName, Variant>::Element *E = cd.variant_value.find(p_value); + if (E) { + if (r_valid) + *r_valid = true; + return E->get(); + } else { + return -1; + } } if (r_valid) *r_valid = true; @@ -1459,6 +1494,7 @@ void register_variant_methods() { ADDFUNC2R(STRING, STRING, String, insert, INT, "position", STRING, "what", varray()); ADDFUNC0R(STRING, STRING, String, capitalize, varray()); ADDFUNC3R(STRING, POOL_STRING_ARRAY, String, split, STRING, "divisor", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0)); + ADDFUNC3R(STRING, POOL_STRING_ARRAY, String, rsplit, STRING, "divisor", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0)); ADDFUNC2R(STRING, POOL_REAL_ARRAY, String, split_floats, STRING, "divisor", BOOL, "allow_empty", varray(true)); ADDFUNC0R(STRING, STRING, String, to_upper, varray()); @@ -1518,6 +1554,7 @@ void register_variant_methods() { ADDFUNC1R(VECTOR2, REAL, Vector2, angle_to, VECTOR2, "to", varray()); ADDFUNC1R(VECTOR2, REAL, Vector2, angle_to_point, VECTOR2, "to", varray()); ADDFUNC2R(VECTOR2, VECTOR2, Vector2, linear_interpolate, VECTOR2, "b", REAL, "t", varray()); + ADDFUNC2R(VECTOR2, VECTOR2, Vector2, slerp, VECTOR2, "b", REAL, "t", varray()); ADDFUNC4R(VECTOR2, VECTOR2, Vector2, cubic_interpolate, VECTOR2, "b", VECTOR2, "pre_a", VECTOR2, "post_b", REAL, "t", varray()); ADDFUNC1R(VECTOR2, VECTOR2, Vector2, rotated, REAL, "phi", varray()); ADDFUNC0R(VECTOR2, VECTOR2, Vector2, tangent, varray()); @@ -1557,6 +1594,7 @@ void register_variant_methods() { ADDFUNC1R(VECTOR3, VECTOR3, Vector3, snapped, VECTOR3, "by", varray()); ADDFUNC2R(VECTOR3, VECTOR3, Vector3, rotated, VECTOR3, "axis", REAL, "phi", varray()); ADDFUNC2R(VECTOR3, VECTOR3, Vector3, linear_interpolate, VECTOR3, "b", REAL, "t", varray()); + ADDFUNC2R(VECTOR3, VECTOR3, Vector3, slerp, VECTOR3, "b", REAL, "t", varray()); ADDFUNC4R(VECTOR3, VECTOR3, Vector3, cubic_interpolate, VECTOR3, "b", VECTOR3, "pre_a", VECTOR3, "post_b", REAL, "t", varray()); ADDFUNC1R(VECTOR3, REAL, Vector3, dot, VECTOR3, "b", varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, cross, VECTOR3, "b", varray()); @@ -1594,9 +1632,16 @@ void register_variant_methods() { ADDFUNC2R(QUAT, QUAT, Quat, slerp, QUAT, "b", REAL, "t", varray()); ADDFUNC2R(QUAT, QUAT, Quat, slerpni, QUAT, "b", REAL, "t", varray()); ADDFUNC4R(QUAT, QUAT, Quat, cubic_slerp, QUAT, "b", QUAT, "pre_a", QUAT, "post_b", REAL, "t", varray()); + ADDFUNC0R(QUAT, VECTOR3, Quat, get_euler, varray()); + ADDFUNC1(QUAT, NIL, Quat, set_euler, VECTOR3, "euler", varray()); + ADDFUNC2(QUAT, NIL, Quat, set_axis_angle, VECTOR3, "axis", REAL, "angle", varray()); - ADDFUNC0R(COLOR, INT, Color, to_rgba32, varray()); ADDFUNC0R(COLOR, INT, Color, to_argb32, varray()); + ADDFUNC0R(COLOR, INT, Color, to_abgr32, varray()); + ADDFUNC0R(COLOR, INT, Color, to_rgba32, varray()); + ADDFUNC0R(COLOR, INT, Color, to_argb64, varray()); + ADDFUNC0R(COLOR, INT, Color, to_abgr64, varray()); + ADDFUNC0R(COLOR, INT, Color, to_rgba64, varray()); ADDFUNC0R(COLOR, REAL, Color, gray, varray()); ADDFUNC0R(COLOR, COLOR, Color, inverted, varray()); ADDFUNC0R(COLOR, COLOR, Color, contrasted, varray()); @@ -1786,6 +1831,7 @@ void register_variant_methods() { ADDFUNC1R(BASIS, VECTOR3, Basis, xform, VECTOR3, "v", varray()); ADDFUNC1R(BASIS, VECTOR3, Basis, xform_inv, VECTOR3, "v", varray()); ADDFUNC0R(BASIS, INT, Basis, get_orthogonal_index, varray()); + ADDFUNC2R(BASIS, BASIS, Basis, slerp, BASIS, "b", REAL, "t", varray()); ADDFUNC0R(TRANSFORM, TRANSFORM, Transform, inverse, varray()); ADDFUNC0R(TRANSFORM, TRANSFORM, Transform, affine_inverse, varray()); @@ -1816,6 +1862,7 @@ void register_variant_methods() { _VariantCall::add_constructor(_VariantCall::Quat_init1, Variant::QUAT, "x", Variant::REAL, "y", Variant::REAL, "z", Variant::REAL, "w", Variant::REAL); _VariantCall::add_constructor(_VariantCall::Quat_init2, Variant::QUAT, "axis", Variant::VECTOR3, "angle", Variant::REAL); + _VariantCall::add_constructor(_VariantCall::Quat_init3, Variant::QUAT, "euler", Variant::VECTOR3); _VariantCall::add_constructor(_VariantCall::Color_init1, Variant::COLOR, "r", Variant::REAL, "g", Variant::REAL, "b", Variant::REAL, "a", Variant::REAL); _VariantCall::add_constructor(_VariantCall::Color_init2, Variant::COLOR, "r", Variant::REAL, "g", Variant::REAL, "b", Variant::REAL); @@ -1830,9 +1877,62 @@ void register_variant_methods() { /* REGISTER CONSTANTS */ + _populate_named_colors(); + for (Map<String, Color>::Element *color = _named_colors.front(); color; color = color->next()) { + _VariantCall::add_variant_constant(Variant::COLOR, color->key(), color->value()); + } + _VariantCall::add_constant(Variant::VECTOR3, "AXIS_X", Vector3::AXIS_X); _VariantCall::add_constant(Variant::VECTOR3, "AXIS_Y", Vector3::AXIS_Y); _VariantCall::add_constant(Variant::VECTOR3, "AXIS_Z", Vector3::AXIS_Z); + + _VariantCall::add_variant_constant(Variant::VECTOR3, "ZERO", Vector3(0, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "INF", Vector3(Math_INF, Math_INF, Math_INF)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "LEFT", Vector3(-1, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "RIGHT", Vector3(1, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "UP", Vector3(0, 1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "DOWN", Vector3(0, -1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "FORWARD", Vector3(0, 0, -1)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "BACK", Vector3(0, 0, 1)); + + _VariantCall::add_variant_constant(Variant::VECTOR2, "ZERO", Vector2(0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "INF", Vector2(Math_INF, Math_INF)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "LEFT", Vector2(-1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "RIGHT", Vector2(1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "UP", Vector2(0, -1)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "DOWN", Vector2(0, 1)); + + _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "IDENTITY", Transform2D(1, 0, 0, 1, 0, 0)); + _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_X", Transform2D(-1, 0, 0, 1, 0, 0)); + _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_Y", Transform2D(1, 0, 0, -1, 0, 0)); + + Transform identity_transform, transform_x, transform_y, transform_z; + identity_transform.set(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "IDENTITY", identity_transform); + transform_x.set(-1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_X", transform_x); + transform_x.set(1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Y", transform_y); + transform_x.set(1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Z", transform_z); + + _VariantCall::add_variant_constant(Variant::PLANE, "X", Plane(Vector3(1, 0, 0), 0)); + _VariantCall::add_variant_constant(Variant::PLANE, "Y", Plane(Vector3(0, 1, 0), 0)); + _VariantCall::add_variant_constant(Variant::PLANE, "Z", Plane(Vector3(0, 0, 1), 0)); + + _VariantCall::add_variant_constant(Variant::QUAT, "IDENTITY", Quat(0, 0, 0, 1)); + + CharType black_circle[2] = { 0x25CF, 0 }; + _VariantCall::add_variant_constant(Variant::STRING, "BLACK_CIRCLE", String(black_circle)); + CharType white_circle[2] = { 0x25CB, 0 }; + _VariantCall::add_variant_constant(Variant::STRING, "WHITE_CIRCLE", String(white_circle)); + CharType black_diamond[2] = { 0x25C6, 0 }; + _VariantCall::add_variant_constant(Variant::STRING, "BLACK_DIAMOND", String(black_diamond)); + CharType white_diamond[2] = { 0x25C7, 0 }; + _VariantCall::add_variant_constant(Variant::STRING, "WHITE_DIAMOND", String(white_diamond)); + + _VariantCall::add_variant_constant(Variant::NODE_PATH, "CURRENT", String(".")); + _VariantCall::add_variant_constant(Variant::NODE_PATH, "PARENT", String("..")); } void unregister_variant_methods() { diff --git a/core/variant_op.cpp b/core/variant_op.cpp index 621af2dfb7..bfa69b1fde 100644 --- a/core/variant_op.cpp +++ b/core/variant_op.cpp @@ -3417,8 +3417,17 @@ Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const { Variant Variant::duplicate(bool deep) const { switch (type) { - // case OBJECT: - // return operator Object *()->duplicate(); + case OBJECT: { + /* breaks stuff :( + if (deep && !_get_obj().ref.is_null()) { + Ref<Resource> resource = _get_obj().ref; + if (resource.is_valid()) { + return resource->duplicate(true); + } + } + */ + return *this; + } break; case DICTIONARY: return operator Dictionary().duplicate(deep); case ARRAY: diff --git a/core/vector.h b/core/vector.h index f586471e27..52e8758f9b 100644 --- a/core/vector.h +++ b/core/vector.h @@ -36,129 +36,68 @@ * @author Juan Linietsky * Vector container. Regular Vector Container. Use with care and for smaller arrays when possible. Use PoolVector for large arrays. */ +#include "cowdata.h" #include "error_macros.h" #include "os/memory.h" -#include "safe_refcount.h" #include "sort.h" template <class T> -class Vector { - - mutable T *_ptr; - - // internal helpers +class VectorWriteProxy { + friend class Vector<T>; + CowData<T> *_parent; - _FORCE_INLINE_ uint32_t *_get_refcount() const { + _FORCE_INLINE_ VectorWriteProxy(CowData<T> *parent) : + _parent(parent){}; - if (!_ptr) - return NULL; - - return reinterpret_cast<uint32_t *>(_ptr) - 2; - } - - _FORCE_INLINE_ uint32_t *_get_size() const { - - if (!_ptr) - return NULL; - - return reinterpret_cast<uint32_t *>(_ptr) - 1; - } - _FORCE_INLINE_ T *_get_data() const { - - if (!_ptr) - return NULL; - return reinterpret_cast<T *>(_ptr); - } - - _FORCE_INLINE_ size_t _get_alloc_size(size_t p_elements) const { - //return nearest_power_of_2_templated(p_elements*sizeof(T)+sizeof(SafeRefCount)+sizeof(int)); - return next_power_of_2(p_elements * sizeof(T)); - } +public: + _FORCE_INLINE_ T &operator[](int p_index) { + CRASH_BAD_INDEX(p_index, _parent->size()); - _FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const { -#if defined(_add_overflow) && defined(_mul_overflow) - size_t o; - size_t p; - if (_mul_overflow(p_elements, sizeof(T), &o)) return false; - *out = next_power_of_2(o); - if (_add_overflow(o, static_cast<size_t>(32), &p)) return false; //no longer allocated here - return true; -#else - // Speed is more important than correctness here, do the operations unchecked - // and hope the best - *out = _get_alloc_size(p_elements); - return true; -#endif + return _parent->ptrw()[p_index]; } +}; - void _unref(void *p_data); +template <class T> +class Vector { + friend class VectorWriteProxy<T>; - void _copy_from(const Vector &p_from); - void _copy_on_write(); + CowData<T> *_cowdata; public: - _FORCE_INLINE_ T *ptrw() { - if (!_ptr) return NULL; - _copy_on_write(); - return (T *)_get_data(); - } - _FORCE_INLINE_ const T *ptr() const { - if (!_ptr) return NULL; - return _get_data(); - } - - _FORCE_INLINE_ void clear() { resize(0); } + VectorWriteProxy<T> write; - _FORCE_INLINE_ int size() const { - uint32_t *size = (uint32_t *)_get_size(); - if (size) - return *size; - else - return 0; - } - _FORCE_INLINE_ bool empty() const { return _ptr == 0; } - Error resize(int p_size); bool push_back(const T &p_elem); - void remove(int p_index); + void remove(int p_index) { _cowdata->remove(p_index); } void erase(const T &p_val) { int idx = find(p_val); if (idx >= 0) remove(idx); }; void invert(); - template <class T_val> - int find(const T_val &p_val, int p_from = 0) const; - - void set(int p_index, const T &p_elem); - T get(int p_index) const; - - inline T &operator[](int p_index) { - - CRASH_BAD_INDEX(p_index, size()); - - _copy_on_write(); // wants to write, so copy on write. - - return _get_data()[p_index]; - } - - inline const T &operator[](int p_index) const { - - CRASH_BAD_INDEX(p_index, size()); + _FORCE_INLINE_ T *ptrw() { return _cowdata->ptrw(); } + _FORCE_INLINE_ const T *ptr() const { return _cowdata->ptr(); } + _FORCE_INLINE_ void clear() { resize(0); } + _FORCE_INLINE_ bool empty() const { return _cowdata->empty(); } - // no cow needed, since it's reading - return _get_data()[p_index]; - } + _FORCE_INLINE_ T get(int p_index) { return _cowdata->get(p_index); } + _FORCE_INLINE_ const T get(int p_index) const { return _cowdata->get(p_index); } + _FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata->set(p_index, p_elem); } + _FORCE_INLINE_ int size() const { return _cowdata->size(); } + Error resize(int p_size) { return _cowdata->resize(p_size); } + _FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata->get(p_index); } + Error insert(int p_pos, const T &p_val) { return _cowdata->insert(p_pos, p_val); } - Error insert(int p_pos, const T &p_val); + void append_array(const Vector<T> &p_other); template <class C> void sort_custom() { - int len = size(); + int len = _cowdata->size(); if (len == 0) return; - T *data = &operator[](0); + + T *data = ptrw(); SortArray<T, C> sorter; sorter.sort(data, len); } @@ -170,7 +109,7 @@ public: void ordered_insert(const T &p_val) { int i; - for (i = 0; i < size(); i++) { + for (i = 0; i < _cowdata->size(); i++) { if (p_val < operator[](i)) { break; @@ -179,173 +118,56 @@ public: insert(i, p_val); } - void operator=(const Vector &p_from); - Vector(const Vector &p_from); + int find(const T &p_val, int p_from = 0) const { + int ret = -1; + if (p_from < 0 || size() == 0) + return ret; - _FORCE_INLINE_ Vector(); - _FORCE_INLINE_ ~Vector(); -}; - -template <class T> -void Vector<T>::_unref(void *p_data) { - - if (!p_data) - return; - - uint32_t *refc = _get_refcount(); - - if (atomic_decrement(refc) > 0) - return; // still in use - // clean up - - uint32_t *count = _get_size(); - T *data = (T *)(count + 1); - - for (uint32_t i = 0; i < *count; i++) { - // call destructors - data[i].~T(); - } - - // free mem - Memory::free_static((uint8_t *)p_data, true); -} - -template <class T> -void Vector<T>::_copy_on_write() { - - if (!_ptr) - return; - - uint32_t *refc = _get_refcount(); + for (int i = p_from; i < size(); i++) { - if (*refc > 1) { - /* in use by more than me */ - uint32_t current_size = *_get_size(); - - uint32_t *mem_new = (uint32_t *)Memory::alloc_static(_get_alloc_size(current_size), true); - - *(mem_new - 2) = 1; //refcount - *(mem_new - 1) = current_size; //size - - T *_data = (T *)(mem_new); - - // initialize new elements - for (uint32_t i = 0; i < current_size; i++) { - - memnew_placement(&_data[i], T(_get_data()[i])); - } - - _unref(_ptr); - _ptr = _data; - } -} - -template <class T> -template <class T_val> -int Vector<T>::find(const T_val &p_val, int p_from) const { - - int ret = -1; - if (p_from < 0 || size() == 0) - return ret; - - for (int i = p_from; i < size(); i++) { - - if (operator[](i) == p_val) { - ret = i; - break; + if (ptr()[i] == p_val) { + ret = i; + break; + }; }; - }; - return ret; -} - -template <class T> -Error Vector<T>::resize(int p_size) { - - ERR_FAIL_COND_V(p_size < 0, ERR_INVALID_PARAMETER); - - if (p_size == size()) - return OK; - - if (p_size == 0) { - // wants to clean up - _unref(_ptr); - _ptr = NULL; - return OK; + return ret; } - // possibly changing size, copy on write - _copy_on_write(); - - size_t alloc_size; - ERR_FAIL_COND_V(!_get_alloc_size_checked(p_size, &alloc_size), ERR_OUT_OF_MEMORY); - - if (p_size > size()) { - - if (size() == 0) { - // alloc from scratch - uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true); - ERR_FAIL_COND_V(!ptr, ERR_OUT_OF_MEMORY); - *(ptr - 1) = 0; //size, currently none - *(ptr - 2) = 1; //refcount - - _ptr = (T *)ptr; - - } else { - void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true); - ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); - _ptr = (T *)(_ptrnew); - } - - // construct the newly created elements - T *elems = _get_data(); - - for (int i = *_get_size(); i < p_size; i++) { - - memnew_placement(&elems[i], T); - } - - *_get_size() = p_size; - - } else if (p_size < size()) { - - // deinitialize no longer needed elements - for (uint32_t i = p_size; i < *_get_size(); i++) { - - T *t = &_get_data()[i]; - t->~T(); - } - - void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true); - ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); - - _ptr = (T *)(_ptrnew); - - *_get_size() = p_size; + _FORCE_INLINE_ Vector() : + _cowdata(new CowData<T>()), + write(VectorWriteProxy<T>(_cowdata)) {} + _FORCE_INLINE_ Vector(const Vector &p_from) : + _cowdata(new CowData<T>()), + write(VectorWriteProxy<T>(_cowdata)) { _cowdata->_ref(p_from._cowdata); } + inline Vector &operator=(const Vector &p_from) { + _cowdata->_ref(p_from._cowdata); + return *this; } - return OK; -} + _FORCE_INLINE_ ~Vector() { + delete _cowdata; + } +}; template <class T> void Vector<T>::invert() { for (int i = 0; i < size() / 2; i++) { - - SWAP(operator[](i), operator[](size() - i - 1)); + T *p = ptrw(); + SWAP(p[i], p[size() - i - 1]); } } template <class T> -void Vector<T>::set(int p_index, const T &p_elem) { - - operator[](p_index) = p_elem; -} - -template <class T> -T Vector<T>::get(int p_index) const { - - return operator[](p_index); +void Vector<T>::append_array(const Vector<T> &p_other) { + const int ds = p_other.size(); + if (ds == 0) + return; + const int bs = size(); + resize(bs + ds); + for (int i = 0; i < ds; ++i) + ptrw()[bs + i] = p_other[i]; } template <class T> @@ -358,72 +180,4 @@ bool Vector<T>::push_back(const T &p_elem) { return false; } -template <class T> -void Vector<T>::remove(int p_index) { - - ERR_FAIL_INDEX(p_index, size()); - T *p = ptrw(); - int len = size(); - for (int i = p_index; i < len - 1; i++) { - - p[i] = p[i + 1]; - }; - - resize(len - 1); -}; - -template <class T> -void Vector<T>::_copy_from(const Vector &p_from) { - - if (_ptr == p_from._ptr) - return; // self assign, do nothing. - - _unref(_ptr); - _ptr = NULL; - - if (!p_from._ptr) - return; //nothing to do - - if (atomic_conditional_increment(p_from._get_refcount()) > 0) { // could reference - _ptr = p_from._ptr; - } -} - -template <class T> -void Vector<T>::operator=(const Vector &p_from) { - - _copy_from(p_from); -} - -template <class T> -Error Vector<T>::insert(int p_pos, const T &p_val) { - - ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER); - resize(size() + 1); - for (int i = (size() - 1); i > p_pos; i--) - set(i, get(i - 1)); - set(p_pos, p_val); - - return OK; -} - -template <class T> -Vector<T>::Vector(const Vector &p_from) { - - _ptr = NULL; - _copy_from(p_from); -} - -template <class T> -Vector<T>::Vector() { - - _ptr = NULL; -} - -template <class T> -Vector<T>::~Vector() { - - _unref(_ptr); -} - #endif diff --git a/core/vmap.h b/core/vmap.h index 8636c02997..ce0ddc4ec6 100644 --- a/core/vmap.h +++ b/core/vmap.h @@ -31,8 +31,8 @@ #ifndef VMAP_H #define VMAP_H +#include "cowdata.h" #include "typedefs.h" -#include "vector.h" template <class T, class V> class VMap { @@ -51,17 +51,17 @@ class VMap { } }; - Vector<_Pair> _data; + CowData<_Pair> _cowdata; _FORCE_INLINE_ int _find(const T &p_val, bool &r_exact) const { r_exact = false; - if (_data.empty()) + if (_cowdata.empty()) return 0; int low = 0; - int high = _data.size() - 1; - const _Pair *a = &_data[0]; + int high = _cowdata.size() - 1; + const _Pair *a = _cowdata.ptr(); int middle = 0; #if DEBUG_ENABLED @@ -89,13 +89,13 @@ class VMap { _FORCE_INLINE_ int _find_exact(const T &p_val) const { - if (_data.empty()) + if (_cowdata.empty()) return -1; int low = 0; - int high = _data.size() - 1; + int high = _cowdata.size() - 1; int middle; - const _Pair *a = &_data[0]; + const _Pair *a = _cowdata.ptr(); while (low <= high) { middle = (low + high) / 2; @@ -118,10 +118,10 @@ public: bool exact; int pos = _find(p_key, exact); if (exact) { - _data[pos].value = p_val; + _cowdata.get_m(pos).value = p_val; return pos; } - _data.insert(pos, _Pair(p_key, p_val)); + _cowdata.insert(pos, _Pair(p_key, p_val)); return pos; } @@ -135,7 +135,7 @@ public: int pos = _find_exact(p_val); if (pos < 0) return; - _data.remove(pos); + _cowdata.remove(pos); } int find(const T &p_val) const { @@ -149,37 +149,37 @@ public: return _find(p_val, exact); } - _FORCE_INLINE_ int size() const { return _data.size(); } - _FORCE_INLINE_ bool empty() const { return _data.empty(); } + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + _FORCE_INLINE_ bool empty() const { return _cowdata.empty(); } const _Pair *get_array() const { - return _data.ptr(); + return _cowdata.ptr(); } _Pair *get_array() { - return _data.ptr(); + return _cowdata.ptrw(); } const V &getv(int p_index) const { - return _data[p_index].value; + return _cowdata.get(p_index).value; } V &getv(int p_index) { - return _data[p_index].value; + return _cowdata.get_m(p_index).value; } const T &getk(int p_index) const { - return _data[p_index].key; + return _cowdata.get(p_index).key; } T &getk(int p_index) { - return _data[p_index].key; + return _cowdata.get_m(p_index).key; } inline const V &operator[](const T &p_key) const { @@ -188,7 +188,7 @@ public: CRASH_COND(pos < 0); - return _data[pos].value; + return _cowdata.get(pos).value; } inline V &operator[](const T &p_key) { @@ -199,7 +199,14 @@ public: pos = insert(p_key, val); } - return _data[pos].value; + return _cowdata.get_m(pos).value; + } + + _FORCE_INLINE_ VMap(){}; + _FORCE_INLINE_ VMap(const VMap &p_from) { _cowdata._ref(p_from._cowdata); } + inline VMap &operator=(const VMap &p_from) { + _cowdata._ref(p_from._cowdata); + return *this; } }; #endif // VMAP_H diff --git a/core/vset.h b/core/vset.h index 449943b4a1..7f4d8e7f62 100644 --- a/core/vset.h +++ b/core/vset.h @@ -133,7 +133,7 @@ public: inline T &operator[](int p_index) { - return _data[p_index]; + return _data.write[p_index]; } inline const T &operator[](int p_index) const { |