summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/SCsub2
-rw-r--r--core/input/input_event.cpp2
-rw-r--r--core/io/compression.cpp8
-rw-r--r--core/io/file_access_zip.cpp2
-rw-r--r--core/io/image.cpp26
-rw-r--r--core/io/image.h5
-rw-r--r--core/io/json.cpp8
-rw-r--r--core/io/resource_loader.cpp25
-rw-r--r--core/io/resource_loader.h2
-rw-r--r--core/object/object.cpp8
-rw-r--r--core/object/script_language.cpp10
-rw-r--r--core/object/script_language.h5
-rw-r--r--core/object/undo_redo.h2
-rw-r--r--core/object/worker_thread_pool.cpp9
-rw-r--r--core/os/os.cpp10
-rw-r--r--core/os/threaded_array_processor.h87
-rw-r--r--core/string/ustring.cpp90
-rw-r--r--core/string/ustring.h1
-rw-r--r--core/variant/callable_bind.cpp22
-rw-r--r--core/variant/callable_bind.h2
-rw-r--r--core/variant/native_ptr.h70
-rw-r--r--core/variant/variant_parser.cpp60
22 files changed, 268 insertions, 188 deletions
diff --git a/core/SCsub b/core/SCsub
index a0176f6c33..7e9cd97351 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -65,7 +65,7 @@ thirdparty_misc_sources = [thirdparty_misc_dir + file for file in thirdparty_mis
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_misc_sources)
# Brotli
-if env["brotli"]:
+if env["brotli"] and env["builtin_brotli"]:
thirdparty_brotli_dir = "#thirdparty/brotli/"
thirdparty_brotli_sources = [
"common/constants.c",
diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp
index e547b04d0b..e37886cbe9 100644
--- a/core/input/input_event.cpp
+++ b/core/input/input_event.cpp
@@ -1192,7 +1192,7 @@ static const char *_joy_button_descriptions[(size_t)JoyButton::SDL_MAX] = {
TTRC("Top Action, Sony Triangle, Xbox Y, Nintendo X"),
TTRC("Back, Sony Select, Xbox Back, Nintendo -"),
TTRC("Guide, Sony PS, Xbox Home"),
- TTRC("Start, Nintendo +"),
+ TTRC("Start, Xbox Menu, Nintendo +"),
TTRC("Left Stick, Sony L3, Xbox L/LS"),
TTRC("Right Stick, Sony R3, Xbox R/RS"),
TTRC("Left Shoulder, Sony L1, Xbox LB"),
diff --git a/core/io/compression.cpp b/core/io/compression.cpp
index ac4a637597..e36fb0afa4 100644
--- a/core/io/compression.cpp
+++ b/core/io/compression.cpp
@@ -35,13 +35,13 @@
#include "thirdparty/misc/fastlz.h"
-#ifdef BROTLI_ENABLED
-#include "thirdparty/brotli/include/brotli/decode.h"
-#endif
-
#include <zlib.h>
#include <zstd.h>
+#ifdef BROTLI_ENABLED
+#include <brotli/decode.h>
+#endif
+
int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, Mode p_mode) {
switch (p_mode) {
case MODE_BROTLI: {
diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp
index 064353476f..c7f1a73f97 100644
--- a/core/io/file_access_zip.cpp
+++ b/core/io/file_access_zip.cpp
@@ -47,7 +47,7 @@ static void *godot_open(voidpf opaque, const char *p_fname, int mode) {
return nullptr;
}
- Ref<FileAccess> f = FileAccess::open(p_fname, FileAccess::READ);
+ Ref<FileAccess> f = FileAccess::open(String::utf8(p_fname), FileAccess::READ);
ERR_FAIL_COND_V(f.is_null(), nullptr);
ZipData *zd = memnew(ZipData);
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 9bb987b670..1711e4c265 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -3004,6 +3004,7 @@ ImageMemLoadFunc Image::_jpg_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr;
+ScalableImageMemLoadFunc Image::_svg_scalable_mem_loader_func = nullptr;
void (*Image::_image_compress_bc_func)(Image *, Image::UsedChannels) = nullptr;
void (*Image::_image_compress_bptc_func)(Image *, Image::UsedChannels) = nullptr;
@@ -3476,6 +3477,9 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_tga_from_buffer", "buffer"), &Image::load_tga_from_buffer);
ClassDB::bind_method(D_METHOD("load_bmp_from_buffer", "buffer"), &Image::load_bmp_from_buffer);
+ ClassDB::bind_method(D_METHOD("load_svg_from_buffer", "buffer", "scale"), &Image::load_svg_from_buffer, DEFVAL(1.0));
+ ClassDB::bind_method(D_METHOD("load_svg_from_string", "svg_str", "scale"), &Image::load_svg_from_string, DEFVAL(1.0));
+
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "_set_data", "_get_data");
BIND_CONSTANT(MAX_WIDTH);
@@ -3825,6 +3829,28 @@ Error Image::load_bmp_from_buffer(const Vector<uint8_t> &p_array) {
return _load_from_buffer(p_array, _bmp_mem_loader_func);
}
+Error Image::load_svg_from_buffer(const Vector<uint8_t> &p_array, float scale) {
+ ERR_FAIL_NULL_V_MSG(
+ _svg_scalable_mem_loader_func,
+ ERR_UNAVAILABLE,
+ "The SVG module isn't enabled. Recompile the Godot editor or export template binary with the `module_svg_enabled=yes` SCons option.");
+
+ int buffer_size = p_array.size();
+
+ ERR_FAIL_COND_V(buffer_size == 0, ERR_INVALID_PARAMETER);
+
+ Ref<Image> image = _svg_scalable_mem_loader_func(p_array.ptr(), buffer_size, scale);
+ ERR_FAIL_COND_V(!image.is_valid(), ERR_PARSE_ERROR);
+
+ copy_internals_from(image);
+
+ return OK;
+}
+
+Error Image::load_svg_from_string(const String &p_svg_str, float scale) {
+ return load_svg_from_buffer(p_svg_str.to_utf8_buffer(), scale);
+}
+
void Image::convert_rg_to_ra_rgba8() {
ERR_FAIL_COND(format != FORMAT_RGBA8);
ERR_FAIL_COND(!data.size());
diff --git a/core/io/image.h b/core/io/image.h
index 8e353a8bb7..f877b00ee6 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -48,6 +48,7 @@ typedef Vector<uint8_t> (*SavePNGBufferFunc)(const Ref<Image> &p_img);
typedef Error (*SaveJPGFunc)(const String &p_path, const Ref<Image> &p_img, float p_quality);
typedef Vector<uint8_t> (*SaveJPGBufferFunc)(const Ref<Image> &p_img, float p_quality);
typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size);
+typedef Ref<Image> (*ScalableImageMemLoadFunc)(const uint8_t *p_data, int p_size, float p_scale);
typedef Error (*SaveWebPFunc)(const String &p_path, const Ref<Image> &p_img, const bool p_lossy, const float p_quality);
typedef Vector<uint8_t> (*SaveWebPBufferFunc)(const Ref<Image> &p_img, const bool p_lossy, const float p_quality);
@@ -148,6 +149,7 @@ public:
static ImageMemLoadFunc _webp_mem_loader_func;
static ImageMemLoadFunc _tga_mem_loader_func;
static ImageMemLoadFunc _bmp_mem_loader_func;
+ static ScalableImageMemLoadFunc _svg_scalable_mem_loader_func;
static void (*_image_compress_bc_func)(Image *, UsedChannels p_channels);
static void (*_image_compress_bptc_func)(Image *, UsedChannels p_channels);
@@ -401,6 +403,9 @@ public:
Error load_tga_from_buffer(const Vector<uint8_t> &p_array);
Error load_bmp_from_buffer(const Vector<uint8_t> &p_array);
+ Error load_svg_from_buffer(const Vector<uint8_t> &p_array, float scale = 1.0);
+ Error load_svg_from_string(const String &p_svg_str, float scale = 1.0);
+
void convert_rg_to_ra_rgba8();
void convert_ra_rgba8_to_rg();
void convert_rgba8_to_bgra8();
diff --git a/core/io/json.cpp b/core/io/json.cpp
index a6e054a9fe..496400a5ea 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -299,9 +299,15 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to
}
} break;
- default: {
+ case '"':
+ case '\\':
+ case '/': {
res = next;
} break;
+ default: {
+ r_err_str = "Invalid escape sequence.";
+ return ERR_PARSE_ERROR;
+ }
}
str += res;
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 525c41cf87..1fe662b1fa 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -239,15 +239,15 @@ ResourceLoader::LoadToken::~LoadToken() {
Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress) {
load_nesting++;
- if (load_paths_stack.size()) {
+ if (load_paths_stack->size()) {
thread_load_mutex.lock();
- HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(load_paths_stack[load_paths_stack.size() - 1]);
+ HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(load_paths_stack->get(load_paths_stack->size() - 1));
if (E) {
E->value.sub_tasks.insert(p_path);
}
thread_load_mutex.unlock();
}
- load_paths_stack.push_back(p_path);
+ load_paths_stack->push_back(p_path);
// Try all loaders and pick the first match for the type hint
bool found = false;
@@ -263,7 +263,7 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin
}
}
- load_paths_stack.resize(load_paths_stack.size() - 1);
+ load_paths_stack->resize(load_paths_stack->size() - 1);
load_nesting--;
if (!res.is_null()) {
@@ -296,8 +296,10 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
// Thread-safe either if it's the current thread or a brand new one.
CallQueue *mq_override = nullptr;
if (load_nesting == 0) {
+ load_paths_stack = memnew(Vector<String>);
+
if (!load_task.dependent_path.is_empty()) {
- load_paths_stack.push_back(load_task.dependent_path);
+ load_paths_stack->push_back(load_task.dependent_path);
}
if (!Thread::is_main_thread()) {
mq_override = memnew(CallQueue);
@@ -309,6 +311,10 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
}
// --
+ if (!Thread::is_main_thread()) {
+ set_current_thread_safe_for_nodes(true);
+ }
+
Ref<Resource> res = _load(load_task.remapped_path, load_task.remapped_path != load_task.local_path ? load_task.local_path : String(), load_task.type_hint, load_task.cache_mode, &load_task.error, load_task.use_sub_threads, &load_task.progress);
if (mq_override) {
mq_override->flush();
@@ -356,8 +362,11 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
thread_load_mutex.unlock();
- if (load_nesting == 0 && mq_override) {
- memdelete(mq_override);
+ if (load_nesting == 0) {
+ if (mq_override) {
+ memdelete(mq_override);
+ }
+ memdelete(load_paths_stack);
}
}
@@ -1166,7 +1175,7 @@ bool ResourceLoader::timestamp_on_load = false;
thread_local int ResourceLoader::load_nesting = 0;
thread_local WorkerThreadPool::TaskID ResourceLoader::caller_task_id = 0;
-thread_local Vector<String> ResourceLoader::load_paths_stack;
+thread_local Vector<String> *ResourceLoader::load_paths_stack;
template <>
thread_local uint32_t SafeBinaryMutex<ResourceLoader::BINARY_MUTEX_TAG>::count = 0;
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index 592befb603..2701caa3f4 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -182,7 +182,7 @@ private:
static thread_local int load_nesting;
static thread_local WorkerThreadPool::TaskID caller_task_id;
- static thread_local Vector<String> load_paths_stack;
+ static thread_local Vector<String> *load_paths_stack; // A pointer to avoid broken TLS implementations from double-running the destructor.
static SafeBinaryMutex<BINARY_MUTEX_TAG> thread_load_mutex;
static HashMap<String, ThreadLoadTask> thread_load_tasks;
static bool cleaning_tasks;
diff --git a/core/object/object.cpp b/core/object/object.cpp
index c76188a2cd..4d19a2c75b 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -836,14 +836,16 @@ void Object::set_script(const Variant &p_script) {
return;
}
+ Ref<Script> s = p_script;
+ ERR_FAIL_COND_MSG(s.is_null() && !p_script.is_null(), "Invalid parameter, it should be a reference to a valid script (or null).");
+
+ script = p_script;
+
if (script_instance) {
memdelete(script_instance);
script_instance = nullptr;
}
- script = p_script;
- Ref<Script> s = script;
-
if (!s.is_null()) {
if (s->can_instantiate()) {
OBJ_DEBUG_LOCK
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
index 6f047d80aa..a8b0e426ae 100644
--- a/core/object/script_language.cpp
+++ b/core/object/script_language.cpp
@@ -42,7 +42,7 @@ int ScriptServer::_language_count = 0;
bool ScriptServer::scripting_enabled = true;
bool ScriptServer::reload_scripts_on_save = false;
-bool ScriptServer::languages_finished = false;
+SafeFlag ScriptServer::languages_finished; // Used until GH-76581 is fixed properly.
ScriptEditRequestFunction ScriptServer::edit_request_func = nullptr;
void Script::_notification(int p_what) {
@@ -228,7 +228,7 @@ void ScriptServer::finish_languages() {
_languages[i]->finish();
}
global_classes_clear();
- languages_finished = true;
+ languages_finished.set();
}
void ScriptServer::set_reload_scripts_on_save(bool p_enable) {
@@ -240,12 +240,18 @@ bool ScriptServer::is_reload_scripts_on_save_enabled() {
}
void ScriptServer::thread_enter() {
+ if (!languages_finished.is_set()) {
+ return;
+ }
for (int i = 0; i < _language_count; i++) {
_languages[i]->thread_enter();
}
}
void ScriptServer::thread_exit() {
+ if (!languages_finished.is_set()) {
+ return;
+ }
for (int i = 0; i < _language_count; i++) {
_languages[i]->thread_exit();
}
diff --git a/core/object/script_language.h b/core/object/script_language.h
index c22890e30a..2b685c77a3 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -35,6 +35,7 @@
#include "core/io/resource.h"
#include "core/templates/pair.h"
#include "core/templates/rb_map.h"
+#include "core/templates/safe_refcount.h"
#include "core/variant/typed_array.h"
class ScriptLanguage;
@@ -52,7 +53,7 @@ class ScriptServer {
static int _language_count;
static bool scripting_enabled;
static bool reload_scripts_on_save;
- static bool languages_finished;
+ static SafeFlag languages_finished; // Used until GH-76581 is fixed properly.
struct GlobalScriptClass {
StringName language;
@@ -97,7 +98,7 @@ public:
static void init_languages();
static void finish_languages();
- static bool are_languages_finished() { return languages_finished; }
+ static bool are_languages_finished() { return languages_finished.is_set(); }
};
class ScriptInstance;
diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h
index 2ee17867f2..389d8714f7 100644
--- a/core/object/undo_redo.h
+++ b/core/object/undo_redo.h
@@ -58,7 +58,7 @@ private:
TYPE_REFERENCE
} type;
- bool force_keep_in_merge_ends;
+ bool force_keep_in_merge_ends = false;
Ref<RefCounted> ref;
ObjectID object;
StringName name;
diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp
index d285be3e70..5ec3e1a1a8 100644
--- a/core/object/worker_thread_pool.cpp
+++ b/core/object/worker_thread_pool.cpp
@@ -56,6 +56,8 @@ void WorkerThreadPool::_process_task(Task *p_task) {
Task *prev_low_prio_task = nullptr; // In case this is recursively called.
if (!use_native_low_priority_threads) {
+ // Tasks must start with this unset. They are free to set-and-forget otherwise.
+ set_current_thread_safe_for_nodes(false);
pool_thread_index = thread_ids[Thread::get_caller_id()];
ThreadData &curr_thread = threads[pool_thread_index];
task_mutex.lock();
@@ -179,9 +181,6 @@ void WorkerThreadPool::_process_task(Task *p_task) {
if (post) {
task_available_semaphore.post();
}
-
- // Engine/user tasks can set-and-forget, so we must be sure it's back to normal by the end of the task.
- set_current_thread_safe_for_nodes(false);
}
}
@@ -371,7 +370,9 @@ Error WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) {
must_exit = true;
} else {
// Solve tasks while they are around.
+ bool safe_for_nodes_backup = is_current_thread_safe_for_nodes();
_process_task_queue();
+ set_current_thread_safe_for_nodes(safe_for_nodes_backup);
continue;
}
} else if (!use_native_low_priority_threads && task->low_priority) {
@@ -415,7 +416,7 @@ Error WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) {
WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) {
ERR_FAIL_COND_V(p_elements < 0, INVALID_TASK_ID);
if (p_tasks < 0) {
- p_tasks = threads.size();
+ p_tasks = MAX(1u, threads.size());
}
task_mutex.lock();
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 5704ef7a40..67423128a3 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -295,12 +295,14 @@ Error OS::shell_open(String p_uri) {
}
Error OS::shell_show_in_file_manager(String p_path, bool p_open_folder) {
- if (!p_path.begins_with("file://")) {
- p_path = String("file://") + p_path;
- }
- if (!p_path.ends_with("/")) {
+ p_path = p_path.trim_prefix("file://");
+
+ if (!DirAccess::dir_exists_absolute(p_path)) {
p_path = p_path.get_base_dir();
}
+
+ p_path = String("file://") + p_path;
+
return shell_open(p_path);
}
// implement these with the canvas?
diff --git a/core/os/threaded_array_processor.h b/core/os/threaded_array_processor.h
deleted file mode 100644
index 34b417ae57..0000000000
--- a/core/os/threaded_array_processor.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/**************************************************************************/
-/* threaded_array_processor.h */
-/**************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/**************************************************************************/
-/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
-/* */
-/* 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 THREADED_ARRAY_PROCESSOR_H
-#define THREADED_ARRAY_PROCESSOR_H
-
-#include "core/os/os.h"
-#include "core/os/thread.h"
-#include "core/os/thread_safe.h"
-#include "core/templates/safe_refcount.h"
-
-template <class C, class U>
-struct ThreadArrayProcessData {
- uint32_t elements;
- SafeNumeric<uint32_t> index;
- C *instance;
- U userdata;
- void (C::*method)(uint32_t, U);
-
- void process(uint32_t p_index) {
- (instance->*method)(p_index, userdata);
- }
-};
-
-template <class T>
-void process_array_thread(void *ud) {
- T &data = *(T *)ud;
- while (true) {
- uint32_t index = data.index.increment();
- if (index >= data.elements) {
- break;
- }
- data.process(index);
- }
-}
-
-template <class C, class M, class U>
-void thread_process_array(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) {
- ThreadArrayProcessData<C, U> data;
- data.method = p_method;
- data.instance = p_instance;
- data.userdata = p_userdata;
- data.index.set(0);
- data.elements = p_elements;
- data.process(0); //process first, let threads increment for next
-
- int thread_count = OS::get_singleton()->get_processor_count();
- Thread *threads = memnew_arr(Thread, thread_count);
-
- for (int i = 0; i < thread_count; i++) {
- threads[i].start(process_array_thread<ThreadArrayProcessData<C, U>>, &data);
- }
-
- for (int i = 0; i < thread_count; i++) {
- threads[i].wait_to_finish();
- }
- memdelete_arr(threads);
-}
-
-#endif // THREADED_ARRAY_PROCESSOR_H
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index c276f20f99..12e6423724 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -62,6 +62,7 @@ static _FORCE_INLINE_ char32_t lower_case(char32_t c) {
const char CharString::_null = 0;
const char16_t Char16String::_null = 0;
const char32_t String::_null = 0;
+const char32_t String::_replacement_char = 0xfffd;
bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end) {
const String &s = p_s;
@@ -307,7 +308,7 @@ void String::copy_from(const char *p_cstr) {
uint8_t c = p_cstr[i] >= 0 ? p_cstr[i] : uint8_t(256 + p_cstr[i]);
if (c == 0 && i < len) {
print_unicode_error("NUL character", true);
- dst[i] = 0x20;
+ dst[i] = _replacement_char;
} else {
dst[i] = c;
}
@@ -340,7 +341,7 @@ void String::copy_from(const char *p_cstr, const int p_clip_to) {
uint8_t c = p_cstr[i] >= 0 ? p_cstr[i] : uint8_t(256 + p_cstr[i]);
if (c == 0) {
print_unicode_error("NUL character", true);
- dst[i] = 0x20;
+ dst[i] = _replacement_char;
} else {
dst[i] = c;
}
@@ -373,17 +374,21 @@ void String::copy_from(const char32_t &p_char) {
print_unicode_error("NUL character", true);
return;
}
+
+ resize(2);
+
+ char32_t *dst = ptrw();
+
if ((p_char & 0xfffff800) == 0xd800) {
print_unicode_error(vformat("Unpaired surrogate (%x)", (uint32_t)p_char));
- }
- if (p_char > 0x10ffff) {
+ dst[0] = _replacement_char;
+ } else if (p_char > 0x10ffff) {
print_unicode_error(vformat("Invalid unicode codepoint (%x)", (uint32_t)p_char));
+ dst[0] = _replacement_char;
+ } else {
+ dst[0] = p_char;
}
- resize(2);
-
- char32_t *dst = ptrw();
- dst[0] = p_char;
dst[1] = 0;
}
@@ -439,14 +444,18 @@ void String::copy_from_unchecked(const char32_t *p_char, const int p_length) {
for (int i = 0; i < p_length; i++) {
if (p_char[i] == 0) {
print_unicode_error("NUL character", true);
- dst[i] = 0x20;
+ dst[i] = _replacement_char;
continue;
}
if ((p_char[i] & 0xfffff800) == 0xd800) {
print_unicode_error(vformat("Unpaired surrogate (%x)", (uint32_t)p_char[i]));
+ dst[i] = _replacement_char;
+ continue;
}
if (p_char[i] > 0x10ffff) {
print_unicode_error(vformat("Invalid unicode codepoint (%x)", (uint32_t)p_char[i]));
+ dst[i] = _replacement_char;
+ continue;
}
dst[i] = p_char[i];
}
@@ -538,7 +547,7 @@ String &String::operator+=(const char *p_str) {
uint8_t c = p_str[i] >= 0 ? p_str[i] : uint8_t(256 + p_str[i]);
if (c == 0 && i < rhs_len) {
print_unicode_error("NUL character", true);
- dst[i] = 0x20;
+ dst[i] = _replacement_char;
} else {
dst[i] = c;
}
@@ -568,17 +577,21 @@ String &String::operator+=(char32_t p_char) {
print_unicode_error("NUL character", true);
return *this;
}
+
+ const int lhs_len = length();
+ resize(lhs_len + 2);
+ char32_t *dst = ptrw();
+
if ((p_char & 0xfffff800) == 0xd800) {
print_unicode_error(vformat("Unpaired surrogate (%x)", (uint32_t)p_char));
- }
- if (p_char > 0x10ffff) {
+ dst[lhs_len] = _replacement_char;
+ } else if (p_char > 0x10ffff) {
print_unicode_error(vformat("Invalid unicode codepoint (%x)", (uint32_t)p_char));
+ dst[lhs_len] = _replacement_char;
+ } else {
+ dst[lhs_len] = p_char;
}
- const int lhs_len = length();
- resize(lhs_len + 2);
- char32_t *dst = ptrw();
- dst[lhs_len] = p_char;
dst[lhs_len + 1] = 0;
return *this;
@@ -1737,7 +1750,7 @@ Vector<uint8_t> String::hex_decode() const {
void String::print_unicode_error(const String &p_message, bool p_critical) const {
if (p_critical) {
- print_error(vformat("Unicode parsing error, some characters were replaced with spaces: %s", p_message));
+ print_error(vformat("Unicode parsing error, some characters were replaced with � (U+FFFD): %s", p_message));
} else {
print_error(vformat("Unicode parsing error: %s", p_message));
}
@@ -1757,7 +1770,7 @@ CharString String::ascii(bool p_allow_extended) const {
cs[i] = c;
} else {
print_unicode_error(vformat("Invalid unicode codepoint (%x), cannot represent as ASCII/Latin-1", (uint32_t)c));
- cs[i] = 0x20;
+ cs[i] = 0x20; // ascii doesn't have a replacement character like unicode, 0x1a is sometimes used but is kinda arcane
}
}
@@ -1897,13 +1910,13 @@ Error String::parse_utf8(const char *p_utf8, int p_len, bool p_skip_cr) {
unichar = (0xff >> 7) & c;
skip = 5;
} else {
- *(dst++) = 0x20;
+ *(dst++) = _replacement_char;
unichar = 0;
skip = 0;
}
} else {
if (c < 0x80 || c > 0xbf) {
- *(dst++) = 0x20;
+ *(dst++) = _replacement_char;
skip = 0;
} else {
unichar = (unichar << 6) | (c & 0x3f);
@@ -1912,15 +1925,15 @@ Error String::parse_utf8(const char *p_utf8, int p_len, bool p_skip_cr) {
if (unichar == 0) {
print_unicode_error("NUL character", true);
decode_failed = true;
- unichar = 0x20;
- }
- if ((unichar & 0xfffff800) == 0xd800) {
- print_unicode_error(vformat("Unpaired surrogate (%x)", unichar));
- decode_error = true;
- }
- if (unichar > 0x10ffff) {
- print_unicode_error(vformat("Invalid unicode codepoint (%x)", unichar));
- decode_error = true;
+ unichar = _replacement_char;
+ } else if ((unichar & 0xfffff800) == 0xd800) {
+ print_unicode_error(vformat("Unpaired surrogate (%x)", unichar), true);
+ decode_failed = true;
+ unichar = _replacement_char;
+ } else if (unichar > 0x10ffff) {
+ print_unicode_error(vformat("Invalid unicode codepoint (%x)", unichar), true);
+ decode_failed = true;
+ unichar = _replacement_char;
}
*(dst++) = unichar;
}
@@ -2014,7 +2027,11 @@ CharString String::utf8() const {
APPEND_CHAR(uint32_t(0x80 | ((c >> 6) & 0x3f))); // Lower lower middle 6 bits.
APPEND_CHAR(uint32_t(0x80 | (c & 0x3f))); // Bottom 6 bits.
} else {
- APPEND_CHAR(0x20);
+ // the string is a valid UTF32, so it should never happen ...
+ print_unicode_error(vformat("Non scalar value (%x)", c), true);
+ APPEND_CHAR(uint32_t(0xe0 | ((_replacement_char >> 12) & 0x0f))); // Top 4 bits.
+ APPEND_CHAR(uint32_t(0x80 | ((_replacement_char >> 6) & 0x3f))); // Middle 6 bits.
+ APPEND_CHAR(uint32_t(0x80 | (_replacement_char & 0x3f))); // Bottom 6 bits.
}
}
#undef APPEND_CHAR
@@ -2187,7 +2204,9 @@ Char16String String::utf16() const {
APPEND_CHAR(uint32_t((c >> 10) + 0xd7c0)); // lead surrogate.
APPEND_CHAR(uint32_t((c & 0x3ff) | 0xdc00)); // trail surrogate.
} else {
- APPEND_CHAR(0x20);
+ // the string is a valid UTF32, so it should never happen ...
+ APPEND_CHAR(uint32_t((_replacement_char >> 10) + 0xd7c0));
+ APPEND_CHAR(uint32_t((_replacement_char & 0x3ff) | 0xdc00));
}
}
#undef APPEND_CHAR
@@ -4262,12 +4281,13 @@ String String::pad_zeros(int p_digits) const {
begin++;
}
- if (begin >= end) {
+ int zeros_to_add = p_digits - (end - begin);
+
+ if (zeros_to_add <= 0) {
return s;
+ } else {
+ return s.insert(begin, String("0").repeat(zeros_to_add));
}
-
- int zeros_to_add = p_digits - (end - begin);
- return s.insert(begin, String("0").repeat(zeros_to_add));
}
String String::trim_prefix(const String &p_prefix) const {
diff --git a/core/string/ustring.h b/core/string/ustring.h
index 782ca47507..295625395d 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -183,6 +183,7 @@ struct StrRange {
class String {
CowData<char32_t> _cowdata;
static const char32_t _null;
+ static const char32_t _replacement_char;
void copy_from(const char *p_cstr);
void copy_from(const char *p_cstr, const int p_clip_to);
diff --git a/core/variant/callable_bind.cpp b/core/variant/callable_bind.cpp
index 378d1ff618..e493e50467 100644
--- a/core/variant/callable_bind.cpp
+++ b/core/variant/callable_bind.cpp
@@ -144,6 +144,18 @@ void CallableCustomBind::call(const Variant **p_arguments, int p_argcount, Varia
callable.callp(args, p_argcount + binds.size(), r_return_value, r_call_error);
}
+Error CallableCustomBind::rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const {
+ const Variant **args = (const Variant **)alloca(sizeof(const Variant **) * (binds.size() + p_argcount));
+ for (int i = 0; i < p_argcount; i++) {
+ args[i] = (const Variant *)p_arguments[i];
+ }
+ for (int i = 0; i < binds.size(); i++) {
+ args[i + p_argcount] = (const Variant *)&binds[i];
+ }
+
+ return callable.rpcp(p_peer_id, args, p_argcount + binds.size(), r_call_error);
+}
+
CallableCustomBind::CallableCustomBind(const Callable &p_callable, const Vector<Variant> &p_binds) {
callable = p_callable;
binds = p_binds;
@@ -242,6 +254,16 @@ void CallableCustomUnbind::call(const Variant **p_arguments, int p_argcount, Var
callable.callp(p_arguments, p_argcount - argcount, r_return_value, r_call_error);
}
+Error CallableCustomUnbind::rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const {
+ if (argcount > p_argcount) {
+ r_call_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_call_error.argument = 0;
+ r_call_error.expected = argcount;
+ return ERR_UNCONFIGURED;
+ }
+ return callable.rpcp(p_peer_id, p_arguments, p_argcount - argcount, r_call_error);
+}
+
CallableCustomUnbind::CallableCustomUnbind(const Callable &p_callable, int p_argcount) {
callable = p_callable;
argcount = p_argcount;
diff --git a/core/variant/callable_bind.h b/core/variant/callable_bind.h
index b51076ad0f..5798797a3d 100644
--- a/core/variant/callable_bind.h
+++ b/core/variant/callable_bind.h
@@ -51,6 +51,7 @@ public:
virtual StringName get_method() const override;
virtual ObjectID get_object() const override;
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
+ virtual Error rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const override;
virtual const Callable *get_base_comparator() const override;
virtual int get_bound_arguments_count() const override;
virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const override;
@@ -78,6 +79,7 @@ public:
virtual StringName get_method() const override;
virtual ObjectID get_object() const override;
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
+ virtual Error rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const override;
virtual const Callable *get_base_comparator() const override;
virtual int get_bound_arguments_count() const override;
virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const override;
diff --git a/core/variant/native_ptr.h b/core/variant/native_ptr.h
index ea811daabf..9199b12845 100644
--- a/core/variant/native_ptr.h
+++ b/core/variant/native_ptr.h
@@ -53,36 +53,46 @@ struct GDExtensionPtr {
operator Variant() const { return uint64_t(data); }
};
-#define GDVIRTUAL_NATIVE_PTR(m_type) \
- template <> \
- struct GDExtensionConstPtr<const m_type> { \
- const m_type *data = nullptr; \
- GDExtensionConstPtr() {} \
- GDExtensionConstPtr(const m_type *p_assign) { data = p_assign; } \
- static const char *get_name() { return "const " #m_type; } \
- operator const m_type *() const { return data; } \
- operator Variant() const { return uint64_t(data); } \
- }; \
- template <> \
- struct VariantCaster<GDExtensionConstPtr<const m_type>> { \
- static _FORCE_INLINE_ GDExtensionConstPtr<const m_type> cast(const Variant &p_variant) { \
- return GDExtensionConstPtr<const m_type>((const m_type *)p_variant.operator uint64_t()); \
- } \
- }; \
- template <> \
- struct GDExtensionPtr<m_type> { \
- m_type *data = nullptr; \
- GDExtensionPtr() {} \
- GDExtensionPtr(m_type *p_assign) { data = p_assign; } \
- static const char *get_name() { return #m_type; } \
- operator m_type *() const { return data; } \
- operator Variant() const { return uint64_t(data); } \
- }; \
- template <> \
- struct VariantCaster<GDExtensionPtr<m_type>> { \
- static _FORCE_INLINE_ GDExtensionPtr<m_type> cast(const Variant &p_variant) { \
- return GDExtensionPtr<m_type>((m_type *)p_variant.operator uint64_t()); \
- } \
+#define GDVIRTUAL_NATIVE_PTR(m_type) \
+ template <> \
+ struct GDExtensionConstPtr<const m_type> { \
+ const m_type *data = nullptr; \
+ GDExtensionConstPtr() {} \
+ GDExtensionConstPtr(const m_type *p_assign) { data = p_assign; } \
+ static const char *get_name() { return "const " #m_type; } \
+ operator const m_type *() const { return data; } \
+ operator Variant() const { return uint64_t(data); } \
+ }; \
+ template <> \
+ struct VariantCaster<GDExtensionConstPtr<const m_type>> { \
+ static _FORCE_INLINE_ GDExtensionConstPtr<const m_type> cast(const Variant &p_variant) { \
+ return GDExtensionConstPtr<const m_type>((const m_type *)p_variant.operator uint64_t()); \
+ } \
+ }; \
+ template <> \
+ struct VariantInternalAccessor<GDExtensionConstPtr<const m_type>> { \
+ static _FORCE_INLINE_ const GDExtensionConstPtr<const m_type> &get(const Variant *v) { return *reinterpret_cast<const GDExtensionConstPtr<const m_type> *>(VariantInternal::get_int(v)); } \
+ static _FORCE_INLINE_ void set(Variant *v, const GDExtensionConstPtr<const m_type> &p_value) { *VariantInternal::get_int(v) = uint64_t(p_value.data); } \
+ }; \
+ template <> \
+ struct GDExtensionPtr<m_type> { \
+ m_type *data = nullptr; \
+ GDExtensionPtr() {} \
+ GDExtensionPtr(m_type *p_assign) { data = p_assign; } \
+ static const char *get_name() { return #m_type; } \
+ operator m_type *() const { return data; } \
+ operator Variant() const { return uint64_t(data); } \
+ }; \
+ template <> \
+ struct VariantCaster<GDExtensionPtr<m_type>> { \
+ static _FORCE_INLINE_ GDExtensionPtr<m_type> cast(const Variant &p_variant) { \
+ return GDExtensionPtr<m_type>((m_type *)p_variant.operator uint64_t()); \
+ } \
+ }; \
+ template <> \
+ struct VariantInternalAccessor<GDExtensionPtr<m_type>> { \
+ static _FORCE_INLINE_ const GDExtensionPtr<m_type> &get(const Variant *v) { return *reinterpret_cast<const GDExtensionPtr<m_type> *>(VariantInternal::get_int(v)); } \
+ static _FORCE_INLINE_ void set(Variant *v, const GDExtensionPtr<m_type> &p_value) { *VariantInternal::get_int(v) = uint64_t(p_value.data); } \
};
template <class T>
diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp
index a40fcfbd47..3320750994 100644
--- a/core/variant/variant_parser.cpp
+++ b/core/variant/variant_parser.cpp
@@ -865,12 +865,46 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
}
get_token(p_stream, token, line, r_err_str);
- if (token.type != TK_NUMBER) {
- r_err_str = "Expected number as argument";
+ // Permit empty RID.
+ if (token.type == TK_PARENTHESIS_CLOSE) {
+ value = RID();
+ return OK;
+ } else if (token.type != TK_NUMBER) {
+ r_err_str = "Expected number as argument or ')'";
return ERR_PARSE_ERROR;
}
- value = token.value;
+ value = RID::from_uint64(token.value);
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_PARENTHESIS_CLOSE) {
+ r_err_str = "Expected ')'";
+ return ERR_PARSE_ERROR;
+ }
+ } else if (id == "Signal") {
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_PARENTHESIS_OPEN) {
+ r_err_str = "Expected '('";
+ return ERR_PARSE_ERROR;
+ }
+
+ // Load as empty.
+ value = Signal();
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_PARENTHESIS_CLOSE) {
+ r_err_str = "Expected ')'";
+ return ERR_PARSE_ERROR;
+ }
+ } else if (id == "Callable") {
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_PARENTHESIS_OPEN) {
+ r_err_str = "Expected '('";
+ return ERR_PARSE_ERROR;
+ }
+
+ // Load as empty.
+ value = Callable();
get_token(p_stream, token, line, r_err_str);
if (token.type != TK_PARENTHESIS_CLOSE) {
@@ -1832,6 +1866,24 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
} break;
+ case Variant::RID: {
+ RID rid = p_variant;
+
+ if (rid == RID()) {
+ p_store_string_func(p_store_string_ud, "RID()");
+ } else {
+ p_store_string_func(p_store_string_ud, "RID(" + itos(rid.get_id()) + ")");
+ }
+ } break;
+
+ // Do not really store these, but ensure that assignments are not empty.
+ case Variant::SIGNAL: {
+ p_store_string_func(p_store_string_ud, "Signal()");
+ } break;
+ case Variant::CALLABLE: {
+ p_store_string_func(p_store_string_ud, "Callable()");
+ } break;
+
case Variant::OBJECT: {
Object *obj = p_variant.get_validated_object();
@@ -2129,6 +2181,8 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
} break;
default: {
+ ERR_PRINT("Unknown variant type");
+ return ERR_BUG;
}
}