summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/SCsub53
-rw-r--r--core/array.cpp4
-rw-r--r--core/bind/core_bind.cpp44
-rw-r--r--core/bind/core_bind.h9
-rw-r--r--core/class_db.cpp188
-rw-r--r--core/class_db.h14
-rw-r--r--core/color.cpp79
-rw-r--r--core/color.h4
-rw-r--r--core/color_names.inc2
-rw-r--r--core/command_queue_mt.h24
-rw-r--r--core/compressed_translation.cpp12
-rw-r--r--core/core_builders.py236
-rw-r--r--core/cowdata.h338
-rw-r--r--core/dictionary.cpp31
-rw-r--r--core/dictionary.h3
-rw-r--r--core/dvector.h2
-rw-r--r--core/engine.cpp75
-rw-r--r--core/engine.h5
-rw-r--r--core/error_macros.h16
-rw-r--r--core/global_constants.cpp14
-rw-r--r--core/hashfuncs.h7
-rw-r--r--core/image.cpp229
-rw-r--r--core/image.h19
-rw-r--r--core/io/file_access_encrypted.cpp12
-rw-r--r--core/io/file_access_memory.cpp2
-rw-r--r--core/io/file_access_network.cpp10
-rw-r--r--core/io/http_client.cpp135
-rw-r--r--core/io/http_client.h2
-rw-r--r--core/io/image_loader.cpp80
-rw-r--r--core/io/image_loader.h13
-rw-r--r--core/io/logger.cpp9
-rw-r--r--core/io/marshalls.cpp177
-rw-r--r--core/io/multiplayer_api.cpp229
-rw-r--r--core/io/multiplayer_api.h46
-rw-r--r--core/io/packet_peer.cpp8
-rw-r--r--core/io/pck_packer.cpp2
-rw-r--r--core/io/resource_format_binary.cpp4
-rw-r--r--core/io/resource_loader.cpp2
-rw-r--r--core/io/stream_peer.cpp2
-rw-r--r--core/io/stream_peer_ssl.cpp17
-rw-r--r--core/io/stream_peer_ssl.h8
-rw-r--r--core/io/translation_loader_po.cpp42
-rw-r--r--core/make_binders.py10
-rw-r--r--core/math/a_star.cpp24
-rw-r--r--core/math/a_star.h2
-rw-r--r--core/math/aabb.cpp4
-rw-r--r--core/math/aabb.h20
-rw-r--r--core/math/bsp_tree.cpp32
-rw-r--r--core/math/delaunay.cpp1
-rw-r--r--core/math/delaunay.h145
-rw-r--r--core/math/expression.cpp2136
-rw-r--r--core/math/expression.h325
-rw-r--r--core/math/geometry.cpp22
-rw-r--r--core/math/geometry.h4
-rw-r--r--core/math/math_2d.h9
-rw-r--r--core/math/math_funcs.h18
-rw-r--r--core/math/matrix3.cpp46
-rw-r--r--core/math/matrix3.h12
-rw-r--r--core/math/quat.cpp26
-rw-r--r--core/math/quat.h28
-rw-r--r--core/math/quick_hull.cpp8
-rw-r--r--core/math/transform.cpp13
-rw-r--r--core/math/triangle_mesh.cpp236
-rw-r--r--core/math/triangle_mesh.h6
-rw-r--r--core/math/triangulate.cpp6
-rw-r--r--core/math/vector3.h10
-rw-r--r--core/message_queue.cpp2
-rw-r--r--core/method_bind.h2
-rw-r--r--core/method_ptrcall.h53
-rw-r--r--core/node_path.cpp39
-rw-r--r--core/node_path.h15
-rw-r--r--core/object.cpp25
-rw-r--r--core/object.h4
-rw-r--r--core/os/file_access.cpp7
-rw-r--r--core/os/input_event.cpp139
-rw-r--r--core/os/input_event.h62
-rw-r--r--core/os/main_loop.cpp1
-rw-r--r--core/os/main_loop.h1
-rw-r--r--core/os/midi_driver.cpp107
-rw-r--r--core/os/midi_driver.h59
-rw-r--r--core/os/os.cpp34
-rw-r--r--core/os/os.h13
-rw-r--r--core/os/threaded_array_processor.h2
-rw-r--r--core/packed_data_container.cpp18
-rw-r--r--core/pool_allocator.cpp2
-rw-r--r--core/project_settings.cpp34
-rw-r--r--core/project_settings.h14
-rw-r--r--core/register_core_types.cpp13
-rw-r--r--core/resource.cpp20
-rw-r--r--core/ring_buffer.h6
-rw-r--r--core/safe_refcount.cpp48
-rw-r--r--core/safe_refcount.h50
-rw-r--r--core/script_debugger_local.cpp4
-rw-r--r--core/script_debugger_remote.cpp71
-rw-r--r--core/script_debugger_remote.h5
-rw-r--r--core/script_language.cpp91
-rw-r--r--core/script_language.h46
-rw-r--r--core/sort.h34
-rw-r--r--core/string_buffer.h2
-rw-r--r--core/string_db.h6
-rw-r--r--core/translation.cpp8
-rw-r--r--core/type_info.h1
-rw-r--r--core/typedefs.h13
-rw-r--r--core/undo_redo.cpp42
-rw-r--r--core/undo_redo.h4
-rw-r--r--core/ustring.cpp99
-rw-r--r--core/ustring.h53
-rw-r--r--core/variant.cpp44
-rw-r--r--core/variant.h8
-rw-r--r--core/variant_call.cpp118
-rw-r--r--core/variant_op.cpp13
-rw-r--r--core/vector.h378
-rw-r--r--core/vmap.h49
-rw-r--r--core/vset.h2
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]) + "\", "
+ + "&COPYRIGHT_INFO_DATA[" + str(part["file_index"]) + "], "
+ + "&COPYRIGHT_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) + "\", "
+ + "&COPYRIGHT_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 {