summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/bind/core_bind.cpp38
-rw-r--r--core/bind/core_bind.h20
-rw-r--r--core/class_db.cpp15
-rw-r--r--core/class_db.h1
-rw-r--r--core/color.cpp15
-rw-r--r--core/color.h8
-rw-r--r--core/command_queue_mt.cpp4
-rw-r--r--core/command_queue_mt.h38
-rw-r--r--core/core_builders.py2
-rw-r--r--core/engine.cpp4
-rw-r--r--core/engine.h3
-rw-r--r--core/error_macros.h868
-rw-r--r--core/func_ref.cpp9
-rw-r--r--core/hashfuncs.h3
-rw-r--r--core/image.cpp498
-rw-r--r--core/image.h82
-rw-r--r--core/io/config_file.cpp28
-rw-r--r--core/io/config_file.h4
-rw-r--r--core/io/file_access_compressed.cpp8
-rw-r--r--core/io/file_access_network.cpp6
-rw-r--r--core/io/file_access_network.h6
-rw-r--r--core/io/image_loader.cpp4
-rw-r--r--core/io/ip.cpp6
-rw-r--r--core/io/json.cpp10
-rw-r--r--core/io/logger.cpp12
-rw-r--r--core/io/marshalls.cpp21
-rw-r--r--core/io/multiplayer_api.cpp515
-rw-r--r--core/io/multiplayer_api.h26
-rw-r--r--core/io/packet_peer.cpp20
-rw-r--r--core/io/packet_peer.h5
-rw-r--r--core/io/packet_peer_udp.cpp2
-rw-r--r--core/io/resource_format_binary.cpp119
-rw-r--r--core/io/resource_format_binary.h1
-rw-r--r--core/io/resource_importer.cpp4
-rw-r--r--core/io/resource_loader.cpp10
-rw-r--r--core/io/resource_saver.cpp2
-rw-r--r--core/list.h18
-rw-r--r--core/make_binders.py2
-rw-r--r--core/math/basis.cpp12
-rw-r--r--core/math/basis.h3
-rw-r--r--core/math/bsp_tree.cpp2
-rw-r--r--core/math/camera_matrix.cpp58
-rw-r--r--core/math/camera_matrix.h19
-rw-r--r--core/math/expression.cpp17
-rw-r--r--core/math/expression.h1
-rw-r--r--core/math/rect2.h13
-rw-r--r--core/math/vector3.cpp4
-rw-r--r--core/math/vector3.h10
-rw-r--r--core/math/vector3i.cpp55
-rw-r--r--core/math/vector3i.h272
-rw-r--r--core/message_queue.cpp2
-rw-r--r--core/method_ptrcall.h16
-rw-r--r--core/oa_hash_map.h16
-rw-r--r--core/object.cpp186
-rw-r--r--core/object.h81
-rw-r--r--core/object_id.h33
-rw-r--r--core/os/dir_access.cpp4
-rw-r--r--core/os/main_loop.cpp4
-rw-r--r--core/os/os.cpp16
-rw-r--r--core/os/os.h16
-rw-r--r--core/os/semaphore.cpp6
-rw-r--r--core/os/semaphore.h40
-rw-r--r--core/os/thread_dummy.cpp4
-rw-r--r--core/os/thread_dummy.h4
-rw-r--r--core/project_settings.cpp6
-rw-r--r--core/reference.cpp12
-rw-r--r--core/reference.h100
-rw-r--r--core/resource.h1
-rw-r--r--core/rid.h152
-rw-r--r--core/rid_owner.cpp (renamed from core/rid.cpp)14
-rw-r--r--core/rid_owner.h406
-rw-r--r--core/script_language.cpp9
-rw-r--r--core/script_language.h45
-rw-r--r--core/spin_lock.h (renamed from core/ref_ptr.h)44
-rw-r--r--core/thread_work_pool.cpp (renamed from core/ref_ptr.cpp)98
-rw-r--r--core/thread_work_pool.h111
-rw-r--r--core/translation.cpp2
-rw-r--r--core/type_info.h18
-rw-r--r--core/undo_redo.cpp2
-rw-r--r--core/ustring.cpp19
-rw-r--r--core/ustring.h1
-rw-r--r--core/variant.cpp140
-rw-r--r--core/variant.h18
-rw-r--r--core/variant_call.cpp24
-rw-r--r--core/variant_op.cpp66
-rw-r--r--core/variant_parser.cpp211
86 files changed, 3166 insertions, 1664 deletions
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index efd7e3dbf5..d7614c5a82 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -108,13 +108,6 @@ PoolStringArray _ResourceLoader::get_dependencies(const String &p_path) {
return ret;
};
-#ifndef DISABLE_DEPRECATED
-bool _ResourceLoader::has(const String &p_path) {
- WARN_PRINTS("ResourceLoader.has() is deprecated, please replace it with the equivalent has_cached() or the new exists().");
- return has_cached(p_path);
-}
-#endif // DISABLE_DEPRECATED
-
bool _ResourceLoader::has_cached(const String &p_path) {
String local_path = ProjectSettings::get_singleton()->localize_path(p_path);
@@ -134,9 +127,6 @@ void _ResourceLoader::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_dependencies", "path"), &_ResourceLoader::get_dependencies);
ClassDB::bind_method(D_METHOD("has_cached", "path"), &_ResourceLoader::has_cached);
ClassDB::bind_method(D_METHOD("exists", "path", "type_hint"), &_ResourceLoader::exists, DEFVAL(""));
-#ifndef DISABLE_DEPRECATED
- ClassDB::bind_method(D_METHOD("has", "path"), &_ResourceLoader::has);
-#endif // DISABLE_DEPRECATED
}
_ResourceLoader::_ResourceLoader() {
@@ -597,18 +587,6 @@ bool _OS::is_vsync_via_compositor_enabled() const {
return OS::get_singleton()->is_vsync_via_compositor_enabled();
}
-_OS::PowerState _OS::get_power_state() {
- return _OS::PowerState(OS::get_singleton()->get_power_state());
-}
-
-int _OS::get_power_seconds_left() {
- return OS::get_singleton()->get_power_seconds_left();
-}
-
-int _OS::get_power_percent_left() {
- return OS::get_singleton()->get_power_percent_left();
-}
-
bool _OS::has_feature(const String &p_feature) const {
return OS::get_singleton()->has_feature(p_feature);
@@ -1358,10 +1336,6 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_feature", "tag_name"), &_OS::has_feature);
- ClassDB::bind_method(D_METHOD("get_power_state"), &_OS::get_power_state);
- ClassDB::bind_method(D_METHOD("get_power_seconds_left"), &_OS::get_power_seconds_left);
- ClassDB::bind_method(D_METHOD("get_power_percent_left"), &_OS::get_power_percent_left);
-
ClassDB::bind_method(D_METHOD("request_permission", "name"), &_OS::request_permission);
ClassDB::bind_method(D_METHOD("request_permissions"), &_OS::request_permissions);
ClassDB::bind_method(D_METHOD("get_granted_permissions"), &_OS::get_granted_permissions);
@@ -1410,7 +1384,7 @@ void _OS::_bind_methods() {
ADD_PROPERTY_DEFAULT("window_size", Vector2());
BIND_ENUM_CONSTANT(VIDEO_DRIVER_GLES2);
- BIND_ENUM_CONSTANT(VIDEO_DRIVER_GLES3);
+ BIND_ENUM_CONSTANT(VIDEO_DRIVER_VULKAN);
BIND_ENUM_CONSTANT(DAY_SUNDAY);
BIND_ENUM_CONSTANT(DAY_MONDAY);
@@ -1449,12 +1423,6 @@ void _OS::_bind_methods() {
BIND_ENUM_CONSTANT(SYSTEM_DIR_MUSIC);
BIND_ENUM_CONSTANT(SYSTEM_DIR_PICTURES);
BIND_ENUM_CONSTANT(SYSTEM_DIR_RINGTONES);
-
- BIND_ENUM_CONSTANT(POWERSTATE_UNKNOWN);
- BIND_ENUM_CONSTANT(POWERSTATE_ON_BATTERY);
- BIND_ENUM_CONSTANT(POWERSTATE_NO_BATTERY);
- BIND_ENUM_CONSTANT(POWERSTATE_CHARGING);
- BIND_ENUM_CONSTANT(POWERSTATE_CHARGED);
}
_OS::_OS() {
@@ -2612,7 +2580,7 @@ void _Semaphore::_bind_methods() {
_Semaphore::_Semaphore() {
- semaphore = Semaphore::create();
+ semaphore = SemaphoreOld::create();
}
_Semaphore::~_Semaphore() {
@@ -3217,7 +3185,7 @@ Ref<JSONParseResult> _JSON::parse(const String &p_json) {
result->error = JSON::parse(p_json, result->result, result->error_string, result->error_line);
if (result->error != OK) {
- ERR_PRINTS(vformat("Error parsing JSON at line %s: %s", result->error_line, result->error_string));
+ ERR_PRINT(vformat("Error parsing JSON at line %s: %s", result->error_line, result->error_string));
}
return result;
}
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index 87da51f97e..39bed791d0 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -55,9 +55,6 @@ public:
PoolVector<String> get_recognized_extensions_for_type(const String &p_type);
void set_abort_on_missing_resources(bool p_abort);
PoolStringArray get_dependencies(const String &p_path);
-#ifndef DISABLE_DEPRECATED
- bool has(const String &p_path);
-#endif // DISABLE_DEPRECATED
bool has_cached(const String &p_path);
bool exists(const String &p_path, const String &p_type_hint = "");
@@ -104,16 +101,8 @@ protected:
public:
enum VideoDriver {
- VIDEO_DRIVER_GLES3,
VIDEO_DRIVER_GLES2,
- };
-
- enum PowerState {
- POWERSTATE_UNKNOWN, // Cannot determine power status.
- POWERSTATE_ON_BATTERY, // Not plugged in, running on the battery.
- POWERSTATE_NO_BATTERY, // Plugged in, no battery available.
- POWERSTATE_CHARGING, // Plugged in, charging battery.
- POWERSTATE_CHARGED // Plugged in, battery charged.
+ VIDEO_DRIVER_VULKAN,
};
enum Weekday {
@@ -349,10 +338,6 @@ public:
void set_vsync_via_compositor(bool p_enable);
bool is_vsync_via_compositor_enabled() const;
- PowerState get_power_state();
- int get_power_seconds_left();
- int get_power_percent_left();
-
bool has_feature(const String &p_feature) const;
bool request_permission(const String &p_name);
@@ -365,7 +350,6 @@ public:
};
VARIANT_ENUM_CAST(_OS::VideoDriver);
-VARIANT_ENUM_CAST(_OS::PowerState);
VARIANT_ENUM_CAST(_OS::Weekday);
VARIANT_ENUM_CAST(_OS::Month);
VARIANT_ENUM_CAST(_OS::SystemDir);
@@ -642,7 +626,7 @@ public:
class _Semaphore : public Reference {
GDCLASS(_Semaphore, Reference);
- Semaphore *semaphore;
+ SemaphoreOld *semaphore;
static void _bind_methods();
diff --git a/core/class_db.cpp b/core/class_db.cpp
index 65f0c6008c..a2941d70f6 100644
--- a/core/class_db.cpp
+++ b/core/class_db.cpp
@@ -335,6 +335,19 @@ StringName ClassDB::get_parent_class_nocheck(const StringName &p_class) {
return ti->inherits;
}
+StringName ClassDB::get_compatibility_remapped_class(const StringName &p_class) {
+
+ if (classes.has(p_class)) {
+ return p_class;
+ }
+
+ if (compat_classes.has(p_class)) {
+ return compat_classes[p_class];
+ }
+
+ return p_class;
+}
+
StringName ClassDB::get_parent_class(const StringName &p_class) {
OBJTYPE_RLOCK;
@@ -541,7 +554,7 @@ Object *ClassDB::instance(const StringName &p_class) {
}
#ifdef TOOLS_ENABLED
if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) {
- ERR_PRINTS("Class '" + String(p_class) + "' can only be instantiated by editor.");
+ ERR_PRINT("Class '" + String(p_class) + "' can only be instantiated by editor.");
return NULL;
}
#endif
diff --git a/core/class_db.h b/core/class_db.h
index 34301d6cba..404b04f2d0 100644
--- a/core/class_db.h
+++ b/core/class_db.h
@@ -218,6 +218,7 @@ public:
static void get_direct_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes);
static StringName get_parent_class_nocheck(const StringName &p_class);
static StringName get_parent_class(const StringName &p_class);
+ static StringName get_compatibility_remapped_class(const StringName &p_class);
static bool class_exists(const StringName &p_class);
static bool is_parent_class(const StringName &p_class, const StringName &p_inherits);
static bool can_instance(const StringName &p_class);
diff --git a/core/color.cpp b/core/color.cpp
index 1baa8af45d..03aeb2085b 100644
--- a/core/color.cpp
+++ b/core/color.cpp
@@ -508,13 +508,6 @@ Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) const {
return Color(m + r, m + g, m + b, p_a);
}
-// FIXME: Remove once Godot 3.1 has been released
-float Color::gray() const {
-
- WARN_DEPRECATED_MSG("'Color.gray()' is deprecated and will be removed in a future version. Use 'Color.v' for a better grayscale approximation.");
- return (r + g + b) / 3.0;
-}
-
Color::operator String() const {
return rtos(r) + ", " + rtos(g) + ", " + rtos(b) + ", " + rtos(a);
@@ -529,14 +522,6 @@ Color Color::operator+(const Color &p_color) const {
a + p_color.a);
}
-void Color::operator+=(const Color &p_color) {
-
- r = r + p_color.r;
- g = g + p_color.g;
- b = b + p_color.b;
- a = a + p_color.a;
-}
-
Color Color::operator-(const Color &p_color) const {
return Color(
diff --git a/core/color.h b/core/color.h
index cfdd211b69..a7ab94ab08 100644
--- a/core/color.h
+++ b/core/color.h
@@ -56,7 +56,6 @@ struct Color {
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;
float get_v() const;
@@ -70,7 +69,12 @@ struct Color {
}
Color operator+(const Color &p_color) const;
- void operator+=(const Color &p_color);
+ _FORCE_INLINE_ void operator+=(const Color &p_color) {
+ r = r + p_color.r;
+ g = g + p_color.g;
+ b = b + p_color.b;
+ a = a + p_color.a;
+ }
Color operator-() const;
Color operator-(const Color &p_color) const;
diff --git a/core/command_queue_mt.cpp b/core/command_queue_mt.cpp
index c20735939d..861ca8d1d3 100644
--- a/core/command_queue_mt.cpp
+++ b/core/command_queue_mt.cpp
@@ -111,11 +111,11 @@ CommandQueueMT::CommandQueueMT(bool p_sync) {
for (int i = 0; i < SYNC_SEMAPHORES; i++) {
- sync_sems[i].sem = Semaphore::create();
+ sync_sems[i].sem = SemaphoreOld::create();
sync_sems[i].in_use = false;
}
if (p_sync)
- sync = Semaphore::create();
+ sync = SemaphoreOld::create();
else
sync = NULL;
}
diff --git a/core/command_queue_mt.h b/core/command_queue_mt.h
index e5f93bcc36..2b6e0201f0 100644
--- a/core/command_queue_mt.h
+++ b/core/command_queue_mt.h
@@ -52,9 +52,17 @@
#define _COMMA_11 ,
#define _COMMA_12 ,
#define _COMMA_13 ,
+#define _COMMA_14 ,
+#define _COMMA_15 ,
// 1-based comma separated list of ITEMs
#define COMMA_SEP_LIST(ITEM, LENGTH) _COMMA_SEP_LIST_##LENGTH(ITEM)
+#define _COMMA_SEP_LIST_15(ITEM) \
+ _COMMA_SEP_LIST_14(ITEM) \
+ , ITEM(15)
+#define _COMMA_SEP_LIST_14(ITEM) \
+ _COMMA_SEP_LIST_13(ITEM) \
+ , ITEM(14)
#define _COMMA_SEP_LIST_13(ITEM) \
_COMMA_SEP_LIST_12(ITEM) \
, ITEM(13)
@@ -98,6 +106,12 @@
// 1-based semicolon separated list of ITEMs
#define SEMIC_SEP_LIST(ITEM, LENGTH) _SEMIC_SEP_LIST_##LENGTH(ITEM)
+#define _SEMIC_SEP_LIST_15(ITEM) \
+ _SEMIC_SEP_LIST_14(ITEM); \
+ ITEM(15)
+#define _SEMIC_SEP_LIST_14(ITEM) \
+ _SEMIC_SEP_LIST_13(ITEM); \
+ ITEM(14)
#define _SEMIC_SEP_LIST_13(ITEM) \
_SEMIC_SEP_LIST_12(ITEM); \
ITEM(13)
@@ -141,6 +155,12 @@
// 1-based space separated list of ITEMs
#define SPACE_SEP_LIST(ITEM, LENGTH) _SPACE_SEP_LIST_##LENGTH(ITEM)
+#define _SPACE_SEP_LIST_15(ITEM) \
+ _SPACE_SEP_LIST_14(ITEM) \
+ ITEM(15)
+#define _SPACE_SEP_LIST_14(ITEM) \
+ _SPACE_SEP_LIST_13(ITEM) \
+ ITEM(14)
#define _SPACE_SEP_LIST_13(ITEM) \
_SPACE_SEP_LIST_12(ITEM) \
ITEM(13)
@@ -271,13 +291,13 @@
ss->in_use = false; \
}
-#define MAX_CMD_PARAMS 13
+#define MAX_CMD_PARAMS 15
class CommandQueueMT {
struct SyncSemaphore {
- Semaphore *sem;
+ SemaphoreOld *sem;
bool in_use;
};
@@ -298,15 +318,15 @@ class CommandQueueMT {
};
DECL_CMD(0)
- SPACE_SEP_LIST(DECL_CMD, 13)
+ SPACE_SEP_LIST(DECL_CMD, 15)
/* comands that return */
DECL_CMD_RET(0)
- SPACE_SEP_LIST(DECL_CMD_RET, 13)
+ SPACE_SEP_LIST(DECL_CMD_RET, 15)
/* commands that don't return but sync */
DECL_CMD_SYNC(0)
- SPACE_SEP_LIST(DECL_CMD_SYNC, 13)
+ SPACE_SEP_LIST(DECL_CMD_SYNC, 15)
/***** BASE *******/
@@ -322,7 +342,7 @@ class CommandQueueMT {
uint32_t dealloc_ptr;
SyncSemaphore sync_sems[SYNC_SEMAPHORES];
Mutex *mutex;
- Semaphore *sync;
+ SemaphoreOld *sync;
template <class T>
T *allocate() {
@@ -443,15 +463,15 @@ class CommandQueueMT {
public:
/* NORMAL PUSH COMMANDS */
DECL_PUSH(0)
- SPACE_SEP_LIST(DECL_PUSH, 13)
+ SPACE_SEP_LIST(DECL_PUSH, 15)
/* PUSH AND RET COMMANDS */
DECL_PUSH_AND_RET(0)
- SPACE_SEP_LIST(DECL_PUSH_AND_RET, 13)
+ SPACE_SEP_LIST(DECL_PUSH_AND_RET, 15)
/* PUSH AND RET SYNC COMMANDS*/
DECL_PUSH_AND_SYNC(0)
- SPACE_SEP_LIST(DECL_PUSH_AND_SYNC, 13)
+ SPACE_SEP_LIST(DECL_PUSH_AND_SYNC, 15)
void wait_and_flush_one() {
ERR_FAIL_COND(!sync);
diff --git a/core/core_builders.py b/core/core_builders.py
index f3a9e3b221..7720183595 100644
--- a/core/core_builders.py
+++ b/core/core_builders.py
@@ -1,8 +1,8 @@
"""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, byte_to_str
diff --git a/core/engine.cpp b/core/engine.cpp
index 1772cc7c48..85ad175f38 100644
--- a/core/engine.cpp
+++ b/core/engine.cpp
@@ -214,6 +214,9 @@ Engine *Engine::get_singleton() {
return singleton;
}
+bool Engine::is_abort_on_gpu_errors_enabled() const {
+ return abort_on_gpu_errors;
+}
Engine::Engine() {
singleton = this;
@@ -232,4 +235,5 @@ Engine::Engine() {
_frame_ticks = 0;
_frame_step = 0;
editor_hint = false;
+ abort_on_gpu_errors = false;
}
diff --git a/core/engine.h b/core/engine.h
index 1aab907ac8..cfe3a918fc 100644
--- a/core/engine.h
+++ b/core/engine.h
@@ -64,6 +64,7 @@ private:
bool _pixel_snap;
uint64_t _physics_frames;
float _physics_interpolation_fraction;
+ bool abort_on_gpu_errors;
uint64_t _idle_frames;
bool _in_physics;
@@ -126,6 +127,8 @@ public:
Dictionary get_license_info() const;
String get_license_text() const;
+ bool is_abort_on_gpu_errors_enabled() const;
+
Engine();
virtual ~Engine() {}
};
diff --git a/core/error_macros.h b/core/error_macros.h
index 8ba6618942..4a3ea28957 100644
--- a/core/error_macros.h
+++ b/core/error_macros.h
@@ -32,21 +32,8 @@
#define ERROR_MACROS_H
#include "core/typedefs.h"
-/**
- * Error macros. Unlike exceptions and asserts, these macros try to maintain consistency and stability
- * inside the code. It is recommended to always return processable data, so in case of an error,
- * the engine can keep working well.
- * In most cases, bugs and/or invalid data are not fatal and should never allow a perfectly running application
- * to fail or crash.
- */
-
-/**
- * Pointer to the error macro printing function. Reassign to any function to have errors printed
- */
-
-/** Function used by the error macros */
-// function, file, line, error, explanation
+class String;
enum ErrorHandlerType {
ERR_HANDLER_ERROR,
@@ -55,7 +42,8 @@ enum ErrorHandlerType {
ERR_HANDLER_SHADER,
};
-class String;
+// Pointer to the error handler printing function. Reassign to any function to have errors printed.
+// Parameters: userdata, function, file, line, error, explanation, type.
typedef void (*ErrorHandlerFunc)(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type);
struct ErrorHandlerList {
@@ -75,6 +63,7 @@ struct ErrorHandlerList {
void add_error_handler(ErrorHandlerList *p_handler);
void remove_error_handler(ErrorHandlerList *p_handler);
+// Functions used by the error macros.
void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
@@ -84,15 +73,6 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co
void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message = "", bool fatal = false);
void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool fatal = false);
-#ifndef _STR
-#define _STR(m_x) #m_x
-#define _MKSTR(m_x) _STR(m_x)
-#endif
-
-#define _FNL __FILE__ ":"
-
-/** An index has failed if m_index<0 or m_index >=m_size, the function exits */
-
#ifdef __GNUC__
//#define FUNCTION_STR __PRETTY_FUNCTION__ - too annoying
#define FUNCTION_STR __FUNCTION__
@@ -100,15 +80,16 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
#define FUNCTION_STR __FUNCTION__
#endif
-// Don't use this directly; instead, use any of the CRASH_* macros
#ifdef _MSC_VER
-#define GENERATE_TRAP \
- __debugbreak(); \
- /* Avoid warning about control paths */ \
- for (;;) { \
- }
+/**
+ * Don't use GENERATE_TRAP() directly, should only be used be the macros below.
+ */
+#define GENERATE_TRAP() __debugbreak()
#else
-#define GENERATE_TRAP __builtin_trap();
+/**
+ * Don't use GENERATE_TRAP() directly, should only be used be the macros below.
+ */
+#define GENERATE_TRAP() __builtin_trap()
#endif
// Used to strip debug messages in release mode
@@ -118,411 +99,528 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
#define DEBUG_STR(m_msg) ""
#endif
-// (*): See https://stackoverflow.com/questions/257418/do-while-0-what-is-it-good-for
-
-/**
- * If `m_index` is less than 0 or greater than or equal to `m_size`, prints a generic
- * error message and returns from the function. This macro should be preferred to
- * `ERR_FAIL_COND` for bounds checking.
- */
-#define ERR_FAIL_INDEX(m_index, m_size) \
- do { \
- if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
- _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
- return; \
- } \
- } while (0); // (*)
-
-/**
- * If `m_index` is less than 0 or greater than or equal to `m_size`, prints a custom
- * error message and returns from the function. This macro should be preferred to
- * `ERR_FAIL_COND_MSG` for bounds checking.
- */
-#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \
- do { \
- if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
- _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \
- return; \
- } \
- } while (0); // (*)
-
-/**
- * If `m_index` is less than 0 or greater than or equal to `m_size`,
- * prints a generic error message and returns the value specified in `m_retval`.
- * This macro should be preferred to `ERR_FAIL_COND_V` for bounds checking.
- */
-#define ERR_FAIL_INDEX_V(m_index, m_size, m_retval) \
- do { \
- if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
- _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
- return m_retval; \
- } \
- } while (0); // (*)
-
-/**
- * If `m_index` is less than 0 or greater than or equal to `m_size`,
- * prints a custom error message and returns the value specified in `m_retval`.
- * This macro should be preferred to `ERR_FAIL_COND_V_MSG` for bounds checking.
- */
-#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \
- do { \
- if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
- _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \
- return m_retval; \
- } \
- } while (0); // (*)
-
-/**
- * If `m_index` is greater than or equal to `m_size`,
- * prints a generic error message and returns the value specified in `m_retval`.
- * This macro should be preferred to `ERR_FAIL_COND_V` for unsigned bounds checking.
- */
-#define ERR_FAIL_UNSIGNED_INDEX_V(m_index, m_size, m_retval) \
- do { \
- if (unlikely((m_index) >= (m_size))) { \
- _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
- return m_retval; \
- } \
- } while (0); // (*)
-
-/**
- * If `m_index` is greater than or equal to `m_size`,
- * prints a custom error message and returns the value specified in `m_retval`.
- * This macro should be preferred to `ERR_FAIL_COND_V_MSG` for unsigned bounds checking.
- */
-#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \
- do { \
- if (unlikely((m_index) >= (m_size))) { \
- _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \
- return m_retval; \
- } \
- } while (0); // (*)
-
-/**
- * If `m_index` is less than 0 or greater than or equal to `m_size`,
- * crashes the engine immediately with a generic error message.
- * Only use this if there's no sensible fallback (i.e. the error is unrecoverable).
- * This macro should be preferred to `CRASH_COND` for bounds checking.
- */
-#define CRASH_BAD_INDEX(m_index, m_size) \
- do { \
- if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
- _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", true); \
- GENERATE_TRAP \
- } \
- } while (0); // (*)
-
-/**
- * If `m_index` is less than 0 or greater than or equal to `m_size`,
- * crashes the engine immediately with a custom error message.
- * Only use this if there's no sensible fallback (i.e. the error is unrecoverable).
- * This macro should be preferred to `CRASH_COND` for bounds checking.
- */
-#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \
- do { \
- if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
- _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \
- GENERATE_TRAP \
- } \
- } while (0); // (*)
-
-/**
- * If `m_param` is `null`, prints a generic error message and returns from the function.
- */
-#define ERR_FAIL_NULL(m_param) \
- { \
- if (unlikely(!m_param)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \
- return; \
- } \
- }
-
/**
- * If `m_param` is `null`, prints a custom error message and returns from the function.
+ * Error macros.
+ * WARNING: These macros work in the opposite way to assert().
+ *
+ * Unlike exceptions and asserts, these macros try to maintain consistency and stability.
+ * In most cases, bugs and/or invalid data are not fatal. They should never allow a perfectly
+ * running application to fail or crash.
+ * Always try to return processable data, so the engine can keep running well.
+ * Use the _MSG versions to print a meaningful message to help with debugging.
*/
-#define ERR_FAIL_NULL_MSG(m_param, m_msg) \
- { \
- if (unlikely(!m_param)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \
- return; \
- } \
- }
-/**
- * If `m_param` is `null`, prints a generic error message and returns the value specified in `m_retval`.
- */
-#define ERR_FAIL_NULL_V(m_param, m_retval) \
- { \
- if (unlikely(!m_param)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \
- return m_retval; \
- } \
- }
+// Index out of bounds error macros.
+// These macros should be used instead of `ERR_FAIL_COND` for bounds checking.
-/**
- * If `m_param` is `null`, prints a custom error message and returns the value specified in `m_retval`.
- */
-#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \
- { \
- if (unlikely(!m_param)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \
- return m_retval; \
- } \
- }
+// Integer index out of bounds error macros.
/**
- * If `m_cond` evaluates to `true`, prints a generic error message and returns from the function.
+ * Try using `ERR_FAIL_INDEX_MSG`.
+ * Only use this macro if there is no sensible error message.
+ *
+ * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.
+ * If not, the current function returns.
*/
-#define ERR_FAIL_COND(m_cond) \
- { \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true."); \
- return; \
- } \
- }
+#define ERR_FAIL_INDEX(m_index, m_size) \
+ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
+ return; \
+ } else \
+ ((void)0)
/**
- * If `m_cond` evaluates to `true`, prints a custom error message and returns from the function.
+ * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.
+ * If not, prints `m_msg` and the current function returns.
*/
-#define ERR_FAIL_COND_MSG(m_cond, m_msg) \
- { \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", DEBUG_STR(m_msg)); \
- return; \
- } \
- }
+#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \
+ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \
+ return; \
+ } else \
+ ((void)0)
/**
- * If `m_cond` evaluates to `true`, crashes the engine immediately with a generic error message.
- * Only use this if there's no sensible fallback (i.e. the error is unrecoverable).
+ * Try using `ERR_FAIL_INDEX_V_MSG`.
+ * Only use this macro if there is no sensible error message.
+ *
+ * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.
+ * If not, the current function returns `m_retval`.
*/
-#define CRASH_COND(m_cond) \
- { \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true."); \
- GENERATE_TRAP \
- } \
- }
+#define ERR_FAIL_INDEX_V(m_index, m_size, m_retval) \
+ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
+ return m_retval; \
+ } else \
+ ((void)0)
/**
- * If `m_cond` evaluates to `true`, crashes the engine immediately with a custom error message.
- * Only use this if there's no sensible fallback (i.e. the error is unrecoverable).
- */
-#define CRASH_COND_MSG(m_cond, m_msg) \
- { \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true.", DEBUG_STR(m_msg)); \
- GENERATE_TRAP \
- } \
- }
+ * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.
+ * If not, prints `m_msg` and the current function returns `m_retval`.
+ */
+#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \
+ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \
+ return m_retval; \
+ } else \
+ ((void)0)
-/**
- * If `m_cond` evaluates to `true`, prints a generic error message and returns the value specified in `m_retval`.
- */
-#define ERR_FAIL_COND_V(m_cond, m_retval) \
- { \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returned: " _STR(m_retval)); \
- return m_retval; \
- } \
- }
+/**
+ * Try using `ERR_FAIL_INDEX_MSG` or `ERR_FAIL_INDEX_V_MSG`.
+ * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable, and
+ * there is no sensible error message.
+ *
+ * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.
+ * If not, the application crashes.
+ */
+#define CRASH_BAD_INDEX(m_index, m_size) \
+ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", true); \
+ GENERATE_TRAP(); \
+ } else \
+ ((void)0)
/**
- * If `m_cond` evaluates to `true`, prints a custom error message and returns the value specified in `m_retval`.
- */
-#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \
- { \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returned: " _STR(m_retval), DEBUG_STR(m_msg)); \
- return m_retval; \
- } \
- }
+ * Try using `ERR_FAIL_INDEX_MSG` or `ERR_FAIL_INDEX_V_MSG`.
+ * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable.
+ *
+ * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.
+ * If not, prints `m_msg` and the application crashes.
+ */
+#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \
+ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg), true); \
+ GENERATE_TRAP(); \
+ } else \
+ ((void)0)
+
+// Unsigned integer index out of bounds error macros.
/**
- * If `m_cond` evaluates to `true`, prints a custom error message and continues the loop the macro is located in.
- */
-#define ERR_CONTINUE(m_cond) \
- { \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing."); \
- continue; \
- } \
- }
+ * Try using `ERR_FAIL_UNSIGNED_INDEX_MSG`.
+ * Only use this macro if there is no sensible error message.
+ *
+ * Ensures an unsigned integer index `m_index` is less than `m_size`.
+ * If not, the current function returns.
+ */
+#define ERR_FAIL_UNSIGNED_INDEX(m_index, m_size) \
+ if (unlikely((m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
+ return; \
+ } else \
+ ((void)0)
+
+/**
+ * Ensures an unsigned integer index `m_index` is less than `m_size`.
+ * If not, prints `m_msg` and the current function returns.
+ */
+#define ERR_FAIL_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \
+ if (unlikely((m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \
+ return; \
+ } else \
+ ((void)0)
+
+/**
+ * Try using `ERR_FAIL_UNSIGNED_INDEX_V_MSG`.
+ * Only use this macro if there is no sensible error message.
+ *
+ * Ensures an unsigned integer index `m_index` is less than `m_size`.
+ * If not, the current function returns `m_retval`.
+ */
+#define ERR_FAIL_UNSIGNED_INDEX_V(m_index, m_size, m_retval) \
+ if (unlikely((m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
+ return m_retval; \
+ } else \
+ ((void)0)
+
+/**
+ * Ensures an unsigned integer index `m_index` is less than `m_size`.
+ * If not, prints `m_msg` and the current function returns `m_retval`.
+ */
+#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \
+ if (unlikely((m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \
+ return m_retval; \
+ } else \
+ ((void)0)
+
+/**
+ * Try using `ERR_FAIL_UNSIGNED_INDEX_MSG` or `ERR_FAIL_UNSIGNED_INDEX_V_MSG`.
+ * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable, and
+ * there is no sensible error message.
+ *
+ * Ensures an unsigned integer index `m_index` is less than `m_size`.
+ * If not, the application crashes.
+ */
+#define CRASH_BAD_UNSIGNED_INDEX(m_index, m_size) \
+ if (unlikely((m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", true); \
+ GENERATE_TRAP(); \
+ } else \
+ ((void)0)
+
+/**
+ * Try using `ERR_FAIL_UNSIGNED_INDEX_MSG` or `ERR_FAIL_UNSIGNED_INDEX_V_MSG`.
+ * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable.
+ *
+ * Ensures an unsigned integer index `m_index` is less than `m_size`.
+ * If not, prints `m_msg` and the application crashes.
+ */
+#define CRASH_BAD_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \
+ if (unlikely((m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg), true); \
+ GENERATE_TRAP(); \
+ } else \
+ ((void)0)
+
+// Null reference error macros.
+
+/**
+ * Try using `ERR_FAIL_NULL_MSG`.
+ * Only use this macro if there is no sensible error message.
+ *
+ * Ensures a pointer `m_param` is not null.
+ * If it is null, the current function returns.
+ */
+#define ERR_FAIL_NULL(m_param) \
+ if (unlikely(!m_param)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \
+ return; \
+ } else \
+ ((void)0)
+
+/**
+ * Ensures a pointer `m_param` is not null.
+ * If it is null, prints `m_msg` and the current function returns.
+ */
+#define ERR_FAIL_NULL_MSG(m_param, m_msg) \
+ if (unlikely(!m_param)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \
+ return; \
+ } else \
+ ((void)0)
+
+/**
+ * Try using `ERR_FAIL_NULL_V_MSG`.
+ * Only use this macro if there is no sensible error message.
+ *
+ * Ensures a pointer `m_param` is not null.
+ * If it is null, the current function returns `m_retval`.
+ */
+#define ERR_FAIL_NULL_V(m_param, m_retval) \
+ if (unlikely(!m_param)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \
+ return m_retval; \
+ } else \
+ ((void)0)
+
+/**
+ * Ensures a pointer `m_param` is not null.
+ * If it is null, prints `m_msg` and the current function returns `m_retval`.
+ */
+#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \
+ if (unlikely(!m_param)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \
+ return m_retval; \
+ } else \
+ ((void)0)
+
+/**
+ * Try using `ERR_FAIL_COND_MSG`.
+ * Only use this macro if there is no sensible error message.
+ * If checking for null use ERR_FAIL_NULL_MSG instead.
+ * If checking index bounds use ERR_FAIL_INDEX_MSG instead.
+ *
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, the current function returns.
+ */
+#define ERR_FAIL_COND(m_cond) \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true."); \
+ return; \
+ } else \
+ ((void)0)
+
+/**
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, prints `m_msg` and the current function returns.
+ *
+ * If checking for null use ERR_FAIL_NULL_MSG instead.
+ * If checking index bounds use ERR_FAIL_INDEX_MSG instead.
+ */
+#define ERR_FAIL_COND_MSG(m_cond, m_msg) \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", DEBUG_STR(m_msg)); \
+ return; \
+ } else \
+ ((void)0)
+
+/**
+ * Try using `ERR_FAIL_COND_V_MSG`.
+ * Only use this macro if there is no sensible error message.
+ * If checking for null use ERR_FAIL_NULL_V_MSG instead.
+ * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead.
+ *
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, the current function returns `m_retval`.
+ */
+#define ERR_FAIL_COND_V(m_cond, m_retval) \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. returned: " _STR(m_retval)); \
+ return m_retval; \
+ } else \
+ ((void)0)
+
+/**
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, prints `m_msg` and the current function returns `m_retval`.
+ *
+ * If checking for null use ERR_FAIL_NULL_V_MSG instead.
+ * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead.
+ */
+#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. returned: " _STR(m_retval), DEBUG_STR(m_msg)); \
+ return m_retval; \
+ } else \
+ ((void)0)
+
+/**
+ * Try using `ERR_CONTINUE_MSG`.
+ * Only use this macro if there is no sensible error message.
+ *
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, the current loop continues.
+ */
+#define ERR_CONTINUE(m_cond) \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing."); \
+ continue; \
+ } else \
+ ((void)0)
+
+/**
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, prints `m_msg` and the current loop continues.
+ */
+#define ERR_CONTINUE_MSG(m_cond, m_msg) \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", DEBUG_STR(m_msg)); \
+ continue; \
+ } else \
+ ((void)0)
+
+/**
+ * Try using `ERR_BREAK_MSG`.
+ * Only use this macro if there is no sensible error message.
+ *
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, the current loop breaks.
+ */
+#define ERR_BREAK(m_cond) \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking."); \
+ break; \
+ } else \
+ ((void)0)
+
+/**
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, prints `m_msg` and the current loop breaks.
+ */
+#define ERR_BREAK_MSG(m_cond, m_msg) \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", DEBUG_STR(m_msg)); \
+ break; \
+ } else \
+ ((void)0)
+
+/**
+ * Try using `ERR_FAIL_COND_MSG` or `ERR_FAIL_COND_V_MSG`.
+ * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable, and
+ * there is no sensible error message.
+ *
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, the application crashes.
+ */
+#define CRASH_COND(m_cond) \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true."); \
+ GENERATE_TRAP(); \
+ } else \
+ ((void)0)
+
+/**
+ * Try using `ERR_FAIL_COND_MSG` or `ERR_FAIL_COND_V_MSG`.
+ * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable.
+ *
+ * Ensures `m_cond` is false.
+ * If `m_cond` is true, prints `m_msg` and the application crashes.
+ */
+#define CRASH_COND_MSG(m_cond, m_msg) \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true.", DEBUG_STR(m_msg)); \
+ GENERATE_TRAP(); \
+ } else \
+ ((void)0)
+
+// Generic error macros.
+
+/**
+ * Try using `ERR_FAIL_COND_MSG` or `ERR_FAIL_MSG`.
+ * Only use this macro if more complex error detection or recovery is required, and
+ * there is no sensible error message.
+ *
+ * The current function returns.
+ */
+#define ERR_FAIL() \
+ if (1) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed."); \
+ return; \
+ } else \
+ ((void)0)
/**
- * If `m_cond` evaluates to `true`, prints a custom error message and continues the loop the macro is located in.
+ * Try using `ERR_FAIL_COND_MSG`.
+ * Only use this macro if more complex error detection or recovery is required.
+ *
+ * Prints `m_msg`, and the current function returns.
*/
-#define ERR_CONTINUE_MSG(m_cond, m_msg) \
- { \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", DEBUG_STR(m_msg)); \
- continue; \
- } \
- }
+#define ERR_FAIL_MSG(m_msg) \
+ if (1) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed.", DEBUG_STR(m_msg)); \
+ return; \
+ } else \
+ ((void)0)
/**
- * If `m_cond` evaluates to `true`, prints a generic error message and breaks from the loop the macro is located in.
+ * Try using `ERR_FAIL_COND_V_MSG` or `ERR_FAIL_V_MSG`.
+ * Only use this macro if more complex error detection or recovery is required, and
+ * there is no sensible error message.
+ *
+ * The current function returns `m_retval`.
*/
-#define ERR_BREAK(m_cond) \
- { \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking."); \
- break; \
- } \
- }
+#define ERR_FAIL_V(m_retval) \
+ if (1) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed, returning: " __STR(m_value)); \
+ return m_retval; \
+ } else \
+ ((void)0)
/**
- * If `m_cond` evaluates to `true`, prints a custom error message and breaks from the loop the macro is located in.
+ * Try using `ERR_FAIL_COND_V_MSG`.
+ * Only use this macro if more complex error detection or recovery is required.
+ *
+ * Prints `m_msg`, and the current function returns `m_retval`.
*/
-#define ERR_BREAK_MSG(m_cond, m_msg) \
- { \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", DEBUG_STR(m_msg)); \
- break; \
- } \
- }
-
-/**
- * Prints a generic error message and returns from the function.
- */
-#define ERR_FAIL() \
- { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method failed."); \
- return; \
- }
-
-/**
- * Prints a custom error message and returns from the function.
- */
-#define ERR_FAIL_MSG(m_msg) \
- { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method failed.", DEBUG_STR(m_msg)); \
- return; \
- }
-
-/**
- * Prints a generic error message and returns the value specified in `m_retval`.
- */
-#define ERR_FAIL_V(m_retval) \
- { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method failed. Returning: " __STR(m_retval)); \
- return m_retval; \
- }
-
-/**
- * Prints a custom error message and returns the value specified in `m_retval`.
- */
-#define ERR_FAIL_V_MSG(m_retval, m_msg) \
- { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method failed. Returning: " __STR(m_retval), DEBUG_STR(m_msg)); \
- return m_retval; \
- }
-
-/**
- * Crashes the engine immediately with a generic error message.
- * Only use this if there's no sensible fallback (i.e. the error is unrecoverable).
- */
-#define CRASH_NOW() \
- { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method failed."); \
- GENERATE_TRAP \
- }
-
-/**
- * Crashes the engine immediately with a custom error message.
- * Only use this if there's no sensible fallback (i.e. the error is unrecoverable).
- */
-#define CRASH_NOW_MSG(m_msg) \
- { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method failed.", DEBUG_STR(m_msg)); \
- GENERATE_TRAP \
- }
+#define ERR_FAIL_V_MSG(m_retval, m_msg) \
+ if (1) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed, returning: " __STR(m_value), DEBUG_STR(m_msg)); \
+ return m_retval; \
+ } else \
+ ((void)0)
/**
- * Prints an error message without returning.
+ * Try using `ERR_FAIL_COND_MSG`, `ERR_FAIL_COND_V_MSG`, `ERR_CONTINUE_MSG` or ERR_BREAK_MSG.
+ * Only use this macro at the start of a function that has not been implemented yet, or
+ * if more complex error detection or recovery is required.
+ *
+ * Prints `m_msg`.
*/
-#define ERR_PRINT(m_string) \
- { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string); \
- }
+#define ERR_PRINT(m_msg) \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, DEBUG_STR(m_msg))
/**
- * Prints an error message without returning.
- * FIXME: Remove this macro and replace all uses with `ERR_PRINT` as it's identical.
+ * Prints `m_msg` once during the application lifetime.
*/
-#define ERR_PRINTS(m_string) \
- { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string); \
- }
+#define ERR_PRINT_ONCE(m_msg) \
+ if (1) { \
+ static bool first_print = true; \
+ if (first_print) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, DEBUG_STR(m_msg)); \
+ first_print = false; \
+ } \
+ } else \
+ ((void)0)
-/**
- * Prints an error message without returning, but only do so once in the application lifecycle.
- * This can be used to avoid spamming the console with error messages.
- */
-#define ERR_PRINT_ONCE(m_string) \
- { \
- static bool first_print = true; \
- if (first_print) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string); \
- first_print = false; \
- } \
- }
+// Print warning message macros.
/**
- * Prints a warning message without returning. To warn about deprecated usage,
- * use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead.
+ * Prints `m_msg`.
+ *
+ * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead.
*/
-#define WARN_PRINT(m_string) \
- { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string, ERR_HANDLER_WARNING); \
- }
+#define WARN_PRINT(m_msg) \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, DEBUG_STR(m_msg), ERR_HANDLER_WARNING)
/**
- * Prints a warning message without returning.
- * FIXME: Remove this macro and replace all uses with `WARN_PRINT` as it's identical.
+ * Prints `m_msg` once during the application lifetime.
+ *
+ * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead.
*/
-#define WARN_PRINTS(m_string) \
- { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string, ERR_HANDLER_WARNING); \
- }
+#define WARN_PRINT_ONCE(m_msg) \
+ if (1) { \
+ static bool first_print = true; \
+ if (first_print) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, DEBUG_STR(m_msg), ERR_HANDLER_WARNING); \
+ first_print = false; \
+ } \
+ } else \
+ ((void)0)
-/**
- * Prints a warning message without returning, but only do so once in the application lifecycle.
- * This can be used to avoid spamming the console with warning messages.
- */
-#define WARN_PRINT_ONCE(m_string) \
- { \
- static bool first_print = true; \
- if (first_print) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string, ERR_HANDLER_WARNING); \
- first_print = false; \
- } \
- }
+// Print deprecated warning message macros.
/**
- * Prints a generic deprecation warning message without returning.
- * This should be preferred to `WARN_PRINT` for deprecation warnings.
+ * Warns that the current function is deprecated.
*/
#define WARN_DEPRECATED \
- { \
+ if (1) { \
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); \
warning_shown = true; \
} \
- }
-
-/**
- * Prints a custom deprecation warning message without returning.
- * This should be preferred to `WARN_PRINT` for deprecation warnings.
- */
-#define WARN_DEPRECATED_MSG(m_msg) \
- { \
- 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.", m_msg, ERR_HANDLER_WARNING); \
- warning_shown = true; \
- } \
- }
+ } else \
+ ((void)0)
+
+/**
+ * Warns that the current function is deprecated and prints `m_msg`.
+ */
+#define WARN_DEPRECATED_MSG(m_msg) \
+ if (1) { \
+ 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.", DEBUG_STR(m_msg), ERR_HANDLER_WARNING); \
+ warning_shown = true; \
+ } \
+ } else \
+ ((void)0)
+
+/**
+ * Do not use.
+ * If the application should never reach this point use CRASH_NOW_MSG(m_msg) to explain why.
+ *
+ * The application crashes.
+ */
+#define CRASH_NOW() \
+ if (1) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/Function Failed."); \
+ GENERATE_TRAP(); \
+ } else \
+ ((void)0)
+
+/**
+ * Only use if the application should never reach this point.
+ *
+ * Prints `m_msg`, and then the application crashes.
+ */
+#define CRASH_NOW_MSG(m_msg) \
+ if (1) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/Function Failed.", DEBUG_STR(m_msg)); \
+ GENERATE_TRAP(); \
+ } else \
+ ((void)0)
#endif
diff --git a/core/func_ref.cpp b/core/func_ref.cpp
index 2dffb30bab..e20188c813 100644
--- a/core/func_ref.cpp
+++ b/core/func_ref.cpp
@@ -32,7 +32,7 @@
Variant FuncRef::call_func(const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
- if (id == 0) {
+ if (id.is_null()) {
r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL;
return Variant();
}
@@ -48,7 +48,7 @@ Variant FuncRef::call_func(const Variant **p_args, int p_argcount, Variant::Call
Variant FuncRef::call_funcv(const Array &p_args) {
- ERR_FAIL_COND_V(id == 0, Variant());
+ ERR_FAIL_COND_V(id.is_null(), Variant());
Object *obj = ObjectDB::get_instance(id);
@@ -69,7 +69,7 @@ void FuncRef::set_function(const StringName &p_func) {
}
bool FuncRef::is_valid() const {
- if (id == 0)
+ if (id.is_null())
return false;
Object *obj = ObjectDB::get_instance(id);
@@ -95,6 +95,5 @@ void FuncRef::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_valid"), &FuncRef::is_valid);
}
-FuncRef::FuncRef() :
- id(0) {
+FuncRef::FuncRef() {
}
diff --git a/core/hashfuncs.h b/core/hashfuncs.h
index 647e8a40b2..d6cf04e560 100644
--- a/core/hashfuncs.h
+++ b/core/hashfuncs.h
@@ -34,10 +34,10 @@
#include "core/math/math_defs.h"
#include "core/math/math_funcs.h"
#include "core/node_path.h"
+#include "core/object_id.h"
#include "core/string_name.h"
#include "core/typedefs.h"
#include "core/ustring.h"
-
/**
* Hashing functions
*/
@@ -137,6 +137,7 @@ 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); }
+ static _FORCE_INLINE_ uint32_t hash(const ObjectID &p_id) { return hash_one_uint64(p_id); }
static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash(uint64_t(p_int)); }
static _FORCE_INLINE_ uint32_t hash(const float p_float) { return hash_djb2_one_float(p_float); }
diff --git a/core/image.cpp b/core/image.cpp
index f43c26ab19..d9f84621a3 100644
--- a/core/image.cpp
+++ b/core/image.cpp
@@ -79,12 +79,16 @@ const char *Image::format_names[Image::FORMAT_MAX] = {
"ETC2_RGB8",
"ETC2_RGBA8",
"ETC2_RGB8A1",
+ "ETC2_RA_AS_RG",
+ "FORMAT_DXT5_RA_AS_RG",
};
SavePNGFunc Image::save_png_func = NULL;
SaveEXRFunc Image::save_exr_func = NULL;
+SavePNGBufferFunc Image::save_png_buffer_func = NULL;
+
void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixelsize, uint8_t *p_data, const uint8_t *p_pixel) {
uint32_t ofs = (p_y * width + p_x) * p_pixelsize;
@@ -115,7 +119,7 @@ int Image::get_format_pixel_size(Format p_format) {
case FORMAT_RGB8: return 3;
case FORMAT_RGBA8: return 4;
case FORMAT_RGBA4444: return 2;
- case FORMAT_RGBA5551: return 2;
+ case FORMAT_RGB565: return 2;
case FORMAT_RF:
return 4; //float
case FORMAT_RGF: return 8;
@@ -159,6 +163,8 @@ int Image::get_format_pixel_size(Format p_format) {
case FORMAT_ETC2_RGB8: return 1;
case FORMAT_ETC2_RGBA8: return 1;
case FORMAT_ETC2_RGB8A1: return 1;
+ case FORMAT_ETC2_RA_AS_RG: return 1;
+ case FORMAT_DXT5_RA_AS_RG: return 1;
case FORMAT_MAX: {
}
}
@@ -207,7 +213,9 @@ void Image::get_format_min_pixel_size(Format p_format, int &r_w, int &r_h) {
case FORMAT_ETC2_RG11S:
case FORMAT_ETC2_RGB8:
case FORMAT_ETC2_RGBA8:
- case FORMAT_ETC2_RGB8A1: {
+ case FORMAT_ETC2_RGB8A1:
+ case FORMAT_ETC2_RA_AS_RG:
+ case FORMAT_DXT5_RA_AS_RG: {
r_w = 4;
r_h = 4;
@@ -268,7 +276,11 @@ int Image::get_format_block_size(Format p_format) {
case FORMAT_ETC2_RG11S:
case FORMAT_ETC2_RGB8:
case FORMAT_ETC2_RGBA8:
- case FORMAT_ETC2_RGB8A1: {
+ case FORMAT_ETC2_RGB8A1:
+ case FORMAT_ETC2_RA_AS_RG: //used to make basis universal happy
+ case FORMAT_DXT5_RA_AS_RG: //used to make basis universal happy
+
+ {
return 4;
}
@@ -318,6 +330,17 @@ int Image::get_mipmap_offset(int p_mipmap) const {
return ofs;
}
+int Image::get_mipmap_byte_size(int p_mipmap) const {
+
+ ERR_FAIL_INDEX_V(p_mipmap, get_mipmap_count() + 1, -1);
+
+ int ofs, w, h;
+ _get_mipmap_offset_and_size(p_mipmap, ofs, w, h);
+ int ofs2;
+ _get_mipmap_offset_and_size(p_mipmap + 1, ofs2, w, h);
+ return ofs2 - ofs;
+}
+
void Image::get_mipmap_offset_and_size(int p_mipmap, int &r_ofs, int &r_size) const {
int ofs, w, h;
@@ -891,6 +914,7 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
ERR_FAIL_COND_MSG(p_height <= 0, "Image height must be greater than 0.");
ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + ".");
ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + ".");
+ ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS));
if (p_width == width && p_height == height)
return;
@@ -1215,7 +1239,7 @@ void Image::flip_x() {
}
}
-int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps) {
+int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps, int *r_mm_width, int *r_mm_height) {
int size = 0;
int w = p_width;
@@ -1242,6 +1266,13 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &
size += s;
+ if (r_mm_width) {
+ *r_mm_width = bw;
+ }
+ if (r_mm_height) {
+ *r_mm_height = bh;
+ }
+
if (p_mipmaps >= 0 && mm == p_mipmaps)
break;
@@ -1547,6 +1578,206 @@ Error Image::generate_mipmaps(bool p_renormalize) {
return OK;
}
+Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, const Ref<Image> &p_normal_map) {
+
+ Vector<double> normal_sat_vec; //summed area table
+ double *normal_sat = nullptr; //summed area table for normalmap
+ int normal_w = 0, normal_h = 0;
+
+ ERR_FAIL_COND_V_MSG(p_normal_map.is_null() || p_normal_map->empty(), ERR_INVALID_PARAMETER, "Must provide a valid normalmap for roughness mipmaps");
+
+ Ref<Image> nm = p_normal_map->duplicate();
+ if (nm->is_compressed()) {
+ nm->decompress();
+ }
+
+ normal_w = nm->get_width();
+ normal_h = nm->get_height();
+
+ normal_sat_vec.resize(normal_w * normal_h * 3);
+
+ normal_sat = normal_sat_vec.ptrw();
+
+ //create summed area table
+ nm->lock();
+
+ for (int y = 0; y < normal_h; y++) {
+ double line_sum[3] = { 0, 0, 0 };
+ for (int x = 0; x < normal_w; x++) {
+ double normal[3];
+ Color color = nm->get_pixel(x, y);
+ normal[0] = color.r * 2.0 - 1.0;
+ normal[1] = color.g * 2.0 - 1.0;
+ normal[2] = Math::sqrt(MAX(0.0, 1.0 - (normal[0] * normal[0] + normal[1] * normal[1]))); //reconstruct if missing
+
+ line_sum[0] += normal[0];
+ line_sum[1] += normal[1];
+ line_sum[2] += normal[2];
+
+ uint32_t ofs = (y * normal_w + x) * 3;
+
+ normal_sat[ofs + 0] = line_sum[0];
+ normal_sat[ofs + 1] = line_sum[1];
+ normal_sat[ofs + 2] = line_sum[2];
+
+ if (y > 0) {
+ uint32_t prev_ofs = ((y - 1) * normal_w + x) * 3;
+ normal_sat[ofs + 0] += normal_sat[prev_ofs + 0];
+ normal_sat[ofs + 1] += normal_sat[prev_ofs + 1];
+ normal_sat[ofs + 2] += normal_sat[prev_ofs + 2];
+ }
+ }
+ }
+
+#if 0
+ {
+ Vector3 beg(normal_sat_vec[0], normal_sat_vec[1], normal_sat_vec[2]);
+ Vector3 end(normal_sat_vec[normal_sat_vec.size() - 3], normal_sat_vec[normal_sat_vec.size() - 2], normal_sat_vec[normal_sat_vec.size() - 1]);
+ Vector3 avg = (end - beg) / (normal_w * normal_h);
+ print_line("average: " + avg);
+ }
+#endif
+
+ int mmcount;
+
+ _get_dst_image_size(width, height, format, mmcount);
+
+ lock();
+
+ uint8_t *base_ptr = write_lock.ptr();
+
+ for (int i = 1; i <= mmcount; i++) {
+
+ int ofs, w, h;
+ _get_mipmap_offset_and_size(i, ofs, w, h);
+ uint8_t *ptr = &base_ptr[ofs];
+
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ int from_x = x * normal_w / w;
+ int from_y = y * normal_h / h;
+ int to_x = (x + 1) * normal_w / w;
+ int to_y = (y + 1) * normal_h / h;
+ to_x = MIN(to_x - 1, normal_w);
+ to_y = MIN(to_y - 1, normal_h);
+
+ int size_x = (to_x - from_x) + 1;
+ int size_y = (to_y - from_y) + 1;
+
+ //summed area table version (much faster)
+
+ double avg[3] = { 0, 0, 0 };
+
+ if (from_x > 0 && from_y > 0) {
+ uint32_t tofs = ((from_y - 1) * normal_w + (from_x - 1)) * 3;
+ avg[0] += normal_sat[tofs + 0];
+ avg[1] += normal_sat[tofs + 1];
+ avg[2] += normal_sat[tofs + 2];
+ }
+
+ if (from_y > 0) {
+ uint32_t tofs = ((from_y - 1) * normal_w + to_x) * 3;
+ avg[0] -= normal_sat[tofs + 0];
+ avg[1] -= normal_sat[tofs + 1];
+ avg[2] -= normal_sat[tofs + 2];
+ }
+
+ if (from_x > 0) {
+ uint32_t tofs = (to_y * normal_w + (from_x - 1)) * 3;
+ avg[0] -= normal_sat[tofs + 0];
+ avg[1] -= normal_sat[tofs + 1];
+ avg[2] -= normal_sat[tofs + 2];
+ }
+
+ uint32_t tofs = (to_y * normal_w + to_x) * 3;
+ avg[0] += normal_sat[tofs + 0];
+ avg[1] += normal_sat[tofs + 1];
+ avg[2] += normal_sat[tofs + 2];
+
+ double div = double(size_x * size_y);
+ Vector3 vec(avg[0] / div, avg[1] / div, avg[2] / div);
+
+ float r = vec.length();
+
+ int pixel_ofs = y * w + x;
+ Color c = _get_color_at_ofs(ptr, pixel_ofs);
+
+ float roughness;
+
+ switch (p_roughness_channel) {
+ case ROUGHNESS_CHANNEL_R: {
+ roughness = c.r;
+ } break;
+ case ROUGHNESS_CHANNEL_G: {
+ roughness = c.g;
+ } break;
+ case ROUGHNESS_CHANNEL_B: {
+ roughness = c.b;
+ } break;
+ case ROUGHNESS_CHANNEL_L: {
+ roughness = c.get_v();
+ } break;
+ case ROUGHNESS_CHANNEL_A: {
+ roughness = c.a;
+ } break;
+ }
+
+ float variance = 0;
+ if (r < 1.0f) {
+ float r2 = r * r;
+ float kappa = (3.0f * r - r * r2) / (1.0f - r2);
+ variance = 0.25f / kappa;
+ }
+
+ float threshold = 0.4;
+ roughness = Math::sqrt(roughness * roughness + MIN(3.0f * variance, threshold * threshold));
+
+ switch (p_roughness_channel) {
+ case ROUGHNESS_CHANNEL_R: {
+ c.r = roughness;
+ } break;
+ case ROUGHNESS_CHANNEL_G: {
+ c.g = roughness;
+ } break;
+ case ROUGHNESS_CHANNEL_B: {
+ c.b = roughness;
+ } break;
+ case ROUGHNESS_CHANNEL_L: {
+ c.r = roughness;
+ c.g = roughness;
+ c.b = roughness;
+ } break;
+ case ROUGHNESS_CHANNEL_A: {
+ c.a = roughness;
+ } break;
+ }
+
+ _set_color_at_ofs(ptr, pixel_ofs, c);
+ }
+ }
+#if 0
+ {
+ int size = get_mipmap_byte_size(i);
+ print_line("size for mimpap " + itos(i) + ": " + itos(size));
+ PoolVector<uint8_t> imgdata;
+ imgdata.resize(size);
+ PoolVector<uint8_t>::Write wr = imgdata.write();
+ copymem(wr.ptr(), ptr, size);
+ wr = PoolVector<uint8_t>::Write();
+ Ref<Image> im;
+ im.instance();
+ im->create(w, h, false, format, imgdata);
+ im->save_png("res://mipmap_" + itos(i) + ".png");
+ }
+#endif
+ }
+
+ unlock();
+ nm->unlock();
+
+ return OK;
+}
+
void Image::clear_mipmaps() {
if (!mipmaps)
@@ -1576,6 +1807,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma
ERR_FAIL_INDEX(p_width - 1, MAX_WIDTH);
ERR_FAIL_INDEX(p_height - 1, MAX_HEIGHT);
+ ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS));
int mm = 0;
int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
@@ -1595,6 +1827,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma
ERR_FAIL_INDEX(p_width - 1, MAX_WIDTH);
ERR_FAIL_INDEX(p_height - 1, MAX_HEIGHT);
+ ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS));
int mm;
int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
@@ -1879,7 +2112,7 @@ Image::AlphaMode Image::detect_alpha() const {
Error Image::load(const String &p_path) {
#ifdef DEBUG_ENABLED
if (p_path.begins_with("res://") && ResourceLoader::exists(p_path)) {
- WARN_PRINTS("Loaded resource as image file, this will not work on export: '" + p_path + "'. Instead, import the image file as an Image resource and load it normally as a resource.");
+ WARN_PRINT("Loaded resource as image file, this will not work on export: '" + p_path + "'. Instead, import the image file as an Image resource and load it normally as a resource.");
}
#endif
return ImageLoader::load_image(p_path, this);
@@ -1893,6 +2126,14 @@ Error Image::save_png(const String &p_path) const {
return save_png_func(p_path, Ref<Image>((Image *)this));
}
+PoolVector<uint8_t> Image::save_png_to_buffer() const {
+ if (save_png_buffer_func == NULL) {
+ return PoolVector<uint8_t>();
+ }
+
+ return save_png_buffer_func(Ref<Image>((Image *)this));
+}
+
Error Image::save_exr(const String &p_path, bool p_grayscale) const {
if (save_exr_func == NULL)
@@ -1914,6 +2155,13 @@ int Image::get_image_required_mipmaps(int p_width, int p_height, Format p_format
return mm;
}
+Size2i Image::get_image_mipmap_size(int p_width, int p_height, Format p_format, int p_mipmap) {
+ int mm;
+ Size2i ret;
+ _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmap, &ret.x, &ret.y);
+ return ret;
+}
+
int Image::get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap) {
if (p_mipmap <= 0) {
@@ -1923,13 +2171,24 @@ int Image::get_image_mipmap_offset(int p_width, int p_height, Format p_format, i
return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmap - 1);
}
+int Image::get_image_mipmap_offset_and_dimensions(int p_width, int p_height, Format p_format, int p_mipmap, int &r_w, int &r_h) {
+
+ if (p_mipmap <= 0) {
+ r_w = p_width;
+ r_h = p_height;
+ return 0;
+ }
+ int mm;
+ return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmap - 1, &r_w, &r_h);
+}
+
bool Image::is_compressed() const {
return format > FORMAT_RGBE9995;
}
Error Image::decompress() {
- if (format >= FORMAT_DXT1 && format <= FORMAT_RGTC_RG && _image_decompress_bc)
+ if (((format >= FORMAT_DXT1 && format <= FORMAT_RGTC_RG) || (format == FORMAT_DXT5_RA_AS_RG)) && _image_decompress_bc)
_image_decompress_bc(this);
else if (format >= FORMAT_BPTC_RGBA && format <= FORMAT_BPTC_RGBFU && _image_decompress_bptc)
_image_decompress_bptc(this);
@@ -1937,7 +2196,7 @@ Error Image::decompress() {
_image_decompress_pvrtc(this);
else if (format == FORMAT_ETC && _image_decompress_etc1)
_image_decompress_etc1(this);
- else if (format >= FORMAT_ETC2_R11 && format <= FORMAT_ETC2_RGB8A1 && _image_decompress_etc2)
+ else if (format >= FORMAT_ETC2_R11 && format <= FORMAT_ETC2_RA_AS_RG && _image_decompress_etc2)
_image_decompress_etc2(this);
else
return ERR_UNAVAILABLE;
@@ -1946,12 +2205,16 @@ Error Image::decompress() {
Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_lossy_quality) {
+ return compress_from_channels(p_mode, detect_used_channels(p_source), p_lossy_quality);
+}
+Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality) {
+
switch (p_mode) {
case COMPRESS_S3TC: {
ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE);
- _image_compress_bc_func(this, p_lossy_quality, p_source);
+ _image_compress_bc_func(this, p_lossy_quality, p_channels);
} break;
case COMPRESS_PVRTC2: {
@@ -1971,12 +2234,12 @@ Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_loss
case COMPRESS_ETC2: {
ERR_FAIL_COND_V(!_image_compress_etc2_func, ERR_UNAVAILABLE);
- _image_compress_etc2_func(this, p_lossy_quality, p_source);
+ _image_compress_etc2_func(this, p_lossy_quality, p_channels);
} break;
case COMPRESS_BPTC: {
ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE);
- _image_compress_bptc_func(this, p_lossy_quality, p_source);
+ _image_compress_bptc_func(this, p_lossy_quality, p_channels);
} break;
}
@@ -2015,7 +2278,7 @@ Image::Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const P
Rect2 Image::get_used_rect() const {
- if (format != FORMAT_LA8 && format != FORMAT_RGBA8 && format != FORMAT_RGBAF && format != FORMAT_RGBAH && format != FORMAT_RGBA4444 && format != FORMAT_RGBA5551)
+ if (format != FORMAT_LA8 && format != FORMAT_RGBA8 && format != FORMAT_RGBAF && format != FORMAT_RGBAH && format != FORMAT_RGBA4444 && format != FORMAT_RGB565)
return Rect2(Point2(), Size2(width, height));
int len = data.size();
@@ -2317,12 +2580,12 @@ 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 *, float, Image::CompressSource) = NULL;
-void (*Image::_image_compress_bptc_func)(Image *, float, Image::CompressSource) = NULL;
+void (*Image::_image_compress_bc_func)(Image *, float, Image::UsedChannels) = NULL;
+void (*Image::_image_compress_bptc_func)(Image *, float, Image::UsedChannels) = NULL;
void (*Image::_image_compress_pvrtc2_func)(Image *) = NULL;
void (*Image::_image_compress_pvrtc4_func)(Image *) = NULL;
void (*Image::_image_compress_etc1_func)(Image *, float) = NULL;
-void (*Image::_image_compress_etc2_func)(Image *, float, Image::CompressSource) = NULL;
+void (*Image::_image_compress_etc2_func)(Image *, float, Image::UsedChannels) = NULL;
void (*Image::_image_decompress_pvrtc)(Image *) = NULL;
void (*Image::_image_decompress_bc)(Image *) = NULL;
void (*Image::_image_decompress_bptc)(Image *) = NULL;
@@ -2333,6 +2596,8 @@ PoolVector<uint8_t> (*Image::lossy_packer)(const Ref<Image> &, float) = NULL;
Ref<Image> (*Image::lossy_unpacker)(const PoolVector<uint8_t> &) = NULL;
PoolVector<uint8_t> (*Image::lossless_packer)(const Ref<Image> &) = NULL;
Ref<Image> (*Image::lossless_unpacker)(const PoolVector<uint8_t> &) = NULL;
+PoolVector<uint8_t> (*Image::basis_universal_packer)(const Ref<Image> &, Image::UsedChannels) = NULL;
+Ref<Image> (*Image::basis_universal_unpacker)(const PoolVector<uint8_t> &) = NULL;
void Image::_set_data(const Dictionary &p_data) {
@@ -2386,18 +2651,7 @@ Color Image::get_pixelv(const Point2 &p_src) const {
return get_pixel(p_src.x, p_src.y);
}
-Color Image::get_pixel(int p_x, int p_y) const {
-
- uint8_t *ptr = write_lock.ptr();
-#ifdef DEBUG_ENABLED
- ERR_FAIL_COND_V_MSG(!ptr, Color(), "Image must be locked with 'lock()' before using get_pixel().");
-
- ERR_FAIL_INDEX_V(p_x, width, Color());
- ERR_FAIL_INDEX_V(p_y, height, Color());
-
-#endif
-
- uint32_t ofs = p_y * width + p_x;
+Color Image::_get_color_at_ofs(uint8_t *ptr, uint32_t ofs) const {
switch (format) {
case FORMAT_L8: {
@@ -2441,14 +2695,13 @@ Color Image::get_pixel(int p_x, int p_y) const {
float a = (u & 0xF) / 15.0;
return Color(r, g, b, a);
}
- case FORMAT_RGBA5551: {
+ case FORMAT_RGB565: {
uint16_t u = ((uint16_t *)ptr)[ofs];
- float r = ((u >> 11) & 0x1F) / 15.0;
- float g = ((u >> 6) & 0x1F) / 15.0;
- float b = ((u >> 1) & 0x1F) / 15.0;
- float a = (u & 0x1) / 1.0;
- return Color(r, g, b, a);
+ float r = (u & 0x1F) / 31.0;
+ float g = ((u >> 5) & 0x3F) / 63.0;
+ float b = ((u >> 11) & 0x1F) / 31.0;
+ return Color(r, g, b, 1.0);
}
case FORMAT_RF: {
@@ -2511,23 +2764,7 @@ Color Image::get_pixel(int p_x, int p_y) const {
}
}
-void Image::set_pixelv(const Point2 &p_dst, const Color &p_color) {
- set_pixel(p_dst.x, p_dst.y, p_color);
-}
-
-void Image::set_pixel(int p_x, int p_y, const Color &p_color) {
-
- uint8_t *ptr = write_lock.ptr();
-#ifdef DEBUG_ENABLED
- ERR_FAIL_COND_MSG(!ptr, "Image must be locked with 'lock()' before using set_pixel().");
-
- ERR_FAIL_INDEX(p_x, width);
- ERR_FAIL_INDEX(p_y, height);
-
-#endif
-
- uint32_t ofs = p_y * width + p_x;
-
+void Image::_set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color) {
switch (format) {
case FORMAT_L8: {
ptr[ofs] = uint8_t(CLAMP(p_color.get_v() * 255.0, 0, 255));
@@ -2569,14 +2806,13 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) {
((uint16_t *)ptr)[ofs] = rgba;
} break;
- case FORMAT_RGBA5551: {
+ case FORMAT_RGB565: {
uint16_t rgba = 0;
- rgba = uint16_t(CLAMP(p_color.r * 31.0, 0, 31)) << 11;
- rgba |= uint16_t(CLAMP(p_color.g * 31.0, 0, 31)) << 6;
- rgba |= uint16_t(CLAMP(p_color.b * 31.0, 0, 31)) << 1;
- rgba |= uint16_t(p_color.a > 0.5 ? 1 : 0);
+ rgba = uint16_t(CLAMP(p_color.r * 31.0, 0, 31));
+ rgba |= uint16_t(CLAMP(p_color.g * 63.0, 0, 33)) << 5;
+ rgba |= uint16_t(CLAMP(p_color.b * 31.0, 0, 31)) << 11;
((uint16_t *)ptr)[ofs] = rgba;
@@ -2636,10 +2872,44 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) {
}
}
-Image::DetectChannels Image::get_detected_channels() {
+Color Image::get_pixel(int p_x, int p_y) const {
+
+ uint8_t *ptr = write_lock.ptr();
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V_MSG(!ptr, Color(), "Image must be locked with 'lock()' before using get_pixel().");
+
+ ERR_FAIL_INDEX_V(p_x, width, Color());
+ ERR_FAIL_INDEX_V(p_y, height, Color());
+
+#endif
+
+ uint32_t ofs = p_y * width + p_x;
+ return _get_color_at_ofs(ptr, ofs);
+}
+
+void Image::set_pixelv(const Point2 &p_dst, const Color &p_color) {
+ set_pixel(p_dst.x, p_dst.y, p_color);
+}
+
+void Image::set_pixel(int p_x, int p_y, const Color &p_color) {
+
+ uint8_t *ptr = write_lock.ptr();
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_MSG(!ptr, "Image must be locked with 'lock()' before using set_pixel().");
+
+ ERR_FAIL_INDEX(p_x, width);
+ ERR_FAIL_INDEX(p_y, height);
+
+#endif
+
+ uint32_t ofs = p_y * width + p_x;
+ _set_color_at_ofs(ptr, ofs, p_color);
+}
- ERR_FAIL_COND_V(data.size() == 0, DETECTED_RGBA);
- ERR_FAIL_COND_V(is_compressed(), DETECTED_RGBA);
+Image::UsedChannels Image::detect_used_channels(CompressSource p_source) {
+
+ ERR_FAIL_COND_V(data.size() == 0, USED_CHANNELS_RGBA);
+ ERR_FAIL_COND_V(is_compressed(), USED_CHANNELS_RGBA);
bool r = false, g = false, b = false, a = false, c = false;
lock();
for (int i = 0; i < width; i++) {
@@ -2664,31 +2934,42 @@ Image::DetectChannels Image::get_detected_channels() {
unlock();
- if (!c && !a)
- return DETECTED_L;
- if (!c && a)
- return DETECTED_LA;
+ UsedChannels used_channels;
- if (r && !g && !b && !a)
- return DETECTED_R;
+ if (!c && !a)
+ used_channels = USED_CHANNELS_L;
+ else if (!c && a)
+ used_channels = USED_CHANNELS_LA;
+ else if (r && !g && !b && !a)
+ used_channels = USED_CHANNELS_R;
+ else if (r && g && !b && !a)
+ used_channels = USED_CHANNELS_RG;
+ else if (r && g && b && !a)
+ used_channels = USED_CHANNELS_RGB;
+ else
+ used_channels = USED_CHANNELS_RGBA;
- if (r && g && !b && !a)
- return DETECTED_RG;
+ if (p_source == COMPRESS_SOURCE_SRGB && (used_channels == USED_CHANNELS_R || used_channels == USED_CHANNELS_RG)) {
+ //R and RG do not support SRGB
+ used_channels = USED_CHANNELS_RGB;
+ }
- if (r && g && b && !a)
- return DETECTED_RGB;
+ if (p_source == COMPRESS_SOURCE_NORMAL) {
+ //use RG channels only for normal
+ used_channels = USED_CHANNELS_RG;
+ }
- return DETECTED_RGBA;
+ return used_channels;
}
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;
+ switch (detect_used_channels()) {
+ case USED_CHANNELS_L: convert(FORMAT_L8); break;
+ case USED_CHANNELS_LA: convert(FORMAT_LA8); break;
+ case USED_CHANNELS_R: convert(FORMAT_R8); break;
+ case USED_CHANNELS_RG: convert(FORMAT_RG8); break;
+ case USED_CHANNELS_RGB: convert(FORMAT_RGB8); break;
+ case USED_CHANNELS_RGBA: convert(FORMAT_RGBA8); break;
}
}
@@ -2728,7 +3009,9 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("detect_alpha"), &Image::detect_alpha);
ClassDB::bind_method(D_METHOD("is_invisible"), &Image::is_invisible);
- ClassDB::bind_method(D_METHOD("compress", "mode", "source", "lossy_quality"), &Image::compress);
+ ClassDB::bind_method(D_METHOD("detect_used_channels", "source"), &Image::detect_used_channels, DEFVAL(COMPRESS_SOURCE_GENERIC));
+ ClassDB::bind_method(D_METHOD("compress", "mode", "source", "lossy_quality"), &Image::compress, DEFVAL(COMPRESS_SOURCE_GENERIC), DEFVAL(0.7));
+ ClassDB::bind_method(D_METHOD("compress_from_channels", "mode", "channels", "lossy_quality"), &Image::compress, DEFVAL(0.7));
ClassDB::bind_method(D_METHOD("decompress"), &Image::decompress);
ClassDB::bind_method(D_METHOD("is_compressed"), &Image::is_compressed);
@@ -2776,7 +3059,7 @@ void Image::_bind_methods() {
BIND_ENUM_CONSTANT(FORMAT_RGB8);
BIND_ENUM_CONSTANT(FORMAT_RGBA8);
BIND_ENUM_CONSTANT(FORMAT_RGBA4444);
- BIND_ENUM_CONSTANT(FORMAT_RGBA5551);
+ BIND_ENUM_CONSTANT(FORMAT_RGB565);
BIND_ENUM_CONSTANT(FORMAT_RF); //float
BIND_ENUM_CONSTANT(FORMAT_RGF);
BIND_ENUM_CONSTANT(FORMAT_RGBF);
@@ -2806,6 +3089,8 @@ void Image::_bind_methods() {
BIND_ENUM_CONSTANT(FORMAT_ETC2_RGB8);
BIND_ENUM_CONSTANT(FORMAT_ETC2_RGBA8);
BIND_ENUM_CONSTANT(FORMAT_ETC2_RGB8A1);
+ BIND_ENUM_CONSTANT(FORMAT_ETC2_RA_AS_RG);
+ BIND_ENUM_CONSTANT(FORMAT_DXT5_RA_AS_RG);
BIND_ENUM_CONSTANT(FORMAT_MAX);
BIND_ENUM_CONSTANT(INTERPOLATE_NEAREST);
@@ -2824,17 +3109,24 @@ void Image::_bind_methods() {
BIND_ENUM_CONSTANT(COMPRESS_ETC);
BIND_ENUM_CONSTANT(COMPRESS_ETC2);
+ BIND_ENUM_CONSTANT(USED_CHANNELS_L);
+ BIND_ENUM_CONSTANT(USED_CHANNELS_LA);
+ BIND_ENUM_CONSTANT(USED_CHANNELS_R);
+ BIND_ENUM_CONSTANT(USED_CHANNELS_RG);
+ BIND_ENUM_CONSTANT(USED_CHANNELS_RGB);
+ BIND_ENUM_CONSTANT(USED_CHANNELS_RGBA);
+
BIND_ENUM_CONSTANT(COMPRESS_SOURCE_GENERIC);
BIND_ENUM_CONSTANT(COMPRESS_SOURCE_SRGB);
BIND_ENUM_CONSTANT(COMPRESS_SOURCE_NORMAL);
}
-void Image::set_compress_bc_func(void (*p_compress_func)(Image *, float, CompressSource)) {
+void Image::set_compress_bc_func(void (*p_compress_func)(Image *, float, UsedChannels)) {
_image_compress_bc_func = p_compress_func;
}
-void Image::set_compress_bptc_func(void (*p_compress_func)(Image *, float, CompressSource)) {
+void Image::set_compress_bptc_func(void (*p_compress_func)(Image *, float, UsedChannels)) {
_image_compress_bptc_func = p_compress_func;
}
@@ -2890,6 +3182,29 @@ Ref<Image> Image::rgbe_to_srgb() {
return new_image;
}
+Ref<Image> Image::get_image_from_mipmap(int p_mipamp) const {
+
+ int ofs, size, w, h;
+ get_mipmap_offset_size_and_dimensions(p_mipamp, ofs, size, w, h);
+
+ PoolVector<uint8_t> new_data;
+ new_data.resize(size);
+ {
+ PoolVector<uint8_t>::Write wr = new_data.write();
+ PoolVector<uint8_t>::Read rd = data.read();
+ copymem(wr.ptr(), rd.ptr() + ofs, size);
+ }
+
+ Ref<Image> image;
+ image.instance();
+ image->width = w;
+ image->height = h;
+ image->format = format;
+ image->data = new_data;
+ image->mipmaps = false;
+ return image;
+}
+
void Image::bumpmap_to_normalmap(float bump_scale) {
ERR_FAIL_COND(!_can_modify(format));
convert(Image::FORMAT_RF);
@@ -3078,6 +3393,31 @@ Error Image::load_webp_from_buffer(const PoolVector<uint8_t> &p_array) {
return _load_from_buffer(p_array, _webp_mem_loader_func);
}
+void Image::convert_rg_to_ra_rgba8() {
+ ERR_FAIL_COND(format != FORMAT_RGBA8);
+ ERR_FAIL_COND(!data.size());
+
+ int s = data.size();
+ PoolVector<uint8_t>::Write w = data.write();
+ for (int i = 0; i < s; i += 4) {
+ w[i + 3] = w[i + 1];
+ w[i + 1] = 0;
+ w[i + 2] = 0;
+ }
+}
+void Image::convert_ra_rgba8_to_rg() {
+ ERR_FAIL_COND(format != FORMAT_RGBA8);
+ ERR_FAIL_COND(!data.size());
+
+ int s = data.size();
+ PoolVector<uint8_t>::Write w = data.write();
+ for (int i = 0; i < s; i += 4) {
+ w[i + 1] = w[i + 3];
+ w[i + 2] = 0;
+ w[i + 3] = 255;
+ }
+}
+
Error Image::_load_from_buffer(const PoolVector<uint8_t> &p_array, ImageMemLoadFunc p_loader) {
int buffer_size = p_array.size();
diff --git a/core/image.h b/core/image.h
index c15cfc9f6f..1cc22420d5 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 PoolVector<uint8_t> (*SavePNGBufferFunc)(const Ref<Image> &p_img);
typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size);
typedef Error (*SaveEXRFunc)(const String &p_path, const Ref<Image> &p_img, bool p_grayscale);
@@ -57,10 +58,12 @@ class Image : public Resource {
public:
static SavePNGFunc save_png_func;
static SaveEXRFunc save_exr_func;
+ static SavePNGBufferFunc save_png_buffer_func;
enum {
- MAX_WIDTH = 16384, // force a limit somehow
- MAX_HEIGHT = 16384 // force a limit somehow
+ MAX_WIDTH = (1 << 24), // force a limit somehow
+ MAX_HEIGHT = (1 << 24), // force a limit somehow
+ MAX_PIXELS = 268435456
};
enum Format {
@@ -72,7 +75,7 @@ public:
FORMAT_RGB8,
FORMAT_RGBA8,
FORMAT_RGBA4444,
- FORMAT_RGBA5551,
+ FORMAT_RGB565,
FORMAT_RF, //float
FORMAT_RGF,
FORMAT_RGBF,
@@ -102,6 +105,8 @@ public:
FORMAT_ETC2_RGB8,
FORMAT_ETC2_RGBA8,
FORMAT_ETC2_RGB8A1,
+ FORMAT_ETC2_RA_AS_RG, //used to make basis universal happy
+ FORMAT_DXT5_RA_AS_RG, //used to make basis universal happy
FORMAT_MAX
};
@@ -117,25 +122,27 @@ public:
/* INTERPOLATE GAUSS */
};
- enum CompressSource {
- COMPRESS_SOURCE_GENERIC,
- COMPRESS_SOURCE_SRGB,
- COMPRESS_SOURCE_NORMAL,
- COMPRESS_SOURCE_LAYERED,
+ //this is used for compression
+ enum UsedChannels {
+ USED_CHANNELS_L,
+ USED_CHANNELS_LA,
+ USED_CHANNELS_R,
+ USED_CHANNELS_RG,
+ USED_CHANNELS_RGB,
+ USED_CHANNELS_RGBA,
};
-
//some functions provided by something else
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 *, float, CompressSource p_source);
- static void (*_image_compress_bptc_func)(Image *, float p_lossy_quality, CompressSource p_source);
+ static void (*_image_compress_bc_func)(Image *, float, UsedChannels p_channels);
+ static void (*_image_compress_bptc_func)(Image *, float p_lossy_quality, UsedChannels p_channels);
static void (*_image_compress_pvrtc2_func)(Image *);
static void (*_image_compress_pvrtc4_func)(Image *);
static void (*_image_compress_etc1_func)(Image *, float);
- static void (*_image_compress_etc2_func)(Image *, float, CompressSource p_source);
+ static void (*_image_compress_etc2_func)(Image *, float, UsedChannels p_channels);
static void (*_image_decompress_pvrtc)(Image *);
static void (*_image_decompress_bc)(Image *);
@@ -147,9 +154,14 @@ public:
static Ref<Image> (*lossy_unpacker)(const PoolVector<uint8_t> &p_buffer);
static PoolVector<uint8_t> (*lossless_packer)(const Ref<Image> &p_image);
static Ref<Image> (*lossless_unpacker)(const PoolVector<uint8_t> &p_buffer);
+ static PoolVector<uint8_t> (*basis_universal_packer)(const Ref<Image> &p_image, UsedChannels p_channels);
+ static Ref<Image> (*basis_universal_unpacker)(const PoolVector<uint8_t> &p_buffer);
PoolVector<uint8_t>::Write write_lock;
+ _FORCE_INLINE_ Color _get_color_at_ofs(uint8_t *ptr, uint32_t ofs) const;
+ _FORCE_INLINE_ void _set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color);
+
protected:
static void _bind_methods();
@@ -177,7 +189,7 @@ private:
_FORCE_INLINE_ void _get_mipmap_offset_and_size(int p_mipmap, int &r_offset, int &r_width, int &r_height) const; //get where the mipmap begins in data
- static int _get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps = -1);
+ static int _get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps = -1, int *r_mm_width = NULL, int *r_mm_height = NULL);
bool _can_modify(Format p_format) const;
_FORCE_INLINE_ void _put_pixelb(int p_x, int p_y, uint32_t p_pixelsize, uint8_t *p_data, const uint8_t *p_pixel);
@@ -214,6 +226,7 @@ public:
*/
Format get_format() const;
+ int get_mipmap_byte_size(int p_mipmap) const; //get where the mipmap begins in data
int get_mipmap_offset(int p_mipmap) const; //get where the mipmap begins in data
void get_mipmap_offset_and_size(int p_mipmap, int &r_ofs, int &r_size) const; //get where the mipmap begins in data
void get_mipmap_offset_size_and_dimensions(int p_mipmap, int &r_ofs, int &r_size, int &w, int &h) const; //get where the mipmap begins in data
@@ -240,6 +253,16 @@ public:
*/
Error generate_mipmaps(bool p_renormalize = false);
+ enum RoughnessChannel {
+ ROUGHNESS_CHANNEL_R,
+ ROUGHNESS_CHANNEL_G,
+ ROUGHNESS_CHANNEL_B,
+ ROUGHNESS_CHANNEL_A,
+ ROUGHNESS_CHANNEL_L,
+ };
+
+ Error generate_mipmap_roughness(RoughnessChannel p_roughness_channel, const Ref<Image> &p_normal_map);
+
void clear_mipmaps();
void normalize(); //for normal maps
@@ -259,6 +282,7 @@ public:
Error load(const String &p_path);
Error save_png(const String &p_path) const;
+ PoolVector<uint8_t> save_png_to_buffer() const;
Error save_exr(const String &p_path, bool p_grayscale) const;
/**
@@ -290,7 +314,9 @@ public:
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);
+ static Size2i get_image_mipmap_size(int p_width, int p_height, Format p_format, int p_mipmap);
static int get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap);
+ static int get_image_mipmap_offset_and_dimensions(int p_width, int p_height, Format p_format, int p_mipmap, int &r_w, int &r_h);
enum CompressMode {
COMPRESS_S3TC,
@@ -300,8 +326,14 @@ public:
COMPRESS_ETC2,
COMPRESS_BPTC
};
+ enum CompressSource {
+ COMPRESS_SOURCE_GENERIC,
+ COMPRESS_SOURCE_SRGB,
+ COMPRESS_SOURCE_NORMAL
+ };
- Error compress(CompressMode p_mode = COMPRESS_S3TC, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7);
+ Error compress(CompressMode p_mode, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7);
+ Error compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality = 0.7);
Error decompress();
bool is_compressed() const;
@@ -310,6 +342,7 @@ public:
void srgb_to_linear();
void normalmap_to_xy();
Ref<Image> rgbe_to_srgb();
+ Ref<Image> get_image_from_mipmap(int p_mipamp) const;
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);
@@ -321,14 +354,17 @@ public:
Rect2 get_used_rect() const;
Ref<Image> get_rect(const Rect2 &p_area) const;
- static void set_compress_bc_func(void (*p_compress_func)(Image *, float, CompressSource));
- static void set_compress_bptc_func(void (*p_compress_func)(Image *, float, CompressSource));
+ static void set_compress_bc_func(void (*p_compress_func)(Image *, float, UsedChannels));
+ static void set_compress_bptc_func(void (*p_compress_func)(Image *, float, UsedChannels));
static String get_format_name(Format p_format);
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);
+ void convert_rg_to_ra_rgba8();
+ void convert_ra_rgba8_to_rg();
+
Image(const uint8_t *p_mem_png_jpg, int p_len = -1);
Image(const char **p_xpm);
@@ -337,17 +373,7 @@ public:
void lock();
void unlock();
- //this is used for compression
- enum DetectChannels {
- DETECTED_L,
- DETECTED_LA,
- DETECTED_R,
- DETECTED_RG,
- DETECTED_RGB,
- DETECTED_RGBA,
- };
-
- DetectChannels get_detected_channels();
+ UsedChannels detect_used_channels(CompressSource p_source = COMPRESS_SOURCE_GENERIC);
void optimize_channels();
Color get_pixelv(const Point2 &p_src) const;
@@ -371,6 +397,8 @@ VARIANT_ENUM_CAST(Image::Format)
VARIANT_ENUM_CAST(Image::Interpolation)
VARIANT_ENUM_CAST(Image::CompressMode)
VARIANT_ENUM_CAST(Image::CompressSource)
+VARIANT_ENUM_CAST(Image::UsedChannels)
VARIANT_ENUM_CAST(Image::AlphaMode)
+VARIANT_ENUM_CAST(Image::RoughnessChannel)
#endif
diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp
index 5c25cad770..5968972143 100644
--- a/core/io/config_file.cpp
+++ b/core/io/config_file.cpp
@@ -86,9 +86,10 @@ void ConfigFile::set_value(const String &p_section, const String &p_key, const V
Variant ConfigFile::get_value(const String &p_section, const String &p_key, Variant p_default) const {
if (!values.has(p_section) || !values[p_section].has(p_key)) {
- ERR_FAIL_COND_V_MSG(p_default.get_type() == Variant::NIL, p_default, "Couldn't find the given section/key and no default was given.");
+ ERR_FAIL_COND_V_MSG(p_default.get_type() == Variant::NIL, Variant(), "Couldn't find the given section '" + p_section + "', key '" + p_key + "' and no default was given.");
return p_default;
}
+
return values[p_section][p_key];
}
@@ -255,6 +256,22 @@ Error ConfigFile::_internal_load(const String &p_path, FileAccess *f) {
VariantParser::StreamFile stream;
stream.f = f;
+ Error err = _parse(p_path, &stream);
+
+ memdelete(f);
+
+ return err;
+}
+
+Error ConfigFile::parse(const String &p_data) {
+
+ VariantParser::StreamString stream;
+ stream.s = p_data;
+ return _parse("<string>", &stream);
+}
+
+Error ConfigFile::_parse(const String &p_path, VariantParser::Stream *p_stream) {
+
String assign;
Variant value;
VariantParser::Tag next_tag;
@@ -270,13 +287,11 @@ Error ConfigFile::_internal_load(const String &p_path, FileAccess *f) {
next_tag.fields.clear();
next_tag.name = String();
- Error err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, NULL, true);
+ Error err = VariantParser::parse_tag_assign_eof(p_stream, lines, error_text, next_tag, assign, value, NULL, true);
if (err == ERR_FILE_EOF) {
- memdelete(f);
return OK;
} else if (err != OK) {
- ERR_PRINTS("ConfgFile::load - " + p_path + ":" + itos(lines) + " error: " + error_text + ".");
- memdelete(f);
+ ERR_PRINT("ConfgFile - " + p_path + ":" + itos(lines) + " error: " + error_text + ".");
return err;
}
@@ -286,6 +301,8 @@ Error ConfigFile::_internal_load(const String &p_path, FileAccess *f) {
section = next_tag.name;
}
}
+
+ return OK;
}
void ConfigFile::_bind_methods() {
@@ -303,6 +320,7 @@ void ConfigFile::_bind_methods() {
ClassDB::bind_method(D_METHOD("erase_section_key", "section", "key"), &ConfigFile::erase_section_key);
ClassDB::bind_method(D_METHOD("load", "path"), &ConfigFile::load);
+ ClassDB::bind_method(D_METHOD("parse", "data"), &ConfigFile::parse);
ClassDB::bind_method(D_METHOD("save", "path"), &ConfigFile::save);
ClassDB::bind_method(D_METHOD("load_encrypted", "path", "key"), &ConfigFile::load_encrypted);
diff --git a/core/io/config_file.h b/core/io/config_file.h
index 95a581d156..2d61ef6afe 100644
--- a/core/io/config_file.h
+++ b/core/io/config_file.h
@@ -34,6 +34,7 @@
#include "core/ordered_hash_map.h"
#include "core/os/file_access.h"
#include "core/reference.h"
+#include "core/variant_parser.h"
class ConfigFile : public Reference {
@@ -46,6 +47,8 @@ class ConfigFile : public Reference {
Error _internal_load(const String &p_path, FileAccess *f);
Error _internal_save(FileAccess *file);
+ Error _parse(const String &p_path, VariantParser::Stream *p_stream);
+
protected:
static void _bind_methods();
@@ -64,6 +67,7 @@ public:
Error save(const String &p_path);
Error load(const String &p_path);
+ Error parse(const String &p_data);
Error load_encrypted(const String &p_path, const Vector<uint8_t> &p_key);
Error load_encrypted_pass(const String &p_path, const String &p_pass);
diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp
index 87ead37b91..17cc6ce58f 100644
--- a/core/io/file_access_compressed.cpp
+++ b/core/io/file_access_compressed.cpp
@@ -63,6 +63,10 @@ Error FileAccessCompressed::open_after_magic(FileAccess *p_base) {
f = p_base;
cmode = (Compression::Mode)f->get_32();
block_size = f->get_32();
+ if (block_size == 0) {
+ f = NULL; // Let the caller to handle the FileAccess object if failed to open as compressed file.
+ ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Can't open compressed file '" + p_base->get_path() + "' with block size 0, it is corrupted.");
+ }
read_total = f->get_32();
int bc = (read_total / block_size) + 1;
int acc_ofs = f->get_position() + bc * 4;
@@ -125,13 +129,11 @@ Error FileAccessCompressed::_open(const String &p_path, int p_mode_flags) {
char rmagic[5];
f->get_buffer((uint8_t *)rmagic, 4);
rmagic[4] = 0;
- if (magic != rmagic) {
+ if (magic != rmagic || open_after_magic(f) != OK) {
memdelete(f);
f = NULL;
return ERR_FILE_UNRECOGNIZED;
}
-
- open_after_magic(f);
}
return OK;
diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp
index 1b09ac7208..202eb89dbd 100644
--- a/core/io/file_access_network.cpp
+++ b/core/io/file_access_network.cpp
@@ -231,7 +231,7 @@ FileAccessNetworkClient::FileAccessNetworkClient() {
singleton = this;
last_id = 0;
client.instance();
- sem = Semaphore::create();
+ sem = SemaphoreOld::create();
lockcount = 0;
}
@@ -522,8 +522,8 @@ FileAccessNetwork::FileAccessNetwork() {
eof_flag = false;
opened = false;
pos = 0;
- sem = Semaphore::create();
- page_sem = Semaphore::create();
+ sem = SemaphoreOld::create();
+ page_sem = SemaphoreOld::create();
buffer_mutex = Mutex::create();
FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton;
nc->lock_mutex();
diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h
index e2da1d0893..f329abf7c5 100644
--- a/core/io/file_access_network.h
+++ b/core/io/file_access_network.h
@@ -49,7 +49,7 @@ class FileAccessNetworkClient {
List<BlockRequest> block_requests;
- Semaphore *sem;
+ SemaphoreOld *sem;
Thread *thread;
bool quit;
Mutex *mutex;
@@ -85,8 +85,8 @@ public:
class FileAccessNetwork : public FileAccess {
- Semaphore *sem;
- Semaphore *page_sem;
+ SemaphoreOld *sem;
+ SemaphoreOld *page_sem;
Mutex *buffer_mutex;
bool opened;
size_t total_size;
diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp
index 05929ee8ed..720f25f91b 100644
--- a/core/io/image_loader.cpp
+++ b/core/io/image_loader.cpp
@@ -53,7 +53,7 @@ Error ImageLoader::load_image(String p_file, Ref<Image> p_image, FileAccess *p_c
Error err;
f = FileAccess::open(p_file, FileAccess::READ, &err);
if (!f) {
- ERR_PRINTS("Error opening file '" + p_file + "'.");
+ ERR_PRINT("Error opening file '" + p_file + "'.");
return err;
}
}
@@ -66,7 +66,7 @@ Error ImageLoader::load_image(String p_file, Ref<Image> p_image, FileAccess *p_c
continue;
Error err = loader[i]->load_image(p_image, f, p_force_linear, p_scale);
if (err != OK) {
- ERR_PRINTS("Error loading image: " + p_file);
+ ERR_PRINT("Error loading image: " + p_file);
}
if (err != ERR_FILE_UNRECOGNIZED) {
diff --git a/core/io/ip.cpp b/core/io/ip.cpp
index ea791fb327..7d18117711 100644
--- a/core/io/ip.cpp
+++ b/core/io/ip.cpp
@@ -71,7 +71,7 @@ struct _IP_ResolverPrivate {
}
Mutex *mutex;
- Semaphore *sem;
+ SemaphoreOld *sem;
Thread *thread;
//Semaphore* semaphore;
@@ -184,7 +184,7 @@ IP_Address IP::get_resolve_item_address(ResolverID p_id) const {
resolver->mutex->lock();
if (resolver->queue[p_id].status != IP::RESOLVER_STATUS_DONE) {
- ERR_PRINTS("Resolve of '" + resolver->queue[p_id].hostname + "'' didn't complete yet.");
+ ERR_PRINT("Resolve of '" + resolver->queue[p_id].hostname + "'' didn't complete yet.");
resolver->mutex->unlock();
return IP_Address();
}
@@ -319,7 +319,7 @@ IP::IP() {
#ifndef NO_THREADS
- resolver->sem = Semaphore::create();
+ resolver->sem = SemaphoreOld::create();
if (resolver->sem) {
resolver->thread_abort = false;
diff --git a/core/io/json.cpp b/core/io/json.cpp
index dbf1676e62..144e4bdc3b 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -203,8 +203,7 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to
case 'f': res = 12; break;
case 'r': res = 13; break;
case 'u': {
- //hexnumbarh - oct is deprecated
-
+ // hex number
for (int j = 0; j < 4; j++) {
CharType c = p_str[index + j + 1];
if (c == 0) {
@@ -226,7 +225,7 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to
v = c - 'A';
v += 10;
} else {
- ERR_PRINT("BUG");
+ ERR_PRINT("Bug parsing hex constant.");
v = 0;
}
@@ -236,13 +235,8 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to
index += 4; //will add at the end anyway
} break;
- //case '\"': res='\"'; break;
- //case '\\': res='\\'; break;
- //case '/': res='/'; break;
default: {
res = next;
- //r_err_str="Invalid escape sequence";
- //return ERR_PARSE_ERROR;
} break;
}
diff --git a/core/io/logger.cpp b/core/io/logger.cpp
index c3ea0d024e..4d732332d5 100644
--- a/core/io/logger.cpp
+++ b/core/io/logger.cpp
@@ -58,12 +58,12 @@ void Logger::log_error(const char *p_function, const char *p_file, int p_line, c
return;
}
- const char *err_type = "**ERROR**";
+ const char *err_type = "ERROR";
switch (p_type) {
- case ERR_ERROR: err_type = "**ERROR**"; break;
- case ERR_WARNING: err_type = "**WARNING**"; break;
- case ERR_SCRIPT: err_type = "**SCRIPT ERROR**"; break;
- case ERR_SHADER: err_type = "**SHADER ERROR**"; break;
+ case ERR_ERROR: err_type = "ERROR"; break;
+ case ERR_WARNING: err_type = "WARNING"; break;
+ case ERR_SCRIPT: err_type = "SCRIPT ERROR"; break;
+ case ERR_SHADER: err_type = "SHADER ERROR"; break;
default: ERR_PRINT("Unknown error type"); break;
}
@@ -74,7 +74,7 @@ void Logger::log_error(const char *p_function, const char *p_file, int p_line, c
err_details = p_code;
logf_error("%s: %s\n", err_type, err_details);
- logf_error(" At: %s:%i:%s() - %s\n", p_file, p_line, p_function, p_code);
+ logf_error(" at: %s (%s:%i) - %s\n", p_function, p_file, p_line, p_code);
}
void Logger::logf(const char *p_format, ...) {
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index e847a9cf0c..17edc4982c 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -53,8 +53,7 @@ ObjectID EncodedObjectAsID::get_object_id() const {
return id;
}
-EncodedObjectAsID::EncodedObjectAsID() :
- id(0) {
+EncodedObjectAsID::EncodedObjectAsID() {
}
#define _S(a) ((int32_t)a)
@@ -386,11 +385,11 @@ 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);
+ ObjectID val = ObjectID(decode_uint64(buf));
if (r_len)
(*r_len) += 8;
- if (val == 0) {
+ if (val.is_null()) {
r_variant = (Object *)NULL;
} else {
Ref<EncodedObjectAsID> obj_as_id;
@@ -803,10 +802,10 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
}
} break;
case Variant::OBJECT: {
-#ifdef DEBUG_ENABLED
+
// Test for potential wrong values sent by the debugger when it breaks.
- Object *obj = p_variant;
- if (!obj || !ObjectDB::instance_validate(obj)) {
+ Object *obj = p_variant.get_validated_object();
+ if (!obj) {
// Object is invalid, send a NULL instead.
if (buf) {
encode_uint32(Variant::NIL, buf);
@@ -814,7 +813,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
r_len += 4;
return OK;
}
-#endif // DEBUG_ENABLED
+
if (!p_full_objects) {
flags |= ENCODE_FLAG_OBJECT_AS_ID;
}
@@ -1128,9 +1127,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
} else {
if (buf) {
- Object *obj = p_variant;
- ObjectID id = 0;
- if (obj && ObjectDB::instance_validate(obj)) {
+ Object *obj = p_variant.get_validated_object();
+ ObjectID id;
+ if (obj) {
id = obj->get_instance_id();
}
diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp
index 381ac4c0bb..3f66e00021 100644
--- a/core/io/multiplayer_api.cpp
+++ b/core/io/multiplayer_api.cpp
@@ -32,6 +32,7 @@
#include "core/io/marshalls.h"
#include "scene/main/node.h"
+#include <stdint.h>
#ifdef DEBUG_ENABLED
#include "core/os/os.h"
@@ -180,7 +181,8 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
}
#endif
- uint8_t packet_type = p_packet[0];
+ // Extract the `packet_type` from the LSB three bits:
+ uint8_t packet_type = p_packet[0] & 7;
switch (packet_type) {
@@ -197,31 +199,80 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
case NETWORK_COMMAND_REMOTE_CALL:
case NETWORK_COMMAND_REMOTE_SET: {
- ERR_FAIL_COND_MSG(p_packet_len < 6, "Invalid packet received. Size too small.");
-
- Node *node = _process_get_node(p_from, p_packet, p_packet_len);
+ // Extract packet meta
+ int packet_min_size = 1;
+ int name_id_offset = 1;
+ ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
+ // Compute the meta size, which depends on the compression level.
+ int node_id_compression = (p_packet[0] & 24) >> 3;
+ int name_id_compression = (p_packet[0] & 32) >> 5;
+
+ switch (node_id_compression) {
+ case NETWORK_NODE_ID_COMPRESSION_8:
+ packet_min_size += 1;
+ name_id_offset += 1;
+ break;
+ case NETWORK_NODE_ID_COMPRESSION_16:
+ packet_min_size += 2;
+ name_id_offset += 2;
+ break;
+ case NETWORK_NODE_ID_COMPRESSION_32:
+ packet_min_size += 4;
+ name_id_offset += 4;
+ break;
+ default:
+ ERR_FAIL_MSG("Was not possible to extract the node id compression mode.");
+ }
+ switch (name_id_compression) {
+ case NETWORK_NAME_ID_COMPRESSION_8:
+ packet_min_size += 1;
+ break;
+ case NETWORK_NAME_ID_COMPRESSION_16:
+ packet_min_size += 2;
+ break;
+ default:
+ ERR_FAIL_MSG("Was not possible to extract the name id compression mode.");
+ }
+ ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
+ uint32_t node_target = 0;
+ switch (node_id_compression) {
+ case NETWORK_NODE_ID_COMPRESSION_8:
+ node_target = p_packet[1];
+ break;
+ case NETWORK_NODE_ID_COMPRESSION_16:
+ node_target = decode_uint16(p_packet + 1);
+ break;
+ case NETWORK_NODE_ID_COMPRESSION_32:
+ node_target = decode_uint32(p_packet + 1);
+ break;
+ default:
+ // Unreachable, checked before.
+ CRASH_NOW();
+ }
+ Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len);
ERR_FAIL_COND_MSG(node == NULL, "Invalid packet received. Requested node was not found.");
- // Detect cstring end.
- int len_end = 5;
- for (; len_end < p_packet_len; len_end++) {
- if (p_packet[len_end] == 0) {
+ uint16_t name_id = 0;
+ switch (name_id_compression) {
+ case NETWORK_NAME_ID_COMPRESSION_8:
+ name_id = p_packet[name_id_offset];
break;
- }
+ case NETWORK_NAME_ID_COMPRESSION_16:
+ name_id = decode_uint16(p_packet + name_id_offset);
+ break;
+ default:
+ // Unreachable, checked before.
+ CRASH_NOW();
}
- ERR_FAIL_COND_MSG(len_end >= p_packet_len, "Invalid packet received. Size too small.");
-
- StringName name = String::utf8((const char *)&p_packet[5]);
-
if (packet_type == NETWORK_COMMAND_REMOTE_CALL) {
- _process_rpc(node, name, p_from, p_packet, p_packet_len, len_end + 1);
+ _process_rpc(node, name_id, p_from, p_packet, p_packet_len, packet_min_size);
} else {
- _process_rset(node, name, p_from, p_packet, p_packet_len, len_end + 1);
+ _process_rset(node, name_id, p_from, p_packet, p_packet_len, packet_min_size);
}
} break;
@@ -233,15 +284,14 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
}
}
-Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int p_packet_len) {
+Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) {
- uint32_t target = decode_uint32(&p_packet[1]);
Node *node = NULL;
- if (target & 0x80000000) {
+ if (p_node_target & 0x80000000) {
// Use full path (not cached yet).
- int ofs = target & 0x7FFFFFFF;
+ int ofs = p_node_target & 0x7FFFFFFF;
ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, NULL, "Invalid packet received. Size smaller than declared.");
@@ -253,10 +303,10 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int
node = root_node->get_node(np);
if (!node)
- ERR_PRINTS("Failed to get path from RPC: " + String(np) + ".");
+ ERR_PRINT("Failed to get path from RPC: " + String(np) + ".");
} else {
// Use cached path.
- int id = target;
+ int id = p_node_target;
Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from);
ERR_FAIL_COND_V_MSG(!E, NULL, "Invalid packet received. Requests invalid peer cache.");
@@ -269,26 +319,26 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int
node = root_node->get_node(ni->path);
if (!node)
- ERR_PRINTS("Failed to get cached path from RPC: " + String(ni->path) + ".");
+ ERR_PRINT("Failed to get cached path from RPC: " + String(ni->path) + ".");
}
return node;
}
-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) {
+void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
// 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);
+ StringName name = p_node->get_node_rpc_method(p_rpc_method_id);
+ RPCMode rpc_mode = p_node->get_node_rpc_mode_by_id(p_rpc_method_id);
+ if (name == StringName() && p_node->get_script_instance()) {
+ name = p_node->get_script_instance()->get_rpc_method(p_rpc_method_id);
+ rpc_mode = p_node->get_script_instance()->get_rpc_mode_by_id(p_rpc_method_id);
}
+ ERR_FAIL_COND(name == StringName());
bool can_call = _can_call_mode(p_node, rpc_mode, p_from);
- ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
+ ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
int argc = p_packet[p_offset];
Vector<Variant> args;
@@ -311,7 +361,7 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_
ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
int vlen;
- Error err = decode_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen, allow_object_decoding || network_peer->is_object_decoding_allowed());
+ Error err = _decode_and_decompress_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen);
ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument.");
argp.write[i] = &args[i];
@@ -320,29 +370,29 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_
Variant::CallError ce;
- p_node->call(p_name, (const Variant **)argp.ptr(), argc, ce);
+ p_node->call(name, (const Variant **)argp.ptr(), argc, ce);
if (ce.error != Variant::CallError::CALL_OK) {
- String error = Variant::get_call_error_text(p_node, p_name, (const Variant **)argp.ptr(), argc, ce);
+ String error = Variant::get_call_error_text(p_node, name, (const Variant **)argp.ptr(), argc, ce);
error = "RPC - " + error;
- ERR_PRINTS(error);
+ ERR_PRINT(error);
}
}
-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) {
+void MultiplayerAPI::_process_rset(Node *p_node, const uint16_t p_rpc_property_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
// 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);
+ StringName name = p_node->get_node_rset_property(p_rpc_property_id);
+ RPCMode rset_mode = p_node->get_node_rset_mode_by_id(p_rpc_property_id);
+ if (name == StringName() && p_node->get_script_instance()) {
+ name = p_node->get_script_instance()->get_rset_property(p_rpc_property_id);
+ rset_mode = p_node->get_script_instance()->get_rset_mode_by_id(p_rpc_property_id);
}
+ ERR_FAIL_COND(name == StringName());
bool can_call = _can_call_mode(p_node, rset_mode, p_from);
- ERR_FAIL_COND_MSG(!can_call, "RSET '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
+ ERR_FAIL_COND_MSG(!can_call, "RSET '" + String(name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
#ifdef DEBUG_ENABLED
if (profiling) {
@@ -353,26 +403,33 @@ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p
#endif
Variant value;
- Error err = decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset, NULL, allow_object_decoding || network_peer->is_object_decoding_allowed());
+ Error err = _decode_and_decompress_variant(value, &p_packet[p_offset], p_packet_len - p_offset, NULL);
ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RSET value.");
bool valid;
- p_node->set(p_name, value, &valid);
+ p_node->set(name, value, &valid);
if (!valid) {
- String error = "Error setting remote property '" + String(p_name) + "', not found in object of type " + p_node->get_class() + ".";
- ERR_PRINTS(error);
+ String error = "Error setting remote property '" + String(name) + "', not found in object of type " + p_node->get_class() + ".";
+ ERR_PRINT(error);
}
}
void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
- ERR_FAIL_COND_MSG(p_packet_len < 5, "Invalid packet received. Size too small.");
- int id = decode_uint32(&p_packet[1]);
+ ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small.");
+ int ofs = 1;
+
+ String methods_md5;
+ methods_md5.parse_utf8((const char *)(p_packet + ofs), 32);
+ ofs += 33;
+
+ int id = decode_uint32(&p_packet[ofs]);
+ ofs += 4;
String paths;
- paths.parse_utf8((const char *)&p_packet[5], p_packet_len - 5);
+ paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs);
NodePath path = paths;
@@ -380,9 +437,15 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet,
path_get_cache[p_from] = PathGetCache();
}
+ Node *node = root_node->get_node(path);
+ ERR_FAIL_COND(node == NULL);
+ const bool valid_rpc_checksum = node->get_rpc_md5() == methods_md5;
+ if (valid_rpc_checksum == false) {
+ ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
+ }
+
PathGetCache::NodeInfo ni;
ni.path = path;
- ni.instance = 0;
path_get_cache[p_from].nodes[id] = ni;
@@ -392,9 +455,10 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet,
Vector<uint8_t> packet;
- packet.resize(1 + len);
+ packet.resize(1 + 1 + len);
packet.write[0] = NETWORK_COMMAND_CONFIRM_PATH;
- encode_cstring(pname.get_data(), &packet.write[1]);
+ packet.write[1] = valid_rpc_checksum;
+ encode_cstring(pname.get_data(), &packet.write[2]);
network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
network_peer->set_target_peer(p_from);
@@ -403,13 +467,19 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet,
void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
- ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small.");
+ ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small.");
+
+ const bool valid_rpc_checksum = p_packet[1];
String paths;
- paths.parse_utf8((const char *)&p_packet[1], p_packet_len - 1);
+ paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2);
NodePath path = paths;
+ if (valid_rpc_checksum == false) {
+ ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
+ }
+
PathSentCache *psc = path_send_cache.getptr(path);
ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache.");
@@ -418,7 +488,7 @@ void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet,
E->get() = true;
}
-bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int p_target) {
+bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) {
bool has_all_peers = true;
List<int> peers_to_add; // If one is missing, take note to add it.
@@ -443,31 +513,192 @@ bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int
}
}
- // Those that need to be added, send a message for this.
+ if (peers_to_add.size() > 0) {
- for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) {
+ // Those that need to be added, send a message for this.
// Encode function name.
- CharString pname = String(p_path).utf8();
- int len = encode_cstring(pname.get_data(), NULL);
+ const CharString path = String(p_path).utf8();
+ const int path_len = encode_cstring(path.get_data(), NULL);
+
+ // Extract MD5 from rpc methods list.
+ const String methods_md5 = p_node->get_rpc_md5();
+ const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder.
Vector<uint8_t> packet;
+ packet.resize(1 + 4 + path_len + methods_md5_len);
+ int ofs = 0;
+
+ packet.write[ofs] = NETWORK_COMMAND_SIMPLIFY_PATH;
+ ofs += 1;
+
+ ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]);
+
+ ofs += encode_uint32(psc->id, &packet.write[ofs]);
+
+ ofs += encode_cstring(path.get_data(), &packet.write[ofs]);
- packet.resize(1 + 4 + len);
- packet.write[0] = NETWORK_COMMAND_SIMPLIFY_PATH;
- encode_uint32(psc->id, &packet.write[1]);
- encode_cstring(pname.get_data(), &packet.write[5]);
+ for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) {
- network_peer->set_target_peer(E->get()); // To all of you.
- network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
- network_peer->put_packet(packet.ptr(), packet.size());
+ network_peer->set_target_peer(E->get()); // To all of you.
+ network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
+ network_peer->put_packet(packet.ptr(), packet.size());
- psc->confirmed_peers.insert(E->get(), false); // Insert into confirmed, but as false since it was not confirmed.
+ psc->confirmed_peers.insert(E->get(), false); // Insert into confirmed, but as false since it was not confirmed.
+ }
}
return has_all_peers;
}
+// The variant is compressed and encoded; The first byte contains all the meta
+// information and the format is:
+// - The first LSB 5 bits are used for the variant type.
+// - The next two bits are used to store the encoding mode.
+// - The most significant is used to store the boolean value.
+#define VARIANT_META_TYPE_MASK 0x1F
+#define VARIANT_META_EMODE_MASK 0x60
+#define VARIANT_META_BOOL_MASK 0x80
+#define ENCODE_8 0 << 5
+#define ENCODE_16 1 << 5
+#define ENCODE_32 2 << 5
+#define ENCODE_64 3 << 5
+Error MultiplayerAPI::_encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) {
+
+ // Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31
+ CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK);
+
+ uint8_t *buf = r_buffer;
+ r_len = 0;
+ uint8_t encode_mode = 0;
+
+ switch (p_variant.get_type()) {
+ case Variant::BOOL: {
+ if (buf) {
+ // We still have 1 free bit in the meta, so let's use it.
+ buf[0] = (p_variant.operator bool()) ? (1 << 7) : 0;
+ buf[0] |= encode_mode | p_variant.get_type();
+ }
+ r_len += 1;
+ } break;
+ case Variant::INT: {
+ if (buf) {
+ // Reserve the first byte for the meta.
+ buf += 1;
+ }
+ r_len += 1;
+ int64_t val = p_variant;
+ if (val <= (int64_t)INT8_MAX && val >= (int64_t)INT8_MIN) {
+ // Use 8 bit
+ encode_mode = ENCODE_8;
+ if (buf) {
+ buf[0] = val;
+ }
+ r_len += 1;
+ } else if (val <= (int64_t)INT16_MAX && val >= (int64_t)INT16_MIN) {
+ // Use 16 bit
+ encode_mode = ENCODE_16;
+ if (buf) {
+ encode_uint16(val, buf);
+ }
+ r_len += 2;
+ } else if (val <= (int64_t)INT32_MAX && val >= (int64_t)INT32_MIN) {
+ // Use 32 bit
+ encode_mode = ENCODE_32;
+ if (buf) {
+ encode_uint32(val, buf);
+ }
+ r_len += 4;
+ } else {
+ // Use 64 bit
+ encode_mode = ENCODE_64;
+ if (buf) {
+ encode_uint64(val, buf);
+ }
+ r_len += 8;
+ }
+ // Store the meta
+ if (buf) {
+ buf -= 1;
+ buf[0] = encode_mode | p_variant.get_type();
+ }
+ } break;
+ default:
+ // Any other case is not yet compressed.
+ Error err = encode_variant(p_variant, r_buffer, r_len, allow_object_decoding);
+ if (err != OK)
+ return err;
+ if (r_buffer) {
+ // The first byte is not used by the marshaling, so store the type
+ // so we know how to decompress and decode this variant.
+ r_buffer[0] = p_variant.get_type();
+ }
+ }
+
+ return OK;
+}
+Error MultiplayerAPI::_decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) {
+
+ const uint8_t *buf = p_buffer;
+ int len = p_len;
+
+ ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
+ uint8_t type = buf[0] & VARIANT_META_TYPE_MASK;
+ uint8_t encode_mode = buf[0] & VARIANT_META_EMODE_MASK;
+
+ ERR_FAIL_COND_V(type >= Variant::VARIANT_MAX, ERR_INVALID_DATA);
+
+ switch (type) {
+ case Variant::BOOL: {
+ bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0;
+ r_variant = val;
+ if (r_len)
+ *r_len = 1;
+ } break;
+ case Variant::INT: {
+ buf += 1;
+ len -= 1;
+ if (r_len)
+ *r_len = 1;
+ if (encode_mode == ENCODE_8) {
+ // 8 bits.
+ ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
+ int8_t val = buf[0];
+ r_variant = val;
+ if (r_len)
+ (*r_len) += 1;
+ } else if (encode_mode == ENCODE_16) {
+ // 16 bits.
+ ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA);
+ int16_t val = decode_uint16(buf);
+ r_variant = val;
+ if (r_len)
+ (*r_len) += 2;
+ } else if (encode_mode == ENCODE_32) {
+ // 32 bits.
+ ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+ int32_t val = decode_uint32(buf);
+ r_variant = val;
+ if (r_len)
+ (*r_len) += 4;
+ } else {
+ // 64 bits.
+ ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
+ int64_t val = decode_uint64(buf);
+ r_variant = val;
+ if (r_len)
+ (*r_len) += 8;
+ }
+ } break;
+ default:
+ Error err = decode_variant(r_variant, p_buffer, p_len, r_len, allow_object_decoding);
+ if (err != OK)
+ return err;
+ }
+
+ return OK;
+}
+
void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount) {
ERR_FAIL_COND_MSG(network_peer.is_null(), "Attempt to remote call/set when networking is not active in SceneTree.");
@@ -496,6 +727,9 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
psc->id = last_send_cache_id++;
}
+ // See if all peers have cached path (if so, call can be fast).
+ const bool has_all_peers = _send_confirm_path(p_from, from_path, psc, p_to);
+
// Create base packet, lots of hardcode because it must be tight.
int ofs = 0;
@@ -503,45 +737,125 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
#define MAKE_ROOM(m_amount) \
if (packet_cache.size() < m_amount) packet_cache.resize(m_amount);
- // Encode type.
+ // Encode meta.
+ // The meta is composed by a single byte that contains (starting from the least segnificant bit):
+ // - `NetworkCommands` in the first three bits.
+ // - `NetworkNodeIdCompression` in the next 2 bits.
+ // - `NetworkNameIdCompression` in the next 1 bit.
+ // - So we still have the last two bits free!
+ uint8_t command_type = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL;
+ uint8_t node_id_compression = UINT8_MAX;
+ uint8_t name_id_compression = UINT8_MAX;
+
MAKE_ROOM(1);
- packet_cache.write[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL;
+ // The meta is composed along the way, so just set 0 for now.
+ packet_cache.write[0] = 0;
ofs += 1;
- // Encode ID.
- MAKE_ROOM(ofs + 4);
- 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.write[ofs]));
- ofs += len;
+ // Encode Node ID.
+ if (has_all_peers) {
+ // Compress the node ID only if all the target peers already know it.
+ if (psc->id >= 0 && psc->id <= 255) {
+ // We can encode the id in 1 byte
+ node_id_compression = NETWORK_NODE_ID_COMPRESSION_8;
+ MAKE_ROOM(ofs + 1);
+ packet_cache.write[ofs] = static_cast<uint8_t>(psc->id);
+ ofs += 1;
+ } else if (psc->id >= 0 && psc->id <= 65535) {
+ // We can encode the id in 2 bytes
+ node_id_compression = NETWORK_NODE_ID_COMPRESSION_16;
+ MAKE_ROOM(ofs + 2);
+ encode_uint16(static_cast<uint16_t>(psc->id), &(packet_cache.write[ofs]));
+ ofs += 2;
+ } else {
+ // Too big, let's use 4 bytes.
+ node_id_compression = NETWORK_NODE_ID_COMPRESSION_32;
+ MAKE_ROOM(ofs + 4);
+ encode_uint32(psc->id, &(packet_cache.write[ofs]));
+ ofs += 4;
+ }
+ } else {
+ // The targets doesn't know the node yet, so we need to use 32 bits int.
+ node_id_compression = NETWORK_NODE_ID_COMPRESSION_32;
+ MAKE_ROOM(ofs + 4);
+ encode_uint32(psc->id, &(packet_cache.write[ofs]));
+ ofs += 4;
+ }
if (p_set) {
+
+ // Take the rpc property ID
+ uint16_t property_id = p_from->get_node_rset_property_id(p_name);
+ if (property_id == UINT16_MAX && p_from->get_script_instance()) {
+ property_id = p_from->get_script_instance()->get_rset_property_id(p_name);
+ }
+ ERR_FAIL_COND_MSG(property_id == UINT16_MAX, "Unable to take the `property_id` for the property:" + p_name + ". this can happen only if this property is not marked as `remote`.");
+
+ if (property_id <= UINT8_MAX) {
+ // The ID fits in 1 byte
+ name_id_compression = NETWORK_NAME_ID_COMPRESSION_8;
+ MAKE_ROOM(ofs + 1);
+ packet_cache.write[ofs] = static_cast<uint8_t>(property_id);
+ ofs += 1;
+ } else {
+ // The ID is larger, let's use 2 bytes
+ name_id_compression = NETWORK_NAME_ID_COMPRESSION_16;
+ MAKE_ROOM(ofs + 2);
+ encode_uint16(property_id, &(packet_cache.write[ofs]));
+ ofs += 2;
+ }
+
// Set argument.
- Error err = encode_variant(*p_arg[0], NULL, len, allow_object_decoding || network_peer->is_object_decoding_allowed());
+ int len(0);
+ Error err = _encode_and_compress_variant(*p_arg[0], NULL, len);
ERR_FAIL_COND_MSG(err != OK, "Unable to encode RSET value. THIS IS LIKELY A BUG IN THE ENGINE!");
MAKE_ROOM(ofs + len);
- encode_variant(*p_arg[0], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed());
+ _encode_and_compress_variant(*p_arg[0], &(packet_cache.write[ofs]), len);
ofs += len;
} else {
+ // Take the rpc method ID
+ uint16_t method_id = p_from->get_node_rpc_method_id(p_name);
+ if (method_id == UINT16_MAX && p_from->get_script_instance()) {
+ method_id = p_from->get_script_instance()->get_rpc_method_id(p_name);
+ }
+ ERR_FAIL_COND_MSG(method_id == UINT16_MAX, "Unable to take the `method_id` for the function:" + p_name + ". this can happen only if this method is not marked as `remote`.");
+
+ if (method_id <= UINT8_MAX) {
+ // The ID fits in 1 byte
+ name_id_compression = NETWORK_NAME_ID_COMPRESSION_8;
+ MAKE_ROOM(ofs + 1);
+ packet_cache.write[ofs] = static_cast<uint8_t>(method_id);
+ ofs += 1;
+ } else {
+ // The ID is larger, let's use 2 bytes
+ name_id_compression = NETWORK_NAME_ID_COMPRESSION_16;
+ MAKE_ROOM(ofs + 2);
+ encode_uint16(method_id, &(packet_cache.write[ofs]));
+ ofs += 2;
+ }
+
// Call arguments.
MAKE_ROOM(ofs + 1);
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, allow_object_decoding || network_peer->is_object_decoding_allowed());
+ int len(0);
+ Error err = _encode_and_compress_variant(*p_arg[i], NULL, len);
ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!");
MAKE_ROOM(ofs + len);
- encode_variant(*p_arg[i], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed());
+ _encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len);
ofs += len;
}
}
+ ERR_FAIL_COND(command_type > 7);
+ ERR_FAIL_COND(node_id_compression > 3);
+ ERR_FAIL_COND(name_id_compression > 1);
+
+ // We can now set the meta
+ packet_cache.write[0] = command_type + (node_id_compression << 3) + (name_id_compression << 5);
+
#ifdef DEBUG_ENABLED
if (profiling) {
bandwidth_outgoing_data.write[bandwidth_outgoing_pointer].timestamp = OS::get_singleton()->get_ticks_msec();
@@ -550,9 +864,6 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
}
#endif
- // See if all peers have cached path (is so, call can be fast).
- bool has_all_peers = _send_confirm_path(from_path, psc, p_to);
-
// Take chance and set transfer mode, since all send methods will use it.
network_peer->set_transfer_mode(p_unreliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE : NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
@@ -562,6 +873,9 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
network_peer->set_target_peer(p_to); // To all of you.
network_peer->put_packet(packet_cache.ptr(), ofs); // A message with love.
} else {
+ // Unreachable because the node ID is never compressed if the peers doesn't know it.
+ CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32);
+
// Not all verified path, so send one by one.
// Append path at the end, since we will need it for some packets.
@@ -647,16 +961,14 @@ 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, RPCMode>::Element *E = p_node->get_node_rpc_mode(p_method);
- if (E) {
- call_local_native = _should_call_local(E->get(), is_master, skip_rpc);
- }
+ RPCMode rpc_mode = p_node->get_node_rpc_mode(p_method);
+ call_local_native = _should_call_local(rpc_mode, is_master, skip_rpc);
if (call_local_native) {
// Done below.
} else if (p_node->get_script_instance()) {
// Attempt with script.
- RPCMode rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method);
+ rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method);
call_local_script = _should_call_local(rpc_mode, is_master, skip_rpc);
}
}
@@ -683,7 +995,7 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const
if (ce.error != Variant::CallError::CALL_OK) {
String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
error = "rpc() aborted in local call: - " + error + ".";
- ERR_PRINTS(error);
+ ERR_PRINT(error);
return;
}
}
@@ -698,7 +1010,7 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const
if (ce.error != Variant::CallError::CALL_OK) {
String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
error = "rpc() aborted in script local call: - " + error + ".";
- ERR_PRINTS(error);
+ ERR_PRINT(error);
return;
}
}
@@ -719,11 +1031,8 @@ void MultiplayerAPI::rsetp(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, RPCMode>::Element *E = p_node->get_node_rset_mode(p_property);
- if (E) {
-
- set_local = _should_call_local(E->get(), is_master, skip_rset);
- }
+ RPCMode rpc_mode = p_node->get_node_rset_mode(p_property);
+ set_local = _should_call_local(rpc_mode, is_master, skip_rset);
if (set_local) {
bool valid;
@@ -735,12 +1044,12 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
if (!valid) {
String error = "rset() aborted in local set, property not found: - " + String(p_property) + ".";
- ERR_PRINTS(error);
+ ERR_PRINT(error);
return;
}
} else if (p_node->get_script_instance()) {
// Attempt with script.
- RPCMode rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property);
+ rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property);
set_local = _should_call_local(rpc_mode, is_master, skip_rset);
@@ -753,7 +1062,7 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
if (!valid) {
String error = "rset() aborted in local script set, property not found: - " + String(p_property) + ".";
- ERR_PRINTS(error);
+ ERR_PRINT(error);
return;
}
}
@@ -984,9 +1293,7 @@ void MultiplayerAPI::_bind_methods() {
BIND_ENUM_CONSTANT(RPC_MODE_REMOTE);
BIND_ENUM_CONSTANT(RPC_MODE_MASTER);
BIND_ENUM_CONSTANT(RPC_MODE_PUPPET);
- BIND_ENUM_CONSTANT(RPC_MODE_SLAVE); // Deprecated.
BIND_ENUM_CONSTANT(RPC_MODE_REMOTESYNC);
- BIND_ENUM_CONSTANT(RPC_MODE_SYNC); // Deprecated.
BIND_ENUM_CONSTANT(RPC_MODE_MASTERSYNC);
BIND_ENUM_CONSTANT(RPC_MODE_PUPPETSYNC);
}
diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h
index c9f127b6b2..8748dba03c 100644
--- a/core/io/multiplayer_api.h
+++ b/core/io/multiplayer_api.h
@@ -98,32 +98,44 @@ protected:
void _process_packet(int p_from, const uint8_t *p_packet, int p_packet_len);
void _process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len);
void _process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len);
- Node *_process_get_node(int p_from, const uint8_t *p_packet, int p_packet_len);
- void _process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
- void _process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
+ Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len);
+ void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
+ void _process_rset(Node *p_node, const uint16_t p_rpc_property_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len);
void _send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount);
- bool _send_confirm_path(NodePath p_path, PathSentCache *psc, int p_target);
+ bool _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target);
+
+ Error _encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len);
+ Error _decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len);
public:
enum NetworkCommands {
- NETWORK_COMMAND_REMOTE_CALL,
+ NETWORK_COMMAND_REMOTE_CALL = 0,
NETWORK_COMMAND_REMOTE_SET,
NETWORK_COMMAND_SIMPLIFY_PATH,
NETWORK_COMMAND_CONFIRM_PATH,
NETWORK_COMMAND_RAW,
};
+ enum NetworkNodeIdCompression {
+ NETWORK_NODE_ID_COMPRESSION_8 = 0,
+ NETWORK_NODE_ID_COMPRESSION_16,
+ NETWORK_NODE_ID_COMPRESSION_32,
+ };
+
+ enum NetworkNameIdCompression {
+ NETWORK_NAME_ID_COMPRESSION_8 = 0,
+ NETWORK_NAME_ID_COMPRESSION_16,
+ };
+
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_MASTER, // Using rpc() on it will call method on wherever the master is, be it local or remote
RPC_MODE_PUPPET, // Using rpc() on it will call method for all puppets
- RPC_MODE_SLAVE = RPC_MODE_PUPPET, // Deprecated, same as puppet
RPC_MODE_REMOTESYNC, // Using rpc() on it will call method / set property in all remote peers and locally
- RPC_MODE_SYNC = RPC_MODE_REMOTESYNC, // Deprecated. Same as RPC_MODE_REMOTESYNC
RPC_MODE_MASTERSYNC, // Using rpc() on it will call method / set property in the master peer and locally
RPC_MODE_PUPPETSYNC, // Using rpc() on it will call method / set property in all puppets peers and locally
};
diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp
index 9e53d773ba..81d7b86161 100644
--- a/core/io/packet_peer.cpp
+++ b/core/io/packet_peer.cpp
@@ -37,20 +37,9 @@
PacketPeer::PacketPeer() :
last_get_error(OK),
- allow_object_decoding(false),
encode_buffer_max_size(8 * 1024 * 1024) {
}
-void PacketPeer::set_allow_object_decoding(bool p_enable) {
-
- allow_object_decoding = p_enable;
-}
-
-bool PacketPeer::is_object_decoding_allowed() const {
-
- return allow_object_decoding;
-}
-
void PacketPeer::set_encode_buffer_max_size(int p_max_size) {
ERR_FAIL_COND_MSG(p_max_size < 1024, "Max encode buffer must be at least 1024 bytes");
@@ -101,13 +90,13 @@ Error PacketPeer::get_var(Variant &r_variant, bool p_allow_objects) {
if (err)
return err;
- return decode_variant(r_variant, buffer, buffer_size, NULL, p_allow_objects || allow_object_decoding);
+ return decode_variant(r_variant, buffer, buffer_size, NULL, p_allow_objects);
}
Error PacketPeer::put_var(const Variant &p_packet, bool p_full_objects) {
int len;
- Error err = encode_variant(p_packet, NULL, len, p_full_objects || allow_object_decoding); // compute len first
+ Error err = encode_variant(p_packet, NULL, len, p_full_objects); // compute len first
if (err)
return err;
@@ -122,7 +111,7 @@ Error PacketPeer::put_var(const Variant &p_packet, bool p_full_objects) {
}
PoolVector<uint8_t>::Write w = encode_buffer.write();
- err = encode_variant(p_packet, w.ptr(), len, p_full_objects || allow_object_decoding);
+ err = encode_variant(p_packet, w.ptr(), len, p_full_objects);
ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to encode Variant.");
return put_packet(w.ptr(), len);
@@ -160,13 +149,10 @@ void PacketPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_packet_error"), &PacketPeer::_get_packet_error);
ClassDB::bind_method(D_METHOD("get_available_packet_count"), &PacketPeer::get_available_packet_count);
- ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &PacketPeer::set_allow_object_decoding);
- ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &PacketPeer::is_object_decoding_allowed);
ClassDB::bind_method(D_METHOD("get_encode_buffer_max_size"), &PacketPeer::get_encode_buffer_max_size);
ClassDB::bind_method(D_METHOD("set_encode_buffer_max_size", "max_size"), &PacketPeer::set_encode_buffer_max_size);
ADD_PROPERTY(PropertyInfo(Variant::INT, "encode_buffer_max_size"), "set_encode_buffer_max_size", "get_encode_buffer_max_size");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed");
};
/***************/
diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h
index 2b13f2e952..bee69f5ca9 100644
--- a/core/io/packet_peer.h
+++ b/core/io/packet_peer.h
@@ -49,8 +49,6 @@ class PacketPeer : public Reference {
mutable Error last_get_error;
- bool allow_object_decoding;
-
int encode_buffer_max_size;
PoolVector<uint8_t> encode_buffer;
@@ -69,9 +67,6 @@ public:
virtual Error get_var(Variant &r_variant, bool p_allow_objects = false);
virtual Error put_var(const Variant &p_packet, bool p_full_objects = false);
- void set_allow_object_decoding(bool p_enable);
- bool is_object_decoding_allowed() const;
-
void set_encode_buffer_max_size(int p_max_size);
int get_encode_buffer_max_size() const;
diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp
index 5c98c4fcab..a8cfd741bb 100644
--- a/core/io/packet_peer_udp.cpp
+++ b/core/io/packet_peer_udp.cpp
@@ -222,7 +222,7 @@ Error PacketPeerUDP::_poll() {
if (rb.space_left() < read + 24) {
#ifdef TOOLS_ENABLED
- WARN_PRINTS("Buffer full, dropping packets!");
+ WARN_PRINT("Buffer full, dropping packets!");
#endif
continue;
}
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 272d5c1116..02ae5788fc 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -73,13 +73,6 @@ enum {
VARIANT_VECTOR2_ARRAY = 37,
VARIANT_INT64 = 40,
VARIANT_DOUBLE = 41,
-#ifndef DISABLE_DEPRECATED
- VARIANT_IMAGE = 21, // - no longer variant type
- IMAGE_ENCODING_EMPTY = 0,
- IMAGE_ENCODING_RAW = 1,
- IMAGE_ENCODING_LOSSLESS = 2,
- IMAGE_ENCODING_LOSSY = 3,
-#endif
OBJECT_EMPTY = 0,
OBJECT_EXTERNAL_RESOURCE = 1,
OBJECT_INTERNAL_RESOURCE = 2,
@@ -549,69 +542,6 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
w.release();
r_v = array;
} break;
-#ifndef DISABLE_DEPRECATED
- case VARIANT_IMAGE: {
- uint32_t encoding = f->get_32();
- if (encoding == IMAGE_ENCODING_EMPTY) {
- r_v = Ref<Image>();
- break;
- } else if (encoding == IMAGE_ENCODING_RAW) {
- uint32_t width = f->get_32();
- uint32_t height = f->get_32();
- uint32_t mipmaps = f->get_32();
- uint32_t format = f->get_32();
- const uint32_t format_version_shift = 24;
- const uint32_t format_version_mask = format_version_shift - 1;
-
- uint32_t format_version = format >> format_version_shift;
-
- const uint32_t current_version = 0;
- if (format_version > current_version) {
-
- ERR_PRINT("Format version for encoded binary image is too new.");
- return ERR_PARSE_ERROR;
- }
-
- Image::Format fmt = Image::Format(format & format_version_mask); //if format changes, we can add a compatibility bit on top
-
- uint32_t datalen = f->get_32();
-
- PoolVector<uint8_t> imgdata;
- imgdata.resize(datalen);
- PoolVector<uint8_t>::Write w = imgdata.write();
- f->get_buffer(w.ptr(), datalen);
- _advance_padding(datalen);
- w.release();
-
- Ref<Image> image;
- image.instance();
- image->create(width, height, mipmaps, fmt, imgdata);
- r_v = image;
-
- } else {
- //compressed
- PoolVector<uint8_t> data;
- data.resize(f->get_32());
- PoolVector<uint8_t>::Write w = data.write();
- f->get_buffer(w.ptr(), data.size());
- w.release();
-
- Ref<Image> image;
-
- if (encoding == IMAGE_ENCODING_LOSSY && Image::lossy_unpacker) {
-
- image = Image::lossy_unpacker(data);
- } else if (encoding == IMAGE_ENCODING_LOSSLESS && Image::lossless_unpacker) {
-
- image = Image::lossless_unpacker(data);
- }
- _advance_padding(data.size());
-
- r_v = image;
- }
-
- } break;
-#endif
default: {
ERR_FAIL_V(ERR_FILE_CORRUPT);
} break;
@@ -836,15 +766,20 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) {
uint8_t header[4];
f->get_buffer(header, 4);
if (header[0] == 'R' && header[1] == 'S' && header[2] == 'C' && header[3] == 'C') {
- //compressed
+ // Compressed.
FileAccessCompressed *fac = memnew(FileAccessCompressed);
- fac->open_after_magic(f);
+ error = fac->open_after_magic(f);
+ if (error != OK) {
+ memdelete(fac);
+ f->close();
+ ERR_FAIL_MSG("Failed to open binary resource file: " + local_path + ".");
+ }
f = fac;
} else if (header[0] != 'R' || header[1] != 'S' || header[2] != 'R' || header[3] != 'C') {
- //not normal
-
+ // Not normal.
error = ERR_FILE_UNRECOGNIZED;
+ f->close();
ERR_FAIL_MSG("Unrecognized binary resource file: " + local_path + ".");
}
@@ -919,6 +854,7 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) {
if (f->eof_reached()) {
error = ERR_FILE_CORRUPT;
+ f->close();
ERR_FAIL_MSG("Premature end of file (EOF): " + local_path + ".");
}
}
@@ -931,14 +867,20 @@ String ResourceInteractiveLoaderBinary::recognize(FileAccess *p_f) {
uint8_t header[4];
f->get_buffer(header, 4);
if (header[0] == 'R' && header[1] == 'S' && header[2] == 'C' && header[3] == 'C') {
- //compressed
+ // Compressed.
FileAccessCompressed *fac = memnew(FileAccessCompressed);
- fac->open_after_magic(f);
+ error = fac->open_after_magic(f);
+ if (error != OK) {
+ memdelete(fac);
+ f->close();
+ return "";
+ }
f = fac;
} else if (header[0] != 'R' || header[1] != 'S' || header[2] != 'R' || header[3] != 'C') {
- //not normal
+ // Not normal.
error = ERR_FILE_UNRECOGNIZED;
+ f->close();
return "";
}
@@ -1055,14 +997,19 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
uint8_t header[4];
f->get_buffer(header, 4);
if (header[0] == 'R' && header[1] == 'S' && header[2] == 'C' && header[3] == 'C') {
- //compressed
+ // Compressed.
FileAccessCompressed *fac = memnew(FileAccessCompressed);
- fac->open_after_magic(f);
+ Error err = fac->open_after_magic(f);
+ if (err != OK) {
+ memdelete(fac);
+ memdelete(f);
+ ERR_FAIL_V_MSG(err, "Cannot open file '" + p_path + "'.");
+ }
f = fac;
FileAccessCompressed *facw = memnew(FileAccessCompressed);
facw->configure("RSCC");
- Error err = facw->_open(p_path + ".depren", FileAccess::WRITE);
+ err = facw->_open(p_path + ".depren", FileAccess::WRITE);
if (err) {
memdelete(fac);
memdelete(facw);
@@ -1072,9 +1019,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
fw = facw;
} else if (header[0] != 'R' || header[1] != 'S' || header[2] != 'R' || header[3] != 'C') {
- //not normal
-
- //error=ERR_FILE_UNRECOGNIZED;
+ // Not normal.
memdelete(f);
ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unrecognized binary resource file '" + local_path + "'.");
} else {
@@ -1113,7 +1058,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
memdelete(da);
//use the old approach
- WARN_PRINTS("This file is old, so it can't refactor dependencies, opening and resaving '" + p_path + "'.");
+ WARN_PRINT("This file is old, so it can't refactor dependencies, opening and resaving '" + p_path + "'.");
Error err;
f = FileAccess::open(p_path, FileAccess::READ, &err);
@@ -1255,7 +1200,7 @@ String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const
ria->res_path = ria->local_path;
//ria->set_local_path( Globals::get_singleton()->localize_path(p_path) );
String r = ria->recognize(f);
- return r;
+ return ClassDB::get_compatibility_remapped_class(r);
}
///////////////////////////////////////////////////////////
@@ -1628,14 +1573,14 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
switch (p_variant.get_type()) {
case Variant::OBJECT: {
- RES res = p_variant.operator RefPtr();
+ RES res = p_variant;
if (res.is_null() || external_resources.has(res))
return;
if (!p_main && (!bundle_resources) && res->get_path().length() && res->get_path().find("::") == -1) {
if (res->get_path() == path) {
- ERR_PRINTS("Circular reference to resource being saved found: '" + local_path + "' will be null next time it's loaded.");
+ ERR_PRINT("Circular reference to resource being saved found: '" + local_path + "' will be null next time it's loaded.");
return;
}
int idx = external_resources.size();
diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h
index 7737006d10..f02dbaa0c2 100644
--- a/core/io/resource_format_binary.h
+++ b/core/io/resource_format_binary.h
@@ -51,7 +51,6 @@ class ResourceInteractiveLoaderBinary : public ResourceInteractiveLoader {
Vector<char> str_buf;
List<RES> resource_cache;
- //Map<int,StringName> string_map;
Vector<StringName> string_map;
StringName _get_string();
diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp
index c2d90ed204..f147170ff7 100644
--- a/core/io/resource_importer.cpp
+++ b/core/io/resource_importer.cpp
@@ -74,7 +74,7 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy
memdelete(f);
return OK;
} else if (err != OK) {
- ERR_PRINTS("ResourceFormatImporter::load - " + p_path + ".import:" + itos(lines) + " error: " + error_text);
+ ERR_PRINT("ResourceFormatImporter::load - " + p_path + ".import:" + itos(lines) + " error: " + error_text);
memdelete(f);
return err;
}
@@ -279,7 +279,7 @@ void ResourceFormatImporter::get_internal_resource_path_list(const String &p_pat
memdelete(f);
return;
} else if (err != OK) {
- ERR_PRINTS("ResourceFormatImporter::get_internal_resource_path_list - " + p_path + ".import:" + itos(lines) + " error: " + error_text);
+ ERR_PRINT("ResourceFormatImporter::get_internal_resource_path_list - " + p_path + ".import:" + itos(lines) + " error: " + error_text);
memdelete(f);
return;
}
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 7471ab4241..6877f816e1 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -404,6 +404,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
if (!p_no_cache) {
_remove_from_loading_map(local_path);
}
+ print_verbose("Failed loading resource: " + path);
return RES();
}
if (!p_no_cache)
@@ -728,8 +729,9 @@ String ResourceLoader::get_resource_type(const String &p_path) {
for (int i = 0; i < loader_count; i++) {
String result = loader[i]->get_resource_type(local_path);
- if (result != "")
+ if (result != "") {
return result;
+ }
}
return "";
@@ -815,7 +817,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem
if (err == ERR_FILE_EOF) {
break;
} else if (err != OK) {
- ERR_PRINTS("Parse error: " + p_path + ".remap:" + itos(lines) + " error: " + error_text + ".");
+ ERR_PRINT("Parse error: " + p_path + ".remap:" + itos(lines) + " error: " + error_text + ".");
break;
}
@@ -953,7 +955,7 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) {
ERR_FAIL_COND_V_MSG(obj == NULL, false, "Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt) + ".");
ResourceFormatLoader *crl = Object::cast_to<ResourceFormatLoader>(obj);
- crl->set_script(s.get_ref_ptr());
+ crl->set_script(s);
ResourceLoader::add_resource_format_loader(crl);
return true;
@@ -1013,7 +1015,7 @@ void ResourceLoader::finalize() {
#ifndef NO_THREADS
const LoadingMapKey *K = NULL;
while ((K = loading_map.next(K))) {
- ERR_PRINTS("Exited while resource is being loaded: " + K->path);
+ ERR_PRINT("Exited while resource is being loaded: " + K->path);
}
loading_map.clear();
memdelete(loading_map_mutex);
diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp
index b468685e4d..685d21107f 100644
--- a/core/io/resource_saver.cpp
+++ b/core/io/resource_saver.cpp
@@ -221,7 +221,7 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) {
ERR_FAIL_COND_V_MSG(obj == NULL, false, "Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt) + ".");
ResourceFormatSaver *crl = Object::cast_to<ResourceFormatSaver>(obj);
- crl->set_script(s.get_ref_ptr());
+ crl->set_script(s);
ResourceSaver::add_resource_format_saver(crl);
return true;
diff --git a/core/list.h b/core/list.h
index 0796410a84..6250cec598 100644
--- a/core/list.h
+++ b/core/list.h
@@ -456,17 +456,12 @@ public:
Element *I = front();
int c = 0;
- while (I) {
-
- if (c == p_index) {
-
- return I->get();
- }
+ while (c < p_index) {
I = I->next();
c++;
}
- CRASH_NOW(); // bug!!
+ return I->get();
}
const T &operator[](int p_index) const {
@@ -475,17 +470,12 @@ public:
const Element *I = front();
int c = 0;
- while (I) {
-
- if (c == p_index) {
-
- return I->get();
- }
+ while (c < p_index) {
I = I->next();
c++;
}
- CRASH_NOW(); // bug!!
+ return I->get();
}
void move_to_back(Element *p_I) {
diff --git a/core/make_binders.py b/core/make_binders.py
index c38db5cef4..11cfbf6e79 100644
--- a/core/make_binders.py
+++ b/core/make_binders.py
@@ -342,7 +342,7 @@ def make_version(template, nargs, argmax, const, ret):
def run(target, source, env):
- versions = 13
+ versions = 15
versions_ext = 6
text = ""
text_ext = ""
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index ddf5f13d55..14079f811d 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -244,6 +244,18 @@ void Basis::scale_local(const Vector3 &p_scale) {
*this = scaled_local(p_scale);
}
+float Basis::get_uniform_scale() const {
+ return (elements[0].length() + elements[1].length() + elements[2].length()) / 3.0;
+}
+
+void Basis::make_scale_uniform() {
+ float l = (elements[0].length() + elements[1].length() + elements[2].length()) / 3.0;
+ for (int i = 0; i < 3; i++) {
+ elements[i].normalize();
+ elements[i] *= l;
+ }
+}
+
Basis Basis::scaled_local(const Vector3 &p_scale) const {
Basis b;
b.set_diagonal(p_scale);
diff --git a/core/math/basis.h b/core/math/basis.h
index 6c3a939d70..0261cf67c6 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -108,6 +108,9 @@ public:
void scale_local(const Vector3 &p_scale);
Basis scaled_local(const Vector3 &p_scale) const;
+ void make_scale_uniform();
+ float get_uniform_scale() const;
+
Vector3 get_scale() const;
Vector3 get_scale_abs() const;
Vector3 get_scale_local() const;
diff --git a/core/math/bsp_tree.cpp b/core/math/bsp_tree.cpp
index f155d626d7..7ad907db97 100644
--- a/core/math/bsp_tree.cpp
+++ b/core/math/bsp_tree.cpp
@@ -341,7 +341,7 @@ static int _bsp_create_node(const Face3 *p_faces, const Vector<int> &p_indices,
ERR_FAIL_COND_V(p_nodes.size() == BSP_Tree::MAX_NODES, -1);
// should not reach here
- ERR_FAIL_COND_V(p_indices.size() == 0, -1)
+ ERR_FAIL_COND_V(p_indices.size() == 0, -1);
int ic = p_indices.size();
const int *indices = p_indices.ptr();
diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp
index 380bae871a..c4981b954b 100644
--- a/core/math/camera_matrix.cpp
+++ b/core/math/camera_matrix.cpp
@@ -276,6 +276,36 @@ Vector2 CameraMatrix::get_viewport_half_extents() const {
return Vector2(res.x, res.y);
}
+void CameraMatrix::get_far_plane_size(real_t &r_width, real_t &r_height) const {
+
+ const real_t *matrix = (const real_t *)this->matrix;
+ ///////--- Far Plane ---///////
+ Plane far_plane = Plane(matrix[3] - matrix[2],
+ matrix[7] - matrix[6],
+ matrix[11] - matrix[10],
+ -matrix[15] + matrix[14]);
+ far_plane.normalize();
+
+ ///////--- Right Plane ---///////
+ Plane right_plane = Plane(matrix[3] - matrix[0],
+ matrix[7] - matrix[4],
+ matrix[11] - matrix[8],
+ -matrix[15] + matrix[12]);
+ right_plane.normalize();
+
+ Plane top_plane = Plane(matrix[3] - matrix[1],
+ matrix[7] - matrix[5],
+ matrix[11] - matrix[9],
+ -matrix[15] + matrix[13]);
+ top_plane.normalize();
+
+ Vector3 res;
+ far_plane.intersect_3(right_plane, top_plane, &res);
+
+ r_width = res.x;
+ r_height = res.y;
+}
+
bool CameraMatrix::get_endpoints(const Transform &p_transform, Vector3 *p_8points) const {
Vector<Plane> planes = get_projection_planes(Transform());
@@ -485,6 +515,12 @@ void CameraMatrix::invert() {
}
}
+void CameraMatrix::flip_y() {
+ for (int i = 0; i < 4; i++) {
+ matrix[1][i] = -matrix[1][i];
+ }
+}
+
CameraMatrix::CameraMatrix() {
set_identity();
@@ -506,6 +542,28 @@ CameraMatrix CameraMatrix::operator*(const CameraMatrix &p_matrix) const {
return new_matrix;
}
+void CameraMatrix::set_depth_correction(bool p_flip_y) {
+
+ real_t *m = &matrix[0][0];
+
+ m[0] = 1;
+ m[1] = 0.0;
+ m[2] = 0.0;
+ m[3] = 0.0;
+ m[4] = 0.0;
+ m[5] = p_flip_y ? -1 : 1;
+ m[6] = 0.0;
+ m[7] = 0.0;
+ m[8] = 0.0;
+ m[9] = 0.0;
+ m[10] = 0.5;
+ m[11] = 0.0;
+ m[12] = 0.0;
+ m[13] = 0.0;
+ m[14] = 0.5;
+ m[15] = 1.0;
+}
+
void CameraMatrix::set_light_bias() {
real_t *m = &matrix[0][0];
diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h
index 2eed6d25d6..60f7d15974 100644
--- a/core/math/camera_matrix.h
+++ b/core/math/camera_matrix.h
@@ -50,6 +50,7 @@ struct CameraMatrix {
void set_identity();
void set_zero();
void set_light_bias();
+ void set_depth_correction(bool p_flip_y = true);
void set_light_atlas_rect(const Rect2 &p_rect);
void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov = false);
void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist);
@@ -74,6 +75,7 @@ struct CameraMatrix {
bool get_endpoints(const Transform &p_transform, Vector3 *p_8points) const;
Vector2 get_viewport_half_extents() const;
+ void get_far_plane_size(real_t &r_width, real_t &r_height) const;
void invert();
CameraMatrix inverse() const;
@@ -90,6 +92,23 @@ struct CameraMatrix {
int get_pixels_per_meter(int p_for_pixel_width) const;
operator Transform() const;
+ void flip_y();
+
+ bool operator==(const CameraMatrix &p_cam) const {
+ for (uint32_t i = 0; i < 4; i++) {
+ for (uint32_t j = 0; j < 4; j++) {
+ if (matrix[i][j] != p_cam.matrix[i][j]) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ bool operator!=(const CameraMatrix &p_cam) const {
+ return !(*this == p_cam);
+ }
+
CameraMatrix();
CameraMatrix(const Transform &p_transform);
~CameraMatrix();
diff --git a/core/math/expression.cpp b/core/math/expression.cpp
index 655098376c..2fda7a27d5 100644
--- a/core/math/expression.cpp
+++ b/core/math/expression.cpp
@@ -64,7 +64,6 @@ const char *Expression::func_name[Expression::FUNC_MAX] = {
"is_nan",
"is_inf",
"ease",
- "decimals",
"step_decimals",
"stepify",
"lerp",
@@ -153,7 +152,6 @@ int Expression::get_func_argument_count(BuiltinFunc p_func) {
case MATH_EXP:
case MATH_ISNAN:
case MATH_ISINF:
- case MATH_DECIMALS:
case MATH_STEP_DECIMALS:
case MATH_SEED:
case MATH_RANDSEED:
@@ -376,11 +374,6 @@ void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant
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_STEP_DECIMALS: {
VALIDATE_ARG_NUM(0);
@@ -1036,8 +1029,7 @@ Error Expression::_get_token(Token &r_token) {
case 'f': res = 12; break;
case 'r': res = 13; break;
case 'u': {
- //hexnumbarh - oct is deprecated
-
+ // hex number
for (int j = 0; j < 4; j++) {
CharType c = GET_CHAR();
@@ -1062,7 +1054,7 @@ Error Expression::_get_token(Token &r_token) {
v = c - 'A';
v += 10;
} else {
- ERR_PRINT("BUG");
+ ERR_PRINT("Bug parsing hex constant.");
v = 0;
}
@@ -1071,13 +1063,8 @@ Error Expression::_get_token(Token &r_token) {
}
} break;
- //case '\"': res='\"'; break;
- //case '\\': res='\\'; break;
- //case '/': res='/'; break;
default: {
res = next;
- //r_err_str="Invalid escape sequence";
- //return ERR_PARSE_ERROR;
} break;
}
diff --git a/core/math/expression.h b/core/math/expression.h
index c5b9d79a16..1cd1415dcf 100644
--- a/core/math/expression.h
+++ b/core/math/expression.h
@@ -63,7 +63,6 @@ public:
MATH_ISNAN,
MATH_ISINF,
MATH_EASE,
- MATH_DECIMALS,
MATH_STEP_DECIMALS,
MATH_STEPIFY,
MATH_LERP,
diff --git a/core/math/rect2.h b/core/math/rect2.h
index 9017377770..0d2e7eb6e5 100644
--- a/core/math/rect2.h
+++ b/core/math/rect2.h
@@ -60,6 +60,19 @@ struct Rect2 {
return true;
}
+ inline bool intersects_touch(const Rect2 &p_rect) const {
+ if (position.x > (p_rect.position.x + p_rect.size.width))
+ return false;
+ if ((position.x + size.width) < p_rect.position.x)
+ return false;
+ if (position.y > (p_rect.position.y + p_rect.size.height))
+ return false;
+ if ((position.y + size.height) < p_rect.position.y)
+ return false;
+
+ return true;
+ }
+
inline real_t distance_to(const Vector2 &p_point) const {
real_t dist = 0.0;
diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp
index 71ff79c0fc..353b2acd16 100644
--- a/core/math/vector3.cpp
+++ b/core/math/vector3.cpp
@@ -103,7 +103,7 @@ Vector3 Vector3::cubic_interpolaten(const Vector3 &p_b, const Vector3 &p_pre_a,
Vector3 out;
out = 0.5 * ((p1 * 2.0) +
(-p0 + p2) * t +
- (2.0 * p0 - 5.0 * p1 + 4 * p2 - p3) * t2 +
+ (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2 +
(-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3);
return out;
}
@@ -122,7 +122,7 @@ Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, c
Vector3 out;
out = 0.5 * ((p1 * 2.0) +
(-p0 + p2) * t +
- (2.0 * p0 - 5.0 * p1 + 4 * p2 - p3) * t2 +
+ (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2 +
(-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3);
return out;
}
diff --git a/core/math/vector3.h b/core/math/vector3.h
index 4ad3017109..3bf8644af9 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -32,6 +32,7 @@
#define VECTOR3_H
#include "core/math/math_funcs.h"
+#include "core/math/vector3i.h"
#include "core/ustring.h"
class Basis;
@@ -147,6 +148,15 @@ struct Vector3 {
_FORCE_INLINE_ bool operator>=(const Vector3 &p_v) const;
operator String() const;
+ _FORCE_INLINE_ operator Vector3i() const {
+ return Vector3i(x, y, z);
+ }
+
+ _FORCE_INLINE_ Vector3(const Vector3i &p_ivec) {
+ x = p_ivec.x;
+ y = p_ivec.y;
+ z = p_ivec.z;
+ }
_FORCE_INLINE_ Vector3(real_t p_x, real_t p_y, real_t p_z) {
x = p_x;
diff --git a/core/math/vector3i.cpp b/core/math/vector3i.cpp
new file mode 100644
index 0000000000..8a4ddf03b9
--- /dev/null
+++ b/core/math/vector3i.cpp
@@ -0,0 +1,55 @@
+/*************************************************************************/
+/* vector3i.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "vector3i.h"
+
+void Vector3i::set_axis(int p_axis, int32_t p_value) {
+ ERR_FAIL_INDEX(p_axis, 3);
+ coord[p_axis] = p_value;
+}
+int32_t Vector3i::get_axis(int p_axis) const {
+
+ ERR_FAIL_INDEX_V(p_axis, 3, 0);
+ return operator[](p_axis);
+}
+
+int Vector3i::min_axis() const {
+
+ return x < y ? (x < z ? 0 : 2) : (y < z ? 1 : 2);
+}
+int Vector3i::max_axis() const {
+
+ return x < y ? (y < z ? 2 : 1) : (x < z ? 2 : 0);
+}
+
+Vector3i::operator String() const {
+
+ return (itos(x) + ", " + itos(y) + ", " + itos(z));
+}
diff --git a/core/math/vector3i.h b/core/math/vector3i.h
new file mode 100644
index 0000000000..6f9754d3b9
--- /dev/null
+++ b/core/math/vector3i.h
@@ -0,0 +1,272 @@
+/*************************************************************************/
+/* vector3i.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 VECTOR3I_H
+#define VECTOR3I_H
+
+#include "core/typedefs.h"
+#include "core/ustring.h"
+
+struct Vector3i {
+
+ enum Axis {
+ AXIS_X,
+ AXIS_Y,
+ AXIS_Z,
+ };
+
+ union {
+ struct {
+ int32_t x;
+ int32_t y;
+ int32_t z;
+ };
+
+ int32_t coord[3];
+ };
+
+ _FORCE_INLINE_ const int32_t &operator[](int p_axis) const {
+
+ return coord[p_axis];
+ }
+
+ _FORCE_INLINE_ int32_t &operator[](int p_axis) {
+
+ return coord[p_axis];
+ }
+
+ void set_axis(int p_axis, int32_t p_value);
+ int32_t get_axis(int p_axis) const;
+
+ int min_axis() const;
+ int max_axis() const;
+
+ _FORCE_INLINE_ void zero();
+
+ _FORCE_INLINE_ Vector3i abs() const;
+ _FORCE_INLINE_ Vector3i sign() const;
+
+ /* Operators */
+
+ _FORCE_INLINE_ Vector3i &operator+=(const Vector3i &p_v);
+ _FORCE_INLINE_ Vector3i operator+(const Vector3i &p_v) const;
+ _FORCE_INLINE_ Vector3i &operator-=(const Vector3i &p_v);
+ _FORCE_INLINE_ Vector3i operator-(const Vector3i &p_v) const;
+ _FORCE_INLINE_ Vector3i &operator*=(const Vector3i &p_v);
+ _FORCE_INLINE_ Vector3i operator*(const Vector3i &p_v) const;
+ _FORCE_INLINE_ Vector3i &operator/=(const Vector3i &p_v);
+ _FORCE_INLINE_ Vector3i operator/(const Vector3i &p_v) const;
+
+ _FORCE_INLINE_ Vector3i &operator*=(int32_t p_scalar);
+ _FORCE_INLINE_ Vector3i operator*(int32_t p_scalar) const;
+ _FORCE_INLINE_ Vector3i &operator/=(int32_t p_scalar);
+ _FORCE_INLINE_ Vector3i operator/(int32_t p_scalar) const;
+
+ _FORCE_INLINE_ Vector3i operator-() const;
+
+ _FORCE_INLINE_ bool operator==(const Vector3i &p_v) const;
+ _FORCE_INLINE_ bool operator!=(const Vector3i &p_v) const;
+ _FORCE_INLINE_ bool operator<(const Vector3i &p_v) const;
+ _FORCE_INLINE_ bool operator<=(const Vector3i &p_v) const;
+ _FORCE_INLINE_ bool operator>(const Vector3i &p_v) const;
+ _FORCE_INLINE_ bool operator>=(const Vector3i &p_v) const;
+
+ operator String() const;
+
+ _FORCE_INLINE_ Vector3i(int32_t p_x, int32_t p_y, int32_t p_z) {
+ x = p_x;
+ y = p_y;
+ z = p_z;
+ }
+ _FORCE_INLINE_ Vector3i() { x = y = z = 0; }
+};
+
+Vector3i Vector3i::abs() const {
+
+ return Vector3i(ABS(x), ABS(y), ABS(z));
+}
+
+Vector3i Vector3i::sign() const {
+
+ return Vector3i(SGN(x), SGN(y), SGN(z));
+}
+
+/* Operators */
+
+Vector3i &Vector3i::operator+=(const Vector3i &p_v) {
+
+ x += p_v.x;
+ y += p_v.y;
+ z += p_v.z;
+ return *this;
+}
+
+Vector3i Vector3i::operator+(const Vector3i &p_v) const {
+
+ return Vector3i(x + p_v.x, y + p_v.y, z + p_v.z);
+}
+
+Vector3i &Vector3i::operator-=(const Vector3i &p_v) {
+
+ x -= p_v.x;
+ y -= p_v.y;
+ z -= p_v.z;
+ return *this;
+}
+Vector3i Vector3i::operator-(const Vector3i &p_v) const {
+
+ return Vector3i(x - p_v.x, y - p_v.y, z - p_v.z);
+}
+
+Vector3i &Vector3i::operator*=(const Vector3i &p_v) {
+
+ x *= p_v.x;
+ y *= p_v.y;
+ z *= p_v.z;
+ return *this;
+}
+Vector3i Vector3i::operator*(const Vector3i &p_v) const {
+
+ return Vector3i(x * p_v.x, y * p_v.y, z * p_v.z);
+}
+
+Vector3i &Vector3i::operator/=(const Vector3i &p_v) {
+
+ x /= p_v.x;
+ y /= p_v.y;
+ z /= p_v.z;
+ return *this;
+}
+
+Vector3i Vector3i::operator/(const Vector3i &p_v) const {
+
+ return Vector3i(x / p_v.x, y / p_v.y, z / p_v.z);
+}
+
+Vector3i &Vector3i::operator*=(int32_t p_scalar) {
+
+ x *= p_scalar;
+ y *= p_scalar;
+ z *= p_scalar;
+ return *this;
+}
+
+_FORCE_INLINE_ Vector3i operator*(int32_t p_scalar, const Vector3i &p_vec) {
+
+ return p_vec * p_scalar;
+}
+
+Vector3i Vector3i::operator*(int32_t p_scalar) const {
+
+ return Vector3i(x * p_scalar, y * p_scalar, z * p_scalar);
+}
+
+Vector3i &Vector3i::operator/=(int32_t p_scalar) {
+
+ x /= p_scalar;
+ y /= p_scalar;
+ z /= p_scalar;
+ return *this;
+}
+
+Vector3i Vector3i::operator/(int32_t p_scalar) const {
+
+ return Vector3i(x / p_scalar, y / p_scalar, z / p_scalar);
+}
+
+Vector3i Vector3i::operator-() const {
+
+ return Vector3i(-x, -y, -z);
+}
+
+bool Vector3i::operator==(const Vector3i &p_v) const {
+
+ return (x == p_v.x && y == p_v.y && z == p_v.z);
+}
+
+bool Vector3i::operator!=(const Vector3i &p_v) const {
+
+ return (x != p_v.x || y != p_v.y || z != p_v.z);
+}
+
+bool Vector3i::operator<(const Vector3i &p_v) const {
+
+ if (x == p_v.x) {
+ if (y == p_v.y)
+ return z < p_v.z;
+ else
+ return y < p_v.y;
+ } else {
+ return x < p_v.x;
+ }
+}
+
+bool Vector3i::operator>(const Vector3i &p_v) const {
+
+ if (x == p_v.x) {
+ if (y == p_v.y)
+ return z > p_v.z;
+ else
+ return y > p_v.y;
+ } else {
+ return x > p_v.x;
+ }
+}
+
+bool Vector3i::operator<=(const Vector3i &p_v) const {
+
+ if (x == p_v.x) {
+ if (y == p_v.y)
+ return z <= p_v.z;
+ else
+ return y < p_v.y;
+ } else {
+ return x < p_v.x;
+ }
+}
+
+bool Vector3i::operator>=(const Vector3i &p_v) const {
+
+ if (x == p_v.x) {
+ if (y == p_v.y)
+ return z >= p_v.z;
+ else
+ return y > p_v.y;
+ } else {
+ return x > p_v.x;
+ }
+}
+
+void Vector3i::zero() {
+
+ x = y = z = 0;
+}
+
+#endif // VECTOR3I_H
diff --git a/core/message_queue.cpp b/core/message_queue.cpp
index 64ceec5ee4..42390935d4 100644
--- a/core/message_queue.cpp
+++ b/core/message_queue.cpp
@@ -250,7 +250,7 @@ void MessageQueue::_call_function(Object *p_target, const StringName &p_func, co
p_target->call(p_func, argptrs, p_argcount, ce);
if (p_show_error && ce.error != Variant::CallError::CALL_OK) {
- ERR_PRINTS("Error calling deferred method: " + Variant::get_call_error_text(p_target, p_func, argptrs, p_argcount, ce) + ".");
+ ERR_PRINT("Error calling deferred method: " + Variant::get_call_error_text(p_target, p_func, argptrs, p_argcount, ce) + ".");
}
}
diff --git a/core/method_ptrcall.h b/core/method_ptrcall.h
index 0f2458d982..118970de80 100644
--- a/core/method_ptrcall.h
+++ b/core/method_ptrcall.h
@@ -32,6 +32,7 @@
#define METHOD_PTRCALL_H
#include "core/math/transform_2d.h"
+#include "core/object_id.h"
#include "core/typedefs.h"
#include "core/variant.h"
@@ -167,6 +168,21 @@ struct PtrToArg<const T *> {
}
};
+//this is for ObjectID
+
+template <>
+struct PtrToArg<ObjectID> {
+ _FORCE_INLINE_ static const ObjectID convert(const void *p_ptr) {
+
+ return ObjectID(*reinterpret_cast<const uint64_t *>(p_ptr));
+ }
+
+ _FORCE_INLINE_ static void encode(const ObjectID &p_val, void *p_ptr) {
+
+ *((uint64_t *)p_ptr) = p_val;
+ }
+};
+
//this is for the special cases used by Variant
#define MAKE_VECARG(m_type) \
diff --git a/core/oa_hash_map.h b/core/oa_hash_map.h
index 7407c52816..182ed8b116 100644
--- a/core/oa_hash_map.h
+++ b/core/oa_hash_map.h
@@ -240,6 +240,22 @@ public:
return false;
}
+ /**
+ * returns true if the value was found, false otherwise.
+ *
+ * if r_data is not NULL then the value will be written to the object
+ * it points to.
+ */
+ TValue *lookup_ptr(const TKey &p_key) const {
+ uint32_t pos = 0;
+ bool exists = _lookup_pos(p_key, pos);
+
+ if (exists) {
+ return &values[pos];
+ }
+ return NULL;
+ }
+
_FORCE_INLINE_ bool has(const TKey &p_key) const {
uint32_t _pos = 0;
return _lookup_pos(p_key, _pos);
diff --git a/core/object.cpp b/core/object.cpp
index 21a3b2cc6c..9a5cfe5c22 100644
--- a/core/object.cpp
+++ b/core/object.cpp
@@ -968,7 +968,7 @@ void Object::cancel_delete() {
_predelete_ok = true;
}
-void Object::set_script_and_instance(const RefPtr &p_script, ScriptInstance *p_instance) {
+void Object::set_script_and_instance(const Variant &p_script, ScriptInstance *p_instance) {
//this function is not meant to be used in any of these ways
ERR_FAIL_COND(p_script.is_null());
@@ -979,7 +979,7 @@ void Object::set_script_and_instance(const RefPtr &p_script, ScriptInstance *p_i
script_instance = p_instance;
}
-void Object::set_script(const RefPtr &p_script) {
+void Object::set_script(const Variant &p_script) {
if (script == p_script)
return;
@@ -990,7 +990,7 @@ void Object::set_script(const RefPtr &p_script) {
}
script = p_script;
- Ref<Script> s(script);
+ Ref<Script> s = script;
if (!s.is_null()) {
if (s->can_instance()) {
@@ -1017,12 +1017,12 @@ void Object::set_script_instance(ScriptInstance *p_instance) {
script_instance = p_instance;
if (p_instance)
- script = p_instance->get_script().get_ref_ptr();
+ script = p_instance->get_script();
else
- script = RefPtr();
+ script = Variant();
}
-RefPtr Object::get_script() const {
+Variant Object::get_script() const {
return script;
}
@@ -1225,7 +1225,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
if (ce.error == Variant::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) {
//most likely object is not initialized yet, do not throw error.
} else {
- ERR_PRINTS("Error calling method from signal '" + String(p_name) + "': " + Variant::get_call_error_text(target, c.method, args, argc, ce) + ".");
+ ERR_PRINT("Error calling method from signal '" + String(p_name) + "': " + Variant::get_call_error_text(target, c.method, args, argc, ce) + ".");
err = ERR_METHOD_NOT_FOUND;
}
}
@@ -1911,12 +1911,11 @@ void Object::set_script_instance_binding(int p_script_language_index, void *p_da
_script_instance_bindings[p_script_language_index] = p_data;
}
-Object::Object() {
-
+void Object::_construct_object(bool p_reference) {
+ type_is_reference = p_reference;
_class_ptr = NULL;
_block_signals = false;
_predelete_ok = 0;
- _instance_id = 0;
_instance_id = ObjectDB::add_instance(this);
_can_translate = true;
_is_queued_for_deletion = false;
@@ -1934,6 +1933,14 @@ Object::Object() {
_lock_index.init(1);
#endif
}
+Object::Object(bool p_reference) {
+ _construct_object(p_reference);
+}
+
+Object::Object() {
+
+ _construct_object(false);
+}
Object::~Object() {
@@ -1945,7 +1952,7 @@ Object::~Object() {
if (_emitting) {
//@todo this may need to actually reach the debugger prioritarily somehow because it may crash before
- ERR_PRINTS("Object " + to_string() + " was freed or unreferenced while a signal is being emitted from it. Try connecting to the signal using 'CONNECT_DEFERRED' flag, or use queue_free() to free the object (if this object is a Node) to avoid this error and potential crashes.");
+ ERR_PRINT("Object " + to_string() + " was freed or unreferenced while a signal is being emitted from it. Try connecting to the signal using 'CONNECT_DEFERRED' flag, or use queue_free() to free the object (if this object is a Node) to avoid this error and potential crashes.");
}
while ((S = signal_map.next(NULL))) {
@@ -1972,7 +1979,7 @@ Object::~Object() {
}
ObjectDB::remove_instance(this);
- _instance_id = 0;
+ _instance_id = ObjectID();
_predelete_ok = 2;
if (!ScriptServer::are_languages_finished()) {
@@ -1994,96 +2001,139 @@ void postinitialize_handler(Object *p_object) {
p_object->_postinitialize();
}
-HashMap<ObjectID, Object *> ObjectDB::instances;
-ObjectID ObjectDB::instance_counter = 1;
-HashMap<Object *, ObjectID, ObjectDB::ObjectPtrHash> ObjectDB::instance_checks;
-ObjectID ObjectDB::add_instance(Object *p_object) {
-
- ERR_FAIL_COND_V(p_object->get_instance_id() != 0, 0);
-
- rw_lock->write_lock();
- ObjectID instance_id = ++instance_counter;
- instances[instance_id] = p_object;
- instance_checks[p_object] = instance_id;
-
- rw_lock->write_unlock();
+void ObjectDB::debug_objects(DebugFunc p_func) {
- return instance_id;
+ spin_lock.lock();
+ for (uint32_t i = 0; i < slot_count; i++) {
+ uint32_t slot = object_slots[i].next_free;
+ p_func(object_slots[slot].object);
+ }
+ spin_lock.unlock();
}
-void ObjectDB::remove_instance(Object *p_object) {
+void Object::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
+}
- rw_lock->write_lock();
+SpinLock ObjectDB::spin_lock;
+uint32_t ObjectDB::slot_count = 0;
+uint32_t ObjectDB::slot_max = 0;
+ObjectDB::ObjectSlot *ObjectDB::object_slots = nullptr;
+uint64_t ObjectDB::validator_counter = 0;
- instances.erase(p_object->get_instance_id());
- instance_checks.erase(p_object);
+int ObjectDB::get_object_count() {
- rw_lock->write_unlock();
+ return slot_count;
}
-Object *ObjectDB::get_instance(ObjectID p_instance_id) {
- rw_lock->read_lock();
- Object **obj = instances.getptr(p_instance_id);
- rw_lock->read_unlock();
+ObjectID ObjectDB::add_instance(Object *p_object) {
- if (!obj)
- return NULL;
- return *obj;
-}
+ spin_lock.lock();
+ if (unlikely(slot_count == slot_max)) {
-void ObjectDB::debug_objects(DebugFunc p_func) {
+ CRASH_COND(slot_count == (1 << OBJECTDB_SLOT_MAX_COUNT_BITS));
+
+ uint32_t new_slot_max = slot_max > 0 ? slot_max * 2 : 1;
+ object_slots = (ObjectSlot *)memrealloc(object_slots, sizeof(ObjectSlot) * new_slot_max);
+ for (uint32_t i = slot_max; i < new_slot_max; i++) {
+ object_slots[i].object = nullptr;
+ object_slots[i].is_reference = false;
+ object_slots[i].next_free = i;
+ object_slots[i].validator = 0;
+ }
+ slot_max = new_slot_max;
+ }
- rw_lock->read_lock();
+ uint32_t slot = object_slots[slot_count].next_free;
+ if (object_slots[slot].object != nullptr) {
+ spin_lock.unlock();
+ ERR_FAIL_COND_V(object_slots[slot].object != nullptr, ObjectID());
+ }
+ object_slots[slot].object = p_object;
+ object_slots[slot].is_reference = p_object->is_reference();
+ validator_counter = (validator_counter + 1) & OBJECTDB_VALIDATOR_MASK;
+ if (unlikely(validator_counter == 0)) {
+ validator_counter = 1;
+ }
+ object_slots[slot].validator = validator_counter;
- const ObjectID *K = NULL;
- while ((K = instances.next(K))) {
+ uint64_t id = validator_counter;
+ id <<= OBJECTDB_SLOT_MAX_COUNT_BITS;
+ id |= uint64_t(slot);
- p_func(instances[*K]);
+ if (p_object->is_reference()) {
+ id |= OBJECTDB_REFERENCE_BIT;
}
- rw_lock->read_unlock();
-}
+ slot_count++;
-void Object::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
+ spin_lock.unlock();
+
+ return ObjectID(id);
}
-int ObjectDB::get_object_count() {
+void ObjectDB::remove_instance(Object *p_object) {
+ uint64_t t = p_object->get_instance_id();
+ uint32_t slot = t & OBJECTDB_SLOT_MAX_COUNT_MASK; //slot is always valid on valid object
- rw_lock->read_lock();
- int count = instances.size();
- rw_lock->read_unlock();
+ spin_lock.lock();
- return count;
-}
+#ifdef DEBUG_ENABLED
-RWLock *ObjectDB::rw_lock = NULL;
+ if (object_slots[slot].object != p_object) {
+ spin_lock.unlock();
+ ERR_FAIL_COND(object_slots[slot].object != p_object);
+ }
+ {
+ uint64_t validator = (t >> OBJECTDB_SLOT_MAX_COUNT_BITS) & OBJECTDB_VALIDATOR_MASK;
+ if (object_slots[slot].validator != validator) {
+ spin_lock.unlock();
+ ERR_FAIL_COND(object_slots[slot].validator != validator);
+ }
+ }
+
+#endif
+ //decrease slot count
+ slot_count--;
+ //set the free slot properly
+ object_slots[slot_count].next_free = slot;
+ //invalidate, so checks against it fail
+ object_slots[slot].validator = 0;
+ object_slots[slot].is_reference = false;
+ object_slots[slot].object = nullptr;
+
+ spin_lock.unlock();
+}
void ObjectDB::setup() {
- rw_lock = RWLock::create();
+ //nothing to do now
}
void ObjectDB::cleanup() {
- rw_lock->write_lock();
- if (instances.size()) {
+ if (slot_count > 0) {
+ spin_lock.lock();
WARN_PRINT("ObjectDB Instances still exist!");
if (OS::get_singleton()->is_stdout_verbose()) {
- const ObjectID *K = NULL;
- while ((K = instances.next(K))) {
+ for (uint32_t i = 0; i < slot_count; i++) {
+ uint32_t slot = object_slots[i].next_free;
+ Object *obj = object_slots[slot].object;
String node_name;
- if (instances[*K]->is_class("Node"))
- node_name = " - Node name: " + String(instances[*K]->call("get_name"));
- if (instances[*K]->is_class("Resource"))
- node_name = " - Resource name: " + String(instances[*K]->call("get_name")) + " Path: " + String(instances[*K]->call("get_path"));
- print_line("Leaked instance: " + String(instances[*K]->get_class()) + ":" + itos(*K) + node_name);
+ if (obj->is_class("Node"))
+ node_name = " - Node name: " + String(obj->call("get_name"));
+ if (obj->is_class("Resource"))
+ node_name = " - Resource name: " + String(obj->call("get_name")) + " Path: " + String(obj->call("get_path"));
+
+ uint64_t id = uint64_t(slot) | (uint64_t(object_slots[slot].validator) << OBJECTDB_VALIDATOR_BITS) | (object_slots[slot].is_reference ? OBJECTDB_REFERENCE_BIT : 0);
+ print_line("Leaked instance: " + String(obj->get_class()) + ":" + itos(id) + node_name);
}
}
+ spin_lock.unlock();
+ }
+
+ if (object_slots) {
+ memfree(object_slots);
}
- instances.clear();
- instance_checks.clear();
- rw_lock->write_unlock();
- memdelete(rw_lock);
}
diff --git a/core/object.h b/core/object.h
index 865c155764..6a229afaea 100644
--- a/core/object.h
+++ b/core/object.h
@@ -34,8 +34,10 @@
#include "core/hash_map.h"
#include "core/list.h"
#include "core/map.h"
+#include "core/object_id.h"
#include "core/os/rw_lock.h"
#include "core/set.h"
+#include "core/spin_lock.h"
#include "core/variant.h"
#include "core/vmap.h"
@@ -89,6 +91,7 @@ enum PropertyHint {
PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send
PROPERTY_HINT_NODE_PATH_VALID_TYPES,
PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog
+ PROPERTY_HINT_INT_IS_OBJECTID,
PROPERTY_HINT_MAX,
// When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit
};
@@ -397,7 +400,6 @@ public: \
private:
class ScriptInstance;
-typedef uint64_t ObjectID;
class Object {
public:
@@ -452,7 +454,7 @@ private:
_id(p_id),
method(p_method) {
}
- Target() { _id = 0; }
+ Target() { _id = ObjectID(); }
};
struct Slot {
@@ -487,7 +489,7 @@ private:
Set<String> editor_section_folding;
#endif
ScriptInstance *script_instance;
- RefPtr script;
+ Variant script; //reference does not yet exist, store it in a
Dictionary metadata;
mutable StringName _class_name;
mutable const StringName *_class_ptr;
@@ -505,9 +507,13 @@ private:
void property_list_changed_notify();
+ _FORCE_INLINE_ void _construct_object(bool p_reference);
+
friend class Reference;
+ bool type_is_reference = false;
uint32_t instance_binding_count;
void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS];
+ Object(bool p_reference);
protected:
virtual void _initialize_classv() { initialize_class(); }
@@ -679,8 +685,8 @@ public:
/* SCRIPT */
- void set_script(const RefPtr &p_script);
- RefPtr get_script() const;
+ void set_script(const Variant &p_script);
+ Variant get_script() const;
/* SCRIPT */
@@ -699,7 +705,7 @@ public:
void set_script_instance(ScriptInstance *p_instance);
_FORCE_INLINE_ ScriptInstance *get_script_instance() const { return script_instance; }
- void set_script_and_instance(const RefPtr &p_script, ScriptInstance *p_instance); //some script languages can't control instance creation, so this function eases the process
+ void set_script_and_instance(const Variant &p_script, ScriptInstance *p_instance); //some script languages can't control instance creation, so this function eases the process
void add_user_signal(const MethodInfo &p_signal);
Error emit_signal(const StringName &p_name, VARIANT_ARG_LIST);
@@ -750,6 +756,7 @@ public:
void clear_internal_resource_paths();
+ _ALWAYS_INLINE_ bool is_reference() const { return type_is_reference; }
Object();
virtual ~Object();
};
@@ -759,49 +766,63 @@ void postinitialize_handler(Object *p_object);
class ObjectDB {
- struct ObjectPtrHash {
-
- static _FORCE_INLINE_ uint32_t hash(const Object *p_obj) {
-
- union {
- const Object *p;
- unsigned long i;
- } u;
- u.p = p_obj;
- return HashMapHasherDefault::hash((uint64_t)u.i);
- }
+//this needs to add up to 63, 1 bit is for reference
+#define OBJECTDB_VALIDATOR_BITS 39
+#define OBJECTDB_VALIDATOR_MASK ((uint64_t(1) << OBJECTDB_VALIDATOR_BITS) - 1)
+#define OBJECTDB_SLOT_MAX_COUNT_BITS 24
+#define OBJECTDB_SLOT_MAX_COUNT_MASK ((uint64_t(1) << OBJECTDB_SLOT_MAX_COUNT_BITS) - 1)
+#define OBJECTDB_REFERENCE_BIT (uint64_t(1) << (OBJECTDB_SLOT_MAX_COUNT_BITS + OBJECTDB_VALIDATOR_BITS))
+
+ struct ObjectSlot { //128 bits per slot
+ uint64_t validator : OBJECTDB_VALIDATOR_BITS;
+ uint64_t next_free : OBJECTDB_SLOT_MAX_COUNT_BITS;
+ uint64_t is_reference : 1;
+ Object *object;
};
- static HashMap<ObjectID, Object *> instances;
- static HashMap<Object *, ObjectID, ObjectPtrHash> instance_checks;
+ static SpinLock spin_lock;
+ static uint32_t slot_count;
+ static uint32_t slot_max;
+ static ObjectSlot *object_slots;
+ static uint64_t validator_counter;
- static ObjectID instance_counter;
friend class Object;
friend void unregister_core_types();
-
- static RWLock *rw_lock;
static void cleanup();
+
static ObjectID add_instance(Object *p_object);
static void remove_instance(Object *p_object);
+
friend void register_core_types();
static void setup();
public:
typedef void (*DebugFunc)(Object *p_obj);
- static Object *get_instance(ObjectID p_instance_id);
- static void debug_objects(DebugFunc p_func);
- static int get_object_count();
+ _ALWAYS_INLINE_ static Object *get_instance(ObjectID p_instance_id) {
+
+ uint64_t id = p_instance_id;
+ uint32_t slot = id & OBJECTDB_SLOT_MAX_COUNT_MASK;
+
+ ERR_FAIL_COND_V(slot >= slot_max, nullptr); //this should never happen unless RID is corrupted
+
+ spin_lock.lock();
- _FORCE_INLINE_ static bool instance_validate(Object *p_ptr) {
- rw_lock->read_lock();
+ uint64_t validator = (id >> OBJECTDB_SLOT_MAX_COUNT_BITS) & OBJECTDB_VALIDATOR_MASK;
- bool exists = instance_checks.has(p_ptr);
+ if (unlikely(object_slots[slot].validator != validator)) {
+ spin_lock.unlock();
+ return nullptr;
+ }
+
+ Object *object = object_slots[slot].object;
- rw_lock->read_unlock();
+ spin_lock.unlock();
- return exists;
+ return object;
}
+ static void debug_objects(DebugFunc p_func);
+ static int get_object_count();
};
//needed by macros
diff --git a/core/object_id.h b/core/object_id.h
new file mode 100644
index 0000000000..6ab1a3031a
--- /dev/null
+++ b/core/object_id.h
@@ -0,0 +1,33 @@
+#ifndef OBJECT_ID_H
+#define OBJECT_ID_H
+
+#include "core/typedefs.h"
+
+// Class to store an object ID (int64)
+// needs to be compatile with int64 because this is what Variant uses
+// Also, need to be explicitly only castable to 64 bits integer types
+// to avoid bugs due to loss of precision
+
+class ObjectID {
+ uint64_t id = 0;
+
+public:
+ _ALWAYS_INLINE_ bool is_reference() const { return (id & (uint64_t(1) << 63)) != 0; }
+ _ALWAYS_INLINE_ bool is_valid() const { return id != 0; }
+ _ALWAYS_INLINE_ bool is_null() const { return id == 0; }
+ _ALWAYS_INLINE_ operator uint64_t() const { return id; }
+ _ALWAYS_INLINE_ operator int64_t() const { return id; }
+
+ _ALWAYS_INLINE_ bool operator==(const ObjectID &p_id) const { return id == p_id.id; }
+ _ALWAYS_INLINE_ bool operator!=(const ObjectID &p_id) const { return id != p_id.id; }
+ _ALWAYS_INLINE_ bool operator<(const ObjectID &p_id) const { return id < p_id.id; }
+
+ _ALWAYS_INLINE_ void operator=(int64_t p_int64) { id = p_int64; }
+ _ALWAYS_INLINE_ void operator=(uint64_t p_uint64) { id = p_uint64; }
+
+ _ALWAYS_INLINE_ ObjectID() {}
+ _ALWAYS_INLINE_ explicit ObjectID(const uint64_t p_id) { id = p_id; }
+ _ALWAYS_INLINE_ explicit ObjectID(const int64_t p_id) { id = p_id; }
+};
+
+#endif // OBJECT_ID_H
diff --git a/core/os/dir_access.cpp b/core/os/dir_access.cpp
index 0477db82be..f65fc00077 100644
--- a/core/os/dir_access.cpp
+++ b/core/os/dir_access.cpp
@@ -285,7 +285,7 @@ Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) {
FileAccess *fsrc = FileAccess::open(p_from, FileAccess::READ, &err);
if (err) {
- ERR_PRINTS("Failed to open " + p_from);
+ ERR_PRINT("Failed to open " + p_from);
return err;
}
@@ -294,7 +294,7 @@ Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) {
fsrc->close();
memdelete(fsrc);
- ERR_PRINTS("Failed to open " + p_to);
+ ERR_PRINT("Failed to open " + p_to);
return err;
}
diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp
index 5ecdd74a4b..6020c4b219 100644
--- a/core/os/main_loop.cpp
+++ b/core/os/main_loop.cpp
@@ -95,7 +95,7 @@ void MainLoop::input_event(const Ref<InputEvent> &p_event) {
void MainLoop::init() {
if (init_script.is_valid())
- set_script(init_script.get_ref_ptr());
+ set_script(init_script);
if (get_script_instance())
get_script_instance()->call("_initialize");
@@ -131,6 +131,6 @@ void MainLoop::finish() {
if (get_script_instance()) {
get_script_instance()->call("_finalize");
- set_script(RefPtr()); //clear script
+ set_script(Variant()); //clear script
}
}
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 1ed9484208..d1b6ccab54 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -188,7 +188,7 @@ int OS::get_process_id() const {
void OS::vibrate_handheld(int p_duration_ms) {
- WARN_PRINTS("vibrate_handheld() only works with Android and iOS");
+ WARN_PRINT("vibrate_handheld() only works with Android and iOS");
}
bool OS::is_stdout_verbose() const {
@@ -586,16 +586,6 @@ bool OS::is_vsync_via_compositor_enabled() const {
return _vsync_via_compositor;
}
-OS::PowerState OS::get_power_state() {
- return POWERSTATE_UNKNOWN;
-}
-int OS::get_power_seconds_left() {
- return -1;
-}
-int OS::get_power_percent_left() {
- return -1;
-}
-
void OS::set_has_server_feature_callback(HasServerFeatureCallback p_callback) {
has_server_feature_callback = p_callback;
@@ -691,9 +681,9 @@ const char *OS::get_video_driver_name(int p_driver) const {
switch (p_driver) {
case VIDEO_DRIVER_GLES2:
return "GLES2";
- case VIDEO_DRIVER_GLES3:
+ case VIDEO_DRIVER_VULKAN:
default:
- return "GLES3";
+ return "Vulkan";
}
}
diff --git a/core/os/os.h b/core/os/os.h
index 89b3414b3e..e4661e4583 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -78,14 +78,6 @@ public:
typedef void (*ImeCallback)(void *p_inp, String p_text, Point2 p_selection);
typedef bool (*HasServerFeatureCallback)(const String &p_feature);
- enum PowerState {
- POWERSTATE_UNKNOWN, /**< cannot determine power status */
- POWERSTATE_ON_BATTERY, /**< Not plugged in, running on the battery */
- POWERSTATE_NO_BATTERY, /**< Plugged in, no battery available */
- POWERSTATE_CHARGING, /**< Plugged in, charging battery */
- POWERSTATE_CHARGED /**< Plugged in, battery charged */
- };
-
enum RenderThreadMode {
RENDER_THREAD_UNSAFE,
@@ -181,7 +173,7 @@ public:
virtual void get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen = 0) const = 0;
enum VideoDriver {
- VIDEO_DRIVER_GLES3,
+ VIDEO_DRIVER_VULKAN,
VIDEO_DRIVER_GLES2,
VIDEO_DRIVER_MAX,
};
@@ -517,10 +509,6 @@ public:
void set_vsync_via_compositor(bool p_enable);
bool is_vsync_via_compositor_enabled() const;
- virtual OS::PowerState get_power_state();
- virtual int get_power_seconds_left();
- virtual int get_power_percent_left();
-
virtual void force_process_input(){};
bool has_feature(const String &p_feature);
@@ -542,6 +530,4 @@ public:
virtual ~OS();
};
-VARIANT_ENUM_CAST(OS::PowerState);
-
#endif
diff --git a/core/os/semaphore.cpp b/core/os/semaphore.cpp
index b2ba9716f0..2c20f234d0 100644
--- a/core/os/semaphore.cpp
+++ b/core/os/semaphore.cpp
@@ -32,14 +32,14 @@
#include "core/error_macros.h"
-Semaphore *(*Semaphore::create_func)() = 0;
+SemaphoreOld *(*SemaphoreOld::create_func)() = 0;
-Semaphore *Semaphore::create() {
+SemaphoreOld *SemaphoreOld::create() {
ERR_FAIL_COND_V(!create_func, 0);
return create_func();
}
-Semaphore::~Semaphore() {
+SemaphoreOld::~SemaphoreOld() {
}
diff --git a/core/os/semaphore.h b/core/os/semaphore.h
index 9f3c0f549c..f16a15a6db 100644
--- a/core/os/semaphore.h
+++ b/core/os/semaphore.h
@@ -32,19 +32,53 @@
#define SEMAPHORE_H
#include "core/error_list.h"
+#include "core/typedefs.h"
+
+#include <condition_variable>
+#include <mutex>
class Semaphore {
+private:
+ std::mutex mutex_;
+ std::condition_variable condition_;
+ unsigned long count_ = 0; // Initialized as locked.
+
+public:
+ _ALWAYS_INLINE_ void post() {
+ std::lock_guard<decltype(mutex_)> lock(mutex_);
+ ++count_;
+ condition_.notify_one();
+ }
+
+ _ALWAYS_INLINE_ void wait() {
+ std::unique_lock<decltype(mutex_)> lock(mutex_);
+ while (!count_) // Handle spurious wake-ups.
+ condition_.wait(lock);
+ --count_;
+ }
+
+ _ALWAYS_INLINE_ bool try_wait() {
+ std::lock_guard<decltype(mutex_)> lock(mutex_);
+ if (count_) {
+ --count_;
+ return true;
+ }
+ return false;
+ }
+};
+
+class SemaphoreOld {
protected:
- static Semaphore *(*create_func)();
+ static SemaphoreOld *(*create_func)();
public:
virtual Error wait() = 0; ///< wait until semaphore has positive value, then decrement and pass
virtual Error post() = 0; ///< unlock the semaphore, incrementing the value
virtual int get() const = 0; ///< get semaphore value
- static Semaphore *create(); ///< Create a mutex
+ static SemaphoreOld *create(); ///< Create a mutex
- virtual ~Semaphore();
+ virtual ~SemaphoreOld();
};
#endif
diff --git a/core/os/thread_dummy.cpp b/core/os/thread_dummy.cpp
index d4f65b0312..916aeeda30 100644
--- a/core/os/thread_dummy.cpp
+++ b/core/os/thread_dummy.cpp
@@ -48,12 +48,12 @@ void MutexDummy::make_default() {
Mutex::create_func = &MutexDummy::create;
};
-Semaphore *SemaphoreDummy::create() {
+SemaphoreOld *SemaphoreDummy::create() {
return memnew(SemaphoreDummy);
};
void SemaphoreDummy::make_default() {
- Semaphore::create_func = &SemaphoreDummy::create;
+ SemaphoreOld::create_func = &SemaphoreDummy::create;
};
RWLock *RWLockDummy::create() {
diff --git a/core/os/thread_dummy.h b/core/os/thread_dummy.h
index c8b52ae4dd..9329cdaa32 100644
--- a/core/os/thread_dummy.h
+++ b/core/os/thread_dummy.h
@@ -58,9 +58,9 @@ public:
static void make_default();
};
-class SemaphoreDummy : public Semaphore {
+class SemaphoreDummy : public SemaphoreOld {
- static Semaphore *create();
+ static SemaphoreOld *create();
public:
virtual Error wait() { return OK; };
diff --git a/core/project_settings.cpp b/core/project_settings.cpp
index a01a8a35c6..59d7e82850 100644
--- a/core/project_settings.cpp
+++ b/core/project_settings.cpp
@@ -206,7 +206,7 @@ bool ProjectSettings::_get(const StringName &p_name, Variant &r_ret) const {
name = feature_overrides[name];
}
if (!props.has(name)) {
- WARN_PRINTS("Property not found: " + String(name));
+ WARN_PRINT("Property not found: " + String(name));
return false;
}
r_ret = props[name].variant;
@@ -579,7 +579,7 @@ Error ProjectSettings::_load_settings_text(const String &p_path) {
_convert_to_last_version(config_version);
return OK;
} else if (err != OK) {
- ERR_PRINTS("Error parsing " + p_path + " at line " + itos(lines) + ": " + error_text + " File might be corrupted.");
+ ERR_PRINT("Error parsing " + p_path + " at line " + itos(lines) + ": " + error_text + " File might be corrupted.");
memdelete(f);
return err;
}
@@ -612,7 +612,7 @@ Error ProjectSettings::_load_settings_text_or_binary(const String &p_text_path,
return OK;
} else if (err_text != ERR_FILE_NOT_FOUND) {
// If the text-based file exists but can't be loaded, we want to know it
- ERR_PRINTS("Couldn't load file '" + p_text_path + "', error code " + itos(err_text) + ".");
+ ERR_PRINT("Couldn't load file '" + p_text_path + "', error code " + itos(err_text) + ".");
return err_text;
}
diff --git a/core/reference.cpp b/core/reference.cpp
index b24b2a3ec0..dd65ccce69 100644
--- a/core/reference.cpp
+++ b/core/reference.cpp
@@ -102,7 +102,8 @@ bool Reference::unreference() {
return die;
}
-Reference::Reference() {
+Reference::Reference() :
+ Object(true) {
refcount.init();
refcount_init.init();
@@ -113,7 +114,7 @@ Reference::~Reference() {
Variant WeakRef::get_ref() const {
- if (ref == 0)
+ if (ref.is_null())
return Variant();
Object *obj = ObjectDB::get_instance(ref);
@@ -129,16 +130,15 @@ Variant WeakRef::get_ref() const {
}
void WeakRef::set_obj(Object *p_object) {
- ref = p_object ? p_object->get_instance_id() : 0;
+ ref = p_object ? p_object->get_instance_id() : ObjectID();
}
void WeakRef::set_ref(const REF &p_ref) {
- ref = p_ref.is_valid() ? p_ref->get_instance_id() : 0;
+ ref = p_ref.is_valid() ? p_ref->get_instance_id() : ObjectID();
}
-WeakRef::WeakRef() :
- ref(0) {
+WeakRef::WeakRef() {
}
void WeakRef::_bind_methods() {
diff --git a/core/reference.h b/core/reference.h
index d2314005b9..b01e0035a7 100644
--- a/core/reference.h
+++ b/core/reference.h
@@ -33,7 +33,6 @@
#include "core/class_db.h"
#include "core/object.h"
-#include "core/ref_ptr.h"
#include "core/safe_refcount.h"
class Reference : public Object {
@@ -133,17 +132,9 @@ public:
return reference;
}
- RefPtr get_ref_ptr() const {
-
- RefPtr refptr;
- Ref<Reference> *irr = reinterpret_cast<Ref<Reference> *>(refptr.get_data());
- *irr = *this;
- return refptr;
- };
-
operator Variant() const {
- return Variant(get_ref_ptr());
+ return Variant(reference);
}
void operator=(const Ref &p_from) {
@@ -165,33 +156,24 @@ public:
r.reference = NULL;
}
- void operator=(const RefPtr &p_refptr) {
+ void operator=(const Variant &p_variant) {
- Ref<Reference> *irr = reinterpret_cast<Ref<Reference> *>(p_refptr.get_data());
- Reference *refb = irr->ptr();
- if (!refb) {
- unref();
+ Object *object = p_variant.get_validated_object();
+
+ if (object == reference) {
return;
}
- Ref r;
- r.reference = Object::cast_to<T>(refb);
- ref(r);
- r.reference = NULL;
- }
- void operator=(const Variant &p_variant) {
+ unref();
- RefPtr refptr = p_variant;
- Ref<Reference> *irr = reinterpret_cast<Ref<Reference> *>(refptr.get_data());
- Reference *refb = irr->ptr();
- if (!refb) {
- unref();
+ if (!object) {
return;
}
- Ref r;
- r.reference = Object::cast_to<T>(refb);
- ref(r);
- r.reference = NULL;
+
+ Reference *r = Object::cast_to<Reference>(object);
+ if (r && r->reference()) {
+ reference = static_cast<T *>(r);
+ }
}
template <class T_Other>
@@ -237,33 +219,19 @@ public:
Ref(const Variant &p_variant) {
- RefPtr refptr = p_variant;
- Ref<Reference> *irr = reinterpret_cast<Ref<Reference> *>(refptr.get_data());
- reference = NULL;
- Reference *refb = irr->ptr();
- if (!refb) {
- unref();
+ Object *object = p_variant.get_validated_object();
+
+ if (!object) {
+ reference = nullptr;
return;
}
- Ref r;
- r.reference = Object::cast_to<T>(refb);
- ref(r);
- r.reference = NULL;
- }
-
- Ref(const RefPtr &p_refptr) {
- Ref<Reference> *irr = reinterpret_cast<Ref<Reference> *>(p_refptr.get_data());
- reference = NULL;
- Reference *refb = irr->ptr();
- if (!refb) {
- unref();
- return;
+ Reference *r = Object::cast_to<Reference>(object);
+ if (r && r->reference()) {
+ reference = static_cast<T *>(r);
+ } else {
+ reference = nullptr;
}
- Ref r;
- r.reference = Object::cast_to<T>(refb);
- ref(r);
- r.reference = NULL;
}
inline bool is_valid() const { return reference != NULL; }
@@ -340,32 +308,6 @@ struct PtrToArg<const Ref<T> &> {
}
};
-//this is for RefPtr
-
-template <>
-struct PtrToArg<RefPtr> {
-
- _FORCE_INLINE_ static RefPtr convert(const void *p_ptr) {
-
- return Ref<Reference>(const_cast<Reference *>(reinterpret_cast<const Reference *>(p_ptr))).get_ref_ptr();
- }
-
- _FORCE_INLINE_ static void encode(RefPtr p_val, const void *p_ptr) {
-
- Ref<Reference> r = p_val;
- *(Ref<Reference> *)p_ptr = r;
- }
-};
-
-template <>
-struct PtrToArg<const RefPtr &> {
-
- _FORCE_INLINE_ static RefPtr convert(const void *p_ptr) {
-
- return Ref<Reference>(const_cast<Reference *>(reinterpret_cast<const Reference *>(p_ptr))).get_ref_ptr();
- }
-};
-
#endif // PTRCALL_ENABLED
#ifdef DEBUG_METHODS_ENABLED
diff --git a/core/resource.h b/core/resource.h
index 00d330a094..b30788010b 100644
--- a/core/resource.h
+++ b/core/resource.h
@@ -33,7 +33,6 @@
#include "core/class_db.h"
#include "core/object.h"
-#include "core/ref_ptr.h"
#include "core/reference.h"
#include "core/safe_refcount.h"
#include "core/self_list.h"
diff --git a/core/rid.h b/core/rid.h
index 7e12409181..0c4a96efed 100644
--- a/core/rid.h
+++ b/core/rid.h
@@ -32,172 +32,46 @@
#define RID_H
#include "core/list.h"
+#include "core/oa_hash_map.h"
#include "core/os/memory.h"
#include "core/safe_refcount.h"
#include "core/set.h"
#include "core/typedefs.h"
-class RID_OwnerBase;
-
-class RID_Data {
-
- friend class RID_OwnerBase;
-
-#ifndef DEBUG_ENABLED
- RID_OwnerBase *_owner;
-#endif
- uint32_t _id;
-
-public:
- _FORCE_INLINE_ uint32_t get_id() const { return _id; }
-
- virtual ~RID_Data();
-};
+class RID_AllocBase;
class RID {
- friend class RID_OwnerBase;
-
- mutable RID_Data *_data;
+ friend class RID_AllocBase;
+ uint64_t _id;
public:
- _FORCE_INLINE_ RID_Data *get_data() const { return _data; }
-
_FORCE_INLINE_ bool operator==(const RID &p_rid) const {
- return _data == p_rid._data;
+ return _id == p_rid._id;
}
_FORCE_INLINE_ bool operator<(const RID &p_rid) const {
- return _data < p_rid._data;
+ return _id < p_rid._id;
}
_FORCE_INLINE_ bool operator<=(const RID &p_rid) const {
- return _data <= p_rid._data;
+ return _id <= p_rid._id;
}
_FORCE_INLINE_ bool operator>(const RID &p_rid) const {
- return _data > p_rid._data;
+ return _id > p_rid._id;
}
_FORCE_INLINE_ bool operator!=(const RID &p_rid) const {
- return _data != p_rid._data;
+ return _id != p_rid._id;
}
- _FORCE_INLINE_ bool is_valid() const { return _data != NULL; }
+ _FORCE_INLINE_ bool is_valid() const { return _id != 0; }
+ _FORCE_INLINE_ bool is_null() const { return _id == 0; }
- _FORCE_INLINE_ uint32_t get_id() const { return _data ? _data->get_id() : 0; }
+ _FORCE_INLINE_ uint64_t get_id() const { return _id; }
_FORCE_INLINE_ RID() {
- _data = NULL;
- }
-};
-
-class RID_OwnerBase {
-protected:
- static SafeRefCount refcount;
- _FORCE_INLINE_ void _set_data(RID &p_rid, RID_Data *p_data) {
- p_rid._data = p_data;
- refcount.ref();
- p_data->_id = refcount.get();
-#ifndef DEBUG_ENABLED
- p_data->_owner = this;
-#endif
- }
-
-#ifndef DEBUG_ENABLED
-
- _FORCE_INLINE_ bool _is_owner(const RID &p_rid) const {
-
- return this == p_rid._data->_owner;
- }
-
- _FORCE_INLINE_ void _remove_owner(RID &p_rid) {
-
- p_rid._data->_owner = NULL;
- }
-#endif
-
-public:
- virtual void get_owned_list(List<RID> *p_owned) = 0;
-
- static void init_rid();
- virtual ~RID_OwnerBase() {}
-};
-
-template <class T>
-class RID_Owner : public RID_OwnerBase {
-public:
-#ifdef DEBUG_ENABLED
- mutable Set<RID_Data *> id_map;
-#endif
-public:
- _FORCE_INLINE_ RID make_rid(T *p_data) {
-
- RID rid;
- _set_data(rid, p_data);
-
-#ifdef DEBUG_ENABLED
- id_map.insert(p_data);
-#endif
-
- return rid;
- }
-
- _FORCE_INLINE_ T *get(const RID &p_rid) {
-
-#ifdef DEBUG_ENABLED
-
- ERR_FAIL_COND_V(!p_rid.is_valid(), NULL);
- ERR_FAIL_COND_V(!id_map.has(p_rid.get_data()), NULL);
-#endif
- return static_cast<T *>(p_rid.get_data());
- }
-
- _FORCE_INLINE_ T *getornull(const RID &p_rid) {
-
-#ifdef DEBUG_ENABLED
-
- if (p_rid.get_data()) {
- ERR_FAIL_COND_V(!id_map.has(p_rid.get_data()), NULL);
- }
-#endif
- return static_cast<T *>(p_rid.get_data());
- }
-
- _FORCE_INLINE_ T *getptr(const RID &p_rid) {
-
- return static_cast<T *>(p_rid.get_data());
- }
-
- _FORCE_INLINE_ bool owns(const RID &p_rid) const {
-
- if (p_rid.get_data() == NULL)
- return false;
-#ifdef DEBUG_ENABLED
- return id_map.has(p_rid.get_data());
-#else
- return _is_owner(p_rid);
-#endif
- }
-
- void free(RID p_rid) {
-
-#ifdef DEBUG_ENABLED
- id_map.erase(p_rid.get_data());
-#else
- _remove_owner(p_rid);
-#endif
- }
-
- void get_owned_list(List<RID> *p_owned) {
-
-#ifdef DEBUG_ENABLED
-
- for (typename Set<RID_Data *>::Element *E = id_map.front(); E; E = E->next()) {
- RID r;
- _set_data(r, static_cast<T *>(E->get()));
- p_owned->push_back(r);
- }
-#endif
+ _id = 0;
}
};
diff --git a/core/rid.cpp b/core/rid_owner.cpp
index 727658314f..a5065f29f8 100644
--- a/core/rid.cpp
+++ b/core/rid_owner.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* rid.cpp */
+/* rid_owner.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,14 +28,6 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "rid.h"
+#include "rid_owner.h"
-RID_Data::~RID_Data() {
-}
-
-SafeRefCount RID_OwnerBase::refcount;
-
-void RID_OwnerBase::init_rid() {
-
- refcount.init();
-}
+volatile uint64_t RID_AllocBase::base_id = 1;
diff --git a/core/rid_owner.h b/core/rid_owner.h
new file mode 100644
index 0000000000..bd01eba17d
--- /dev/null
+++ b/core/rid_owner.h
@@ -0,0 +1,406 @@
+/*************************************************************************/
+/* rid_owner.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 RID_OWNER_H
+#define RID_OWNER_H
+
+#include "core/print_string.h"
+#include "core/rid.h"
+#include "core/spin_lock.h"
+#include <stdio.h>
+#include <typeinfo>
+
+class RID_AllocBase {
+
+ static volatile uint64_t base_id;
+
+protected:
+ static RID _make_from_id(uint64_t p_id) {
+ RID rid;
+ rid._id = p_id;
+ return rid;
+ }
+
+ static uint64_t _gen_id() {
+ return atomic_increment(&base_id);
+ }
+
+ static RID _gen_rid() {
+ return _make_from_id(_gen_id());
+ }
+
+public:
+ virtual ~RID_AllocBase() {}
+};
+
+template <class T, bool THREAD_SAFE = false>
+class RID_Alloc : public RID_AllocBase {
+
+ T **chunks;
+ uint32_t **free_list_chunks;
+ uint32_t **validator_chunks;
+
+ uint32_t elements_in_chunk;
+ uint32_t max_alloc;
+ uint32_t alloc_count;
+
+ const char *description;
+
+ SpinLock spin_lock;
+
+public:
+ RID make_rid(const T &p_value) {
+
+ if (THREAD_SAFE) {
+ spin_lock.lock();
+ }
+
+ if (alloc_count == max_alloc) {
+ //allocate a new chunk
+ uint32_t chunk_count = alloc_count == 0 ? 0 : (max_alloc / elements_in_chunk);
+
+ //grow chunks
+ chunks = (T **)memrealloc(chunks, sizeof(T *) * (chunk_count + 1));
+ chunks[chunk_count] = (T *)memalloc(sizeof(T) * elements_in_chunk); //but don't initialize
+
+ //grow validators
+ validator_chunks = (uint32_t **)memrealloc(validator_chunks, sizeof(uint32_t *) * (chunk_count + 1));
+ validator_chunks[chunk_count] = (uint32_t *)memalloc(sizeof(uint32_t) * elements_in_chunk);
+ //grow free lists
+ free_list_chunks = (uint32_t **)memrealloc(free_list_chunks, sizeof(uint32_t *) * (chunk_count + 1));
+ free_list_chunks[chunk_count] = (uint32_t *)memalloc(sizeof(uint32_t) * elements_in_chunk);
+
+ //initialize
+ for (uint32_t i = 0; i < elements_in_chunk; i++) {
+ //dont initialize chunk
+ validator_chunks[chunk_count][i] = 0xFFFFFFFF;
+ free_list_chunks[chunk_count][i] = alloc_count + i;
+ }
+
+ max_alloc += elements_in_chunk;
+ }
+
+ uint32_t free_index = free_list_chunks[alloc_count / elements_in_chunk][alloc_count % elements_in_chunk];
+
+ uint32_t free_chunk = free_index / elements_in_chunk;
+ uint32_t free_element = free_index % elements_in_chunk;
+
+ T *ptr = &chunks[free_chunk][free_element];
+ memnew_placement(ptr, T(p_value));
+
+ uint32_t validator = (uint32_t)(_gen_id() & 0xFFFFFFFF);
+ uint64_t id = validator;
+ id <<= 32;
+ id |= free_index;
+
+ validator_chunks[free_chunk][free_element] = validator;
+ alloc_count++;
+
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+
+ return _make_from_id(id);
+ }
+
+ _FORCE_INLINE_ T *getornull(const RID &p_rid) {
+
+ if (THREAD_SAFE) {
+ spin_lock.lock();
+ }
+
+ uint64_t id = p_rid.get_id();
+ uint32_t idx = uint32_t(id & 0xFFFFFFFF);
+ if (unlikely(idx >= max_alloc)) {
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ return NULL;
+ }
+
+ uint32_t idx_chunk = idx / elements_in_chunk;
+ uint32_t idx_element = idx % elements_in_chunk;
+
+ uint32_t validator = uint32_t(id >> 32);
+ if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) {
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ return NULL;
+ }
+
+ T *ptr = &chunks[idx_chunk][idx_element];
+
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+
+ return ptr;
+ }
+
+ _FORCE_INLINE_ bool owns(const RID &p_rid) {
+
+ if (THREAD_SAFE) {
+ spin_lock.lock();
+ }
+
+ uint64_t id = p_rid.get_id();
+ uint32_t idx = uint32_t(id & 0xFFFFFFFF);
+ if (unlikely(idx >= max_alloc)) {
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ return false;
+ }
+
+ uint32_t idx_chunk = idx / elements_in_chunk;
+ uint32_t idx_element = idx % elements_in_chunk;
+
+ uint32_t validator = uint32_t(id >> 32);
+
+ bool owned = validator_chunks[idx_chunk][idx_element] == validator;
+
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+
+ return owned;
+ }
+
+ _FORCE_INLINE_ void free(const RID &p_rid) {
+
+ if (THREAD_SAFE) {
+ spin_lock.lock();
+ }
+
+ uint64_t id = p_rid.get_id();
+ uint32_t idx = uint32_t(id & 0xFFFFFFFF);
+ if (unlikely(idx >= max_alloc)) {
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ ERR_FAIL();
+ }
+
+ uint32_t idx_chunk = idx / elements_in_chunk;
+ uint32_t idx_element = idx % elements_in_chunk;
+
+ uint32_t validator = uint32_t(id >> 32);
+ if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) {
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ ERR_FAIL();
+ }
+
+ chunks[idx_chunk][idx_element].~T();
+ validator_chunks[idx_chunk][idx_element] = 0xFFFFFFFF; // go invalid
+
+ alloc_count--;
+ free_list_chunks[alloc_count / elements_in_chunk][alloc_count % elements_in_chunk] = idx;
+
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ }
+
+ _FORCE_INLINE_ uint32_t get_rid_count() const {
+ return alloc_count;
+ }
+
+ _FORCE_INLINE_ T *get_ptr_by_index(uint32_t p_index) {
+ ERR_FAIL_INDEX_V(p_index, alloc_count, NULL);
+ if (THREAD_SAFE) {
+ spin_lock.lock();
+ }
+ uint64_t idx = free_list_chunks[p_index / elements_in_chunk][p_index % elements_in_chunk];
+ T *ptr = &chunks[idx / elements_in_chunk][idx % elements_in_chunk];
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ return ptr;
+ }
+
+ _FORCE_INLINE_ RID get_rid_by_index(uint32_t p_index) {
+ ERR_FAIL_INDEX_V(p_index, alloc_count, RID());
+ if (THREAD_SAFE) {
+ spin_lock.lock();
+ }
+ uint64_t idx = free_list_chunks[p_index / elements_in_chunk][p_index % elements_in_chunk];
+ uint64_t validator = validator_chunks[idx / elements_in_chunk][idx % elements_in_chunk];
+
+ RID rid = _make_from_id((validator << 32) | idx);
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ return rid;
+ }
+
+ void get_owned_list(List<RID> *p_owned) {
+ if (THREAD_SAFE) {
+ spin_lock.lock();
+ }
+ for (size_t i = 0; i < max_alloc; i++) {
+ uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
+ if (validator != 0xFFFFFFFF) {
+ p_owned->push_back(_make_from_id((validator << 32) | i));
+ }
+ }
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ }
+
+ void set_description(const char *p_descrption) {
+ description = p_descrption;
+ }
+
+ RID_Alloc(uint32_t p_target_chunk_byte_size = 4096) {
+ chunks = NULL;
+ free_list_chunks = NULL;
+ validator_chunks = NULL;
+
+ elements_in_chunk = sizeof(T) > p_target_chunk_byte_size ? 1 : (p_target_chunk_byte_size / sizeof(T));
+ max_alloc = 0;
+ alloc_count = 0;
+ description = NULL;
+ }
+
+ ~RID_Alloc() {
+ if (alloc_count) {
+ if (description) {
+ print_error("ERROR: " + itos(alloc_count) + " RID allocations of type '" + description + "' were leaked at exit.");
+ } else {
+ print_error("ERROR: " + itos(alloc_count) + " RID allocations of type '" + typeid(T).name() + "' were leaked at exit.");
+ }
+
+ for (size_t i = 0; i < max_alloc; i++) {
+ uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
+ if (validator != 0xFFFFFFFF) {
+ chunks[i / elements_in_chunk][i % elements_in_chunk].~T();
+ }
+ }
+ }
+
+ uint32_t chunk_count = max_alloc / elements_in_chunk;
+ for (uint32_t i = 0; i < chunk_count; i++) {
+ memfree(chunks[i]);
+ memfree(validator_chunks[i]);
+ memfree(free_list_chunks[i]);
+ }
+
+ if (chunks) {
+ memfree(chunks);
+ memfree(free_list_chunks);
+ memfree(validator_chunks);
+ }
+ }
+};
+
+template <class T, bool THREAD_SAFE = false>
+class RID_PtrOwner {
+ RID_Alloc<T *, THREAD_SAFE> alloc;
+
+public:
+ _FORCE_INLINE_ RID make_rid(T *p_ptr) {
+ return alloc.make_rid(p_ptr);
+ }
+
+ _FORCE_INLINE_ T *getornull(const RID &p_rid) {
+ T **ptr = alloc.getornull(p_rid);
+ if (unlikely(!ptr)) {
+ return NULL;
+ }
+ return *ptr;
+ }
+
+ _FORCE_INLINE_ bool owns(const RID &p_rid) {
+ return alloc.owns(p_rid);
+ }
+
+ _FORCE_INLINE_ void free(const RID &p_rid) {
+ alloc.free(p_rid);
+ }
+
+ _FORCE_INLINE_ void get_owned_list(List<RID> *p_owned) {
+ return alloc.get_owned_list(p_owned);
+ }
+
+ void set_description(const char *p_descrption) {
+ alloc.set_description(p_descrption);
+ }
+ RID_PtrOwner(uint32_t p_target_chunk_byte_size = 4096) :
+ alloc(p_target_chunk_byte_size) {}
+};
+
+template <class T, bool THREAD_SAFE = false>
+class RID_Owner {
+ RID_Alloc<T, THREAD_SAFE> alloc;
+
+public:
+ _FORCE_INLINE_ RID make_rid(const T &p_ptr) {
+ return alloc.make_rid(p_ptr);
+ }
+
+ _FORCE_INLINE_ T *getornull(const RID &p_rid) {
+ return alloc.getornull(p_rid);
+ }
+
+ _FORCE_INLINE_ bool owns(const RID &p_rid) {
+ return alloc.owns(p_rid);
+ }
+
+ _FORCE_INLINE_ void free(const RID &p_rid) {
+ alloc.free(p_rid);
+ }
+
+ _FORCE_INLINE_ uint32_t get_rid_count() const {
+ return alloc.get_rid_count();
+ }
+
+ _FORCE_INLINE_ RID get_rid_by_index(uint32_t p_index) {
+ return alloc.get_rid_by_index(p_index);
+ }
+
+ _FORCE_INLINE_ T *get_ptr_by_index(uint32_t p_index) {
+ return alloc.get_ptr_by_index(p_index);
+ }
+
+ _FORCE_INLINE_ void get_owned_list(List<RID> *p_owned) {
+ return alloc.get_owned_list(p_owned);
+ }
+
+ void set_description(const char *p_descrption) {
+ alloc.set_description(p_descrption);
+ }
+ RID_Owner(uint32_t p_target_chunk_byte_size = 4096) :
+ alloc(p_target_chunk_byte_size) {}
+};
+#endif // RID_OWNER_H
diff --git a/core/script_language.cpp b/core/script_language.cpp
index 7392e7a0af..1149feac38 100644
--- a/core/script_language.cpp
+++ b/core/script_language.cpp
@@ -32,6 +32,7 @@
#include "core/core_string_names.h"
#include "core/project_settings.h"
+#include <stdint.h>
ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES];
int ScriptServer::_language_count = 0;
@@ -644,6 +645,14 @@ Variant PlaceHolderScriptInstance::property_get_fallback(const StringName &p_nam
return Variant();
}
+uint16_t PlaceHolderScriptInstance::get_rpc_method_id(const StringName &p_method) const {
+ return UINT16_MAX;
+}
+
+uint16_t PlaceHolderScriptInstance::get_rset_property_id(const StringName &p_method) const {
+ return UINT16_MAX;
+}
+
PlaceHolderScriptInstance::PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner) :
owner(p_owner),
language(p_language),
diff --git a/core/script_language.h b/core/script_language.h
index 22f83080bc..788f5d6976 100644
--- a/core/script_language.h
+++ b/core/script_language.h
@@ -40,6 +40,21 @@ class ScriptLanguage;
typedef void (*ScriptEditRequestFunction)(const String &p_path);
+struct ScriptNetData {
+ StringName name;
+ MultiplayerAPI::RPCMode mode;
+ bool operator==(ScriptNetData const &p_other) const {
+ return name == p_other.name;
+ }
+};
+
+struct SortNetData {
+ StringName::AlphCompare compare;
+ bool operator()(const ScriptNetData &p_a, const ScriptNetData &p_b) const {
+ return compare(p_a.name, p_b.name);
+ }
+};
+
class ScriptServer {
enum {
@@ -154,6 +169,18 @@ public:
virtual bool is_placeholder_fallback_enabled() const { return false; }
+ virtual Vector<ScriptNetData> get_rpc_methods() const = 0;
+ virtual uint16_t get_rpc_method_id(const StringName &p_method) const = 0;
+ virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const = 0;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const = 0;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const = 0;
+
+ virtual Vector<ScriptNetData> get_rset_properties() const = 0;
+ virtual uint16_t get_rset_property_id(const StringName &p_property) const = 0;
+ virtual StringName get_rset_property(const uint16_t p_rset_property_id) const = 0;
+ virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_rpc_method_id) const = 0;
+ virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const = 0;
+
Script() {}
};
@@ -195,7 +222,16 @@ public:
virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid);
virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid);
+ virtual Vector<ScriptNetData> get_rpc_methods() const = 0;
+ virtual uint16_t get_rpc_method_id(const StringName &p_method) const = 0;
+ virtual StringName get_rpc_method(uint16_t p_id) const = 0;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const = 0;
virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const = 0;
+
+ virtual Vector<ScriptNetData> get_rset_properties() const = 0;
+ virtual uint16_t get_rset_property_id(const StringName &p_variable) const = 0;
+ virtual StringName get_rset_property(uint16_t p_id) const = 0;
+ virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const = 0;
virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const = 0;
virtual ScriptLanguage *get_language() = 0;
@@ -409,7 +445,16 @@ public:
virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid = NULL);
virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid = NULL);
+ virtual Vector<ScriptNetData> get_rpc_methods() const { return Vector<ScriptNetData>(); }
+ virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
+ virtual StringName get_rpc_method(uint16_t p_id) const { return StringName(); }
+ virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const { return MultiplayerAPI::RPC_MODE_DISABLED; }
virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const { return MultiplayerAPI::RPC_MODE_DISABLED; }
+
+ virtual Vector<ScriptNetData> get_rset_properties() const { return Vector<ScriptNetData>(); }
+ virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
+ virtual StringName get_rset_property(uint16_t p_id) const { return StringName(); }
+ virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) 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);
diff --git a/core/ref_ptr.h b/core/spin_lock.h
index 4736106b4f..c48631f94a 100644
--- a/core/ref_ptr.h
+++ b/core/spin_lock.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* ref_ptr.h */
+/* spin_lock.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,35 +28,23 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef REF_PTR_H
-#define REF_PTR_H
-/**
- @author Juan Linietsky <reduzio@gmail.com>
- * This class exists to workaround a limitation in C++ but keep the design OK.
- * It's basically an opaque container of a Reference reference, so Variant can use it.
-*/
+#ifndef SPIN_LOCK_H
+#define SPIN_LOCK_H
-#include "core/rid.h"
+#include "core/typedefs.h"
+#include <atomic>
-class RefPtr {
+class SpinLock {
+ std::atomic_flag locked = ATOMIC_FLAG_INIT;
- enum {
-
- DATASIZE = sizeof(void *) //*4 -ref was shrunk
- };
-
- mutable char data[DATASIZE]; // too much probably, virtual class + pointer
public:
- bool is_null() const;
- void operator=(const RefPtr &p_other);
- bool operator==(const RefPtr &p_other) const;
- bool operator!=(const RefPtr &p_other) const;
- RID get_rid() const;
- void unref();
- _FORCE_INLINE_ void *get_data() const { return data; }
- RefPtr(const RefPtr &p_other);
- RefPtr();
- ~RefPtr();
+ _ALWAYS_INLINE_ void lock() {
+ while (locked.test_and_set(std::memory_order_acquire)) {
+ ;
+ }
+ }
+ _ALWAYS_INLINE_ void unlock() {
+ locked.clear(std::memory_order_release);
+ }
};
-
-#endif // REF_PTR_H
+#endif // SPIN_LOCK_H
diff --git a/core/ref_ptr.cpp b/core/thread_work_pool.cpp
index 7e35bcc56c..c8311f102f 100644
--- a/core/ref_ptr.cpp
+++ b/core/thread_work_pool.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* ref_ptr.cpp */
+/* thread_work_pool.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,76 +28,56 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "ref_ptr.h"
+#include "thread_work_pool.h"
+#include "core/os/os.h"
-#include "core/reference.h"
-#include "core/resource.h"
+void ThreadWorkPool::_thread_function(ThreadData *p_thread) {
-void RefPtr::operator=(const RefPtr &p_other) {
-
- Ref<Reference> *ref = reinterpret_cast<Ref<Reference> *>(&data[0]);
- Ref<Reference> *ref_other = reinterpret_cast<Ref<Reference> *>(const_cast<char *>(&p_other.data[0]));
-
- *ref = *ref_other;
-}
-
-bool RefPtr::operator==(const RefPtr &p_other) const {
-
- Ref<Reference> *ref = reinterpret_cast<Ref<Reference> *>(&data[0]);
- Ref<Reference> *ref_other = reinterpret_cast<Ref<Reference> *>(const_cast<char *>(&p_other.data[0]));
-
- return *ref == *ref_other;
-}
-
-bool RefPtr::operator!=(const RefPtr &p_other) const {
-
- Ref<Reference> *ref = reinterpret_cast<Ref<Reference> *>(&data[0]);
- Ref<Reference> *ref_other = reinterpret_cast<Ref<Reference> *>(const_cast<char *>(&p_other.data[0]));
-
- return *ref != *ref_other;
+ while (true) {
+ p_thread->start.wait();
+ if (p_thread->exit.load()) {
+ break;
+ }
+ p_thread->work->work();
+ p_thread->completed.post();
+ }
}
-RefPtr::RefPtr(const RefPtr &p_other) {
-
- memnew_placement(&data[0], Ref<Reference>);
-
- Ref<Reference> *ref = reinterpret_cast<Ref<Reference> *>(&data[0]);
- Ref<Reference> *ref_other = reinterpret_cast<Ref<Reference> *>(const_cast<char *>(&p_other.data[0]));
-
- *ref = *ref_other;
-}
+void ThreadWorkPool::init(int p_thread_count) {
+ ERR_FAIL_COND(threads != nullptr);
+ if (p_thread_count < 0) {
+ p_thread_count = OS::get_singleton()->get_processor_count();
+ }
-bool RefPtr::is_null() const {
+ thread_count = p_thread_count;
+ threads = memnew_arr(ThreadData, thread_count);
- Ref<Reference> *ref = reinterpret_cast<Ref<Reference> *>(&data[0]);
- return ref->is_null();
+ for (uint32_t i = 0; i < thread_count; i++) {
+ threads[i].exit.store(false);
+ threads[i].thread = memnew(std::thread(ThreadWorkPool::_thread_function, &threads[i]));
+ }
}
-RID RefPtr::get_rid() const {
+void ThreadWorkPool::finish() {
- Ref<Reference> *ref = reinterpret_cast<Ref<Reference> *>(&data[0]);
- if (ref->is_null())
- return RID();
- Resource *res = Object::cast_to<Resource>(ref->ptr());
- if (res)
- return res->get_rid();
- return RID();
-}
-
-void RefPtr::unref() {
-
- Ref<Reference> *ref = reinterpret_cast<Ref<Reference> *>(&data[0]);
- ref->unref();
-}
+ if (threads == nullptr) {
+ return;
+ }
-RefPtr::RefPtr() {
+ for (uint32_t i = 0; i < thread_count; i++) {
+ threads[i].exit.store(true);
+ threads[i].start.post();
+ }
+ for (uint32_t i = 0; i < thread_count; i++) {
+ threads[i].thread->join();
+ memdelete(threads[i].thread);
+ }
- ERR_FAIL_COND(sizeof(Ref<Reference>) > DATASIZE);
- memnew_placement(&data[0], Ref<Reference>);
+ memdelete_arr(threads);
+ threads = nullptr;
}
-RefPtr::~RefPtr() {
+ThreadWorkPool::~ThreadWorkPool() {
- Ref<Reference> *ref = reinterpret_cast<Ref<Reference> *>(&data[0]);
- ref->~Ref<Reference>();
+ finish();
}
diff --git a/core/thread_work_pool.h b/core/thread_work_pool.h
new file mode 100644
index 0000000000..214d2c4aa7
--- /dev/null
+++ b/core/thread_work_pool.h
@@ -0,0 +1,111 @@
+/*************************************************************************/
+/* thread_work_pool.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 THREAD_WORK_POOL_H
+#define THREAD_WORK_POOL_H
+
+#include "core/os/memory.h"
+#include "core/os/semaphore.h"
+#include <atomic>
+#include <thread>
+class ThreadWorkPool {
+
+ std::atomic<uint32_t> index;
+
+ struct BaseWork {
+ std::atomic<uint32_t> *index;
+ uint32_t max_elements;
+ virtual void work() = 0;
+ virtual ~BaseWork() = default;
+ };
+
+ template <class C, class M, class U>
+ struct Work : public BaseWork {
+ C *instance;
+ M method;
+ U userdata;
+ virtual void work() {
+
+ while (true) {
+ uint32_t work_index = index->fetch_add(1, std::memory_order_relaxed);
+ if (work_index >= max_elements) {
+ break;
+ }
+ (instance->*method)(work_index, userdata);
+ }
+ }
+ };
+
+ struct ThreadData {
+ std::thread *thread;
+ Semaphore start;
+ Semaphore completed;
+ std::atomic<bool> exit;
+ BaseWork *work;
+ };
+
+ ThreadData *threads = nullptr;
+ uint32_t thread_count = 0;
+
+ static void _thread_function(ThreadData *p_thread);
+
+public:
+ template <class C, class M, class U>
+ void do_work(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) {
+
+ ERR_FAIL_COND(!threads); //never initialized
+
+ index.store(0);
+
+ Work<C, M, U> *w = memnew((Work<C, M, U>));
+ w->instance = p_instance;
+ w->userdata = p_userdata;
+ w->method = p_method;
+ w->index = &index;
+ w->max_elements = p_elements;
+
+ for (uint32_t i = 0; i < thread_count; i++) {
+ threads[i].work = w;
+ threads[i].start.post();
+ }
+ for (uint32_t i = 0; i < thread_count; i++) {
+ threads[i].completed.wait();
+ threads[i].work = nullptr;
+ }
+
+ memdelete(w);
+ }
+
+ void init(int p_thread_count = -1);
+ void finish();
+ ~ThreadWorkPool();
+};
+
+#endif // THREAD_POOL_H
diff --git a/core/translation.cpp b/core/translation.cpp
index 02297cffc8..cf76de1c9e 100644
--- a/core/translation.cpp
+++ b/core/translation.cpp
@@ -966,7 +966,7 @@ void TranslationServer::set_locale(const String &p_locale) {
print_verbose(vformat("Unsupported locale '%s', falling back to '%s'.", p_locale, trimmed_locale));
if (!is_locale_valid(trimmed_locale)) {
- ERR_PRINTS(vformat("Unsupported locale '%s', falling back to 'en'.", trimmed_locale));
+ ERR_PRINT(vformat("Unsupported locale '%s', falling back to 'en'.", trimmed_locale));
locale = "en";
} else {
locale = trimmed_locale;
diff --git a/core/type_info.h b/core/type_info.h
index c9b2055241..9ca6d7fe73 100644
--- a/core/type_info.h
+++ b/core/type_info.h
@@ -169,21 +169,13 @@ MAKE_TYPE_INFO(IP_Address, Variant::STRING)
class BSP_Tree;
MAKE_TYPE_INFO(BSP_Tree, Variant::DICTIONARY)
-//for RefPtr
+//objectID
template <>
-struct GetTypeInfo<RefPtr> {
- static const Variant::Type VARIANT_TYPE = Variant::OBJECT;
- static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;
- static inline PropertyInfo get_class_info() {
- return PropertyInfo(Variant::OBJECT, String(), PROPERTY_HINT_RESOURCE_TYPE, "Reference");
- }
-};
-template <>
-struct GetTypeInfo<const RefPtr &> {
- static const Variant::Type VARIANT_TYPE = Variant::OBJECT;
+struct GetTypeInfo<ObjectID> {
+ static const Variant::Type VARIANT_TYPE = Variant::INT;
static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;
static inline PropertyInfo get_class_info() {
- return PropertyInfo(Variant::OBJECT, String(), PROPERTY_HINT_RESOURCE_TYPE, "Reference");
+ return PropertyInfo(Variant::INT, String(), PROPERTY_HINT_INT_IS_OBJECTID);
}
};
@@ -277,7 +269,7 @@ struct GetTypeInfo<const T *, typename EnableIf<TypeInherits<Object, T>::value>:
template <typename T>
inline StringName __constant_get_enum_name(T param, const String &p_constant) {
if (GetTypeInfo<T>::VARIANT_TYPE == Variant::NIL)
- ERR_PRINTS("Missing VARIANT_ENUM_CAST for constant's enum: " + p_constant);
+ ERR_PRINT("Missing VARIANT_ENUM_CAST for constant's enum: " + p_constant);
return GetTypeInfo<T>::get_class_info().class_name;
}
diff --git a/core/undo_redo.cpp b/core/undo_redo.cpp
index 5d1144e1f5..577879d448 100644
--- a/core/undo_redo.cpp
+++ b/core/undo_redo.cpp
@@ -293,7 +293,7 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) {
Variant::CallError ce;
obj->call(op.name, (const Variant **)argptrs.ptr(), argc, ce);
if (ce.error != Variant::CallError::CALL_OK) {
- ERR_PRINTS("Error calling method from signal '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, (const Variant **)argptrs.ptr(), argc, ce));
+ ERR_PRINT("Error calling method from signal '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, (const Variant **)argptrs.ptr(), argc, ce));
}
#ifdef TOOLS_ENABLED
Resource *res = Object::cast_to<Resource>(obj);
diff --git a/core/ustring.cpp b/core/ustring.cpp
index 8a1fbfd383..c4543b89da 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -146,9 +146,11 @@ void CharString::copy_from(const char *p_cstr) {
return;
}
- resize(len + 1); // include terminating null char
+ Error err = resize(++len); // include terminating null char
- strcpy(ptrw(), p_cstr);
+ ERR_FAIL_COND_MSG(err != OK, "Failed to copy C-string.");
+
+ memcpy(ptrw(), p_cstr, len);
}
void String::copy_from(const char *p_cstr) {
@@ -644,6 +646,17 @@ String String::camelcase_to_underscore(bool lowercase) const {
return lowercase ? new_string.to_lower() : new_string;
}
+String String::get_with_code_lines() const {
+ Vector<String> lines = split("\n");
+ String ret;
+ for (int i = 0; i < lines.size(); i++) {
+ if (i > 0) {
+ ret += "\n";
+ }
+ ret += itos(i + 1) + " " + lines[i];
+ }
+ return ret;
+}
int String::get_slice_count(String p_splitter) const {
if (empty())
@@ -3324,7 +3337,7 @@ String String::humanize_size(uint64_t p_size) {
int prefix_idx = 0;
- while (prefix_idx < prefixes.size() && p_size > (_div * 1024)) {
+ while (prefix_idx < prefixes.size() - 1 && p_size > (_div * 1024)) {
_div *= 1024;
prefix_idx++;
}
diff --git a/core/ustring.h b/core/ustring.h
index 5bf73001aa..e70b2bfe27 100644
--- a/core/ustring.h
+++ b/core/ustring.h
@@ -258,6 +258,7 @@ public:
String capitalize() const;
String camelcase_to_underscore(bool lowercase = true) const;
+ String get_with_code_lines() const;
int get_slice_count(String p_splitter) const;
String get_slice(String p_splitter, int p_slice) const;
String get_slicec(CharType p_splitter, int p_slice) const;
diff --git a/core/variant.cpp b/core/variant.cpp
index f4e4cd5341..c2ffe3721f 100644
--- a/core/variant.cpp
+++ b/core/variant.cpp
@@ -908,6 +908,14 @@ bool Variant::is_one() const {
return false;
}
+bool Variant::is_null() const {
+ if (type == OBJECT && _get_obj().obj) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
void Variant::reference(const Variant &p_variant) {
switch (type) {
@@ -999,7 +1007,20 @@ void Variant::reference(const Variant &p_variant) {
} break;
case OBJECT: {
- memnew_placement(_data._mem, ObjData(p_variant._get_obj()));
+ memnew_placement(_data._mem, ObjData);
+
+ if (p_variant._get_obj().obj && p_variant._get_obj().id.is_reference()) {
+ Reference *reference = static_cast<Reference *>(p_variant._get_obj().obj);
+ if (!reference->reference()) {
+ _get_obj().obj = nullptr;
+ _get_obj().id = ObjectID();
+ break;
+ }
+ }
+
+ _get_obj().obj = const_cast<Object *>(p_variant._get_obj().obj);
+ _get_obj().id = p_variant._get_obj().id;
+
} break;
case NODE_PATH: {
@@ -1114,8 +1135,15 @@ void Variant::clear() {
} break;
case OBJECT: {
+ if (_get_obj().id.is_reference()) {
+ //we are safe that there is a reference here
+ Reference *reference = static_cast<Reference *>(_get_obj().obj);
+ if (reference->unreference()) {
+ memdelete(reference);
+ }
+ }
_get_obj().obj = NULL;
- _get_obj().ref.unref();
+ _get_obj().id = ObjectID();
} break;
case _RID: {
// not much need probably
@@ -1248,6 +1276,14 @@ Variant::operator uint64_t() const {
}
}
+Variant::operator ObjectID() const {
+ if (type == INT) {
+ return ObjectID(_data._int);
+ } else {
+ return ObjectID();
+ }
+}
+
#ifdef NEED_LONG_INT
Variant::operator signed long() const {
@@ -1581,14 +1617,11 @@ String Variant::stringify(List<const void *> &stack) const {
case OBJECT: {
if (_get_obj().obj) {
-#ifdef DEBUG_ENABLED
- if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) {
- //only if debugging!
- if (!ObjectDB::instance_validate(_get_obj().obj)) {
- return "[Deleted Object]";
- };
+
+ if (!_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ return "[Freed Object]";
};
-#endif
+
return _get_obj().obj->to_string();
} else
return "[Object:null]";
@@ -1731,24 +1764,16 @@ Variant::operator NodePath() const {
return NodePath();
}
-Variant::operator RefPtr() const {
-
- if (type == OBJECT)
- return _get_obj().ref;
- else
- return RefPtr();
-}
-
Variant::operator RID() const {
if (type == _RID)
return *reinterpret_cast<const RID *>(_data._mem);
- else if (type == OBJECT && !_get_obj().ref.is_null()) {
- return _get_obj().ref.get_rid();
+ else if (type == OBJECT && _get_obj().obj == nullptr) {
+ return RID();
} else if (type == OBJECT && _get_obj().obj) {
#ifdef DEBUG_ENABLED
if (ScriptDebugger::get_singleton()) {
- ERR_FAIL_COND_V_MSG(!ObjectDB::instance_validate(_get_obj().obj), RID(), "Invalid pointer (object was deleted).");
+ ERR_FAIL_COND_V_MSG(ObjectDB::get_instance(_get_obj().id) == nullptr, RID(), "Invalid pointer (object was freed).");
};
#endif
Variant::CallError ce;
@@ -1769,6 +1794,25 @@ Variant::operator Object *() const {
else
return NULL;
}
+
+Object *Variant::get_validated_object_with_check(bool &r_previously_freed) const {
+ if (type == OBJECT) {
+ Object *instance = ObjectDB::get_instance(_get_obj().id);
+ r_previously_freed = !instance && _get_obj().id != ObjectID();
+ return instance;
+ } else {
+ r_previously_freed = false;
+ return NULL;
+ }
+}
+
+Object *Variant::get_validated_object() const {
+ if (type == OBJECT)
+ return ObjectDB::get_instance(_get_obj().id);
+ else
+ return NULL;
+}
+
Variant::operator Node *() const {
if (type == OBJECT)
@@ -2193,6 +2237,11 @@ Variant::Variant(double p_double) {
_data._real = p_double;
}
+Variant::Variant(const ObjectID &p_id) {
+ type = INT;
+ _data._int = p_id;
+}
+
Variant::Variant(const StringName &p_string) {
type = STRING;
@@ -2276,15 +2325,6 @@ Variant::Variant(const NodePath &p_node_path) {
memnew_placement(_data._mem, NodePath(p_node_path));
}
-Variant::Variant(const RefPtr &p_resource) {
-
- type = OBJECT;
- memnew_placement(_data._mem, ObjData);
- REF *ref = reinterpret_cast<REF *>(p_resource.get_data());
- _get_obj().obj = ref->ptr();
- _get_obj().ref = p_resource;
-}
-
Variant::Variant(const RID &p_rid) {
type = _RID;
@@ -2296,7 +2336,24 @@ Variant::Variant(const Object *p_object) {
type = OBJECT;
memnew_placement(_data._mem, ObjData);
- _get_obj().obj = const_cast<Object *>(p_object);
+
+ if (p_object) {
+
+ if (p_object->is_reference()) {
+ Reference *reference = const_cast<Reference *>(static_cast<const Reference *>(p_object));
+ if (!reference->init_ref()) {
+ _get_obj().obj = nullptr;
+ _get_obj().id = ObjectID();
+ return;
+ }
+ }
+
+ _get_obj().obj = const_cast<Object *>(p_object);
+ _get_obj().id = p_object->get_instance_id();
+ } else {
+ _get_obj().obj = nullptr;
+ _get_obj().id = ObjectID();
+ }
}
Variant::Variant(const Dictionary &p_dictionary) {
@@ -2607,7 +2664,26 @@ void Variant::operator=(const Variant &p_variant) {
} break;
case OBJECT: {
- *reinterpret_cast<ObjData *>(_data._mem) = p_variant._get_obj();
+ if (_get_obj().id.is_reference()) {
+ //we are safe that there is a reference here
+ Reference *reference = static_cast<Reference *>(_get_obj().obj);
+ if (reference->unreference()) {
+ memdelete(reference);
+ }
+ }
+
+ if (p_variant._get_obj().obj && p_variant._get_obj().id.is_reference()) {
+ Reference *reference = static_cast<Reference *>(p_variant._get_obj().obj);
+ if (!reference->reference()) {
+ _get_obj().obj = nullptr;
+ _get_obj().id = ObjectID();
+ break;
+ }
+ }
+
+ _get_obj().obj = const_cast<Object *>(p_variant._get_obj().obj);
+ _get_obj().id = p_variant._get_obj().id;
+
} break;
case NODE_PATH: {
@@ -3118,7 +3194,7 @@ bool Variant::hash_compare(const Variant &p_variant) const {
bool Variant::is_ref() const {
- return type == OBJECT && !_get_obj().ref.is_null();
+ return type == OBJECT && _get_obj().id.is_reference();
}
Vector<Variant> varray() {
diff --git a/core/variant.h b/core/variant.h
index 06be914408..bb3840932d 100644
--- a/core/variant.h
+++ b/core/variant.h
@@ -44,12 +44,11 @@
#include "core/math/transform_2d.h"
#include "core/math/vector3.h"
#include "core/node_path.h"
+#include "core/object_id.h"
#include "core/pool_vector.h"
-#include "core/ref_ptr.h"
#include "core/rid.h"
#include "core/ustring.h"
-class RefPtr;
class Object;
class Node; // helper
class Control; // helper
@@ -127,12 +126,12 @@ private:
struct ObjData {
+ ObjectID id;
Object *obj;
- RefPtr ref;
};
- _FORCE_INLINE_ ObjData &_get_obj();
- _FORCE_INLINE_ const ObjData &_get_obj() const;
+ _ALWAYS_INLINE_ ObjData &_get_obj();
+ _ALWAYS_INLINE_ const ObjData &_get_obj() const;
union {
bool _bool;
@@ -161,6 +160,7 @@ public:
bool is_shared() const;
bool is_zero() const;
bool is_one() const;
+ bool is_null() const;
operator bool() const;
operator signed int() const;
@@ -177,6 +177,8 @@ public:
operator unsigned long() const;
#endif
+ operator ObjectID() const;
+
operator CharType() const;
operator float() const;
operator double() const;
@@ -194,7 +196,6 @@ public:
operator Color() const;
operator NodePath() const;
- operator RefPtr() const;
operator RID() const;
operator Object *() const;
@@ -232,6 +233,9 @@ public:
operator IP_Address() const;
+ Object *get_validated_object() const;
+ Object *get_validated_object_with_check(bool &r_previously_freed) const;
+
Variant(bool p_bool);
Variant(signed int p_int); // real one
Variant(unsigned int p_int);
@@ -248,6 +252,7 @@ public:
Variant(uint64_t p_int);
Variant(float p_float);
Variant(double p_double);
+ Variant(const ObjectID &p_id);
Variant(const String &p_string);
Variant(const StringName &p_string);
Variant(const char *const p_cstring);
@@ -263,7 +268,6 @@ public:
Variant(const Transform &p_transform);
Variant(const Color &p_color);
Variant(const NodePath &p_node_path);
- Variant(const RefPtr &p_resource);
Variant(const RID &p_rid);
Variant(const Object *p_object);
Variant(const Dictionary &p_dictionary);
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index 6b5b947185..ac995d1c78 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -488,7 +488,6 @@ struct _VariantCall {
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);
VCALL_LOCALMEM2R(Color, linear_interpolate);
@@ -1100,12 +1099,9 @@ void Variant::call_ptr(const StringName &p_method, const Variant **p_args, int p
return;
}
#ifdef DEBUG_ENABLED
- if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) {
- //only if debugging!
- if (!ObjectDB::instance_validate(obj)) {
- r_error.error = CallError::CALL_ERROR_INSTANCE_IS_NULL;
- return;
- }
+ if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ r_error.error = CallError::CALL_ERROR_INSTANCE_IS_NULL;
+ return;
}
#endif
@@ -1275,18 +1271,11 @@ Variant Variant::construct(const Variant::Type p_type, const Variant **p_args, i
bool Variant::has_method(const StringName &p_method) const {
if (type == OBJECT) {
- Object *obj = operator Object *();
+ Object *obj = get_validated_object();
if (!obj)
return false;
-#ifdef DEBUG_ENABLED
- if (ScriptDebugger::get_singleton()) {
- if (ObjectDB::instance_validate(obj)) {
-#endif
- return obj->has_method(p_method);
-#ifdef DEBUG_ENABLED
- }
- }
-#endif
+
+ return obj->has_method(p_method);
}
const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[type];
@@ -1737,7 +1726,6 @@ void register_variant_methods() {
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());
ADDFUNC2R(COLOR, COLOR, Color, linear_interpolate, COLOR, "b", REAL, "t", varray());
diff --git a/core/variant_op.cpp b/core/variant_op.cpp
index 6caa224cfe..c7a52b0347 100644
--- a/core/variant_op.cpp
+++ b/core/variant_op.cpp
@@ -1515,7 +1515,7 @@ void Variant::set_named(const StringName &p_index, const Variant &p_value, bool
#ifdef DEBUG_ENABLED
if (!_get_obj().obj) {
break;
- } else if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null() && !ObjectDB::instance_validate(_get_obj().obj)) {
+ } else if (ScriptDebugger::get_singleton() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
break;
}
@@ -1684,7 +1684,7 @@ Variant Variant::get_named(const StringName &p_index, bool *r_valid) const {
return "Instance base is null.";
} else {
- if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null() && !ObjectDB::instance_validate(_get_obj().obj)) {
+ if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
if (r_valid)
*r_valid = false;
return "Attempted use of stray pointer object.";
@@ -2172,13 +2172,11 @@ void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid)
if (obj) {
#ifdef DEBUG_ENABLED
- if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) {
+ if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
- if (!ObjectDB::instance_validate(obj)) {
- WARN_PRINT("Attempted use of stray pointer object.");
- valid = false;
- return;
- }
+ WARN_PRINT("Attempted use of previously freed pointer object.");
+ valid = false;
+ return;
}
#endif
@@ -2546,12 +2544,10 @@ Variant Variant::get(const Variant &p_index, bool *r_valid) const {
if (obj) {
#ifdef DEBUG_ENABLED
- if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) {
- //only if debugging!
- if (!ObjectDB::instance_validate(obj)) {
- valid = false;
- return "Attempted get on stray pointer.";
- }
+
+ if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ valid = false;
+ return "Attempted get on previously freed instance.";
}
#endif
@@ -2611,15 +2607,14 @@ bool Variant::in(const Variant &p_index, bool *r_valid) const {
bool valid = false;
#ifdef DEBUG_ENABLED
- if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) {
- //only if debugging!
- if (!ObjectDB::instance_validate(obj)) {
- if (r_valid) {
- *r_valid = false;
- }
- return true; // Attempted get on stray pointer.
+
+ if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ if (r_valid) {
+ *r_valid = false;
}
+ return true; // Attempted get on stray pointer.
}
+
#endif
if (p_index.get_type() != Variant::STRING) {
@@ -2883,13 +2878,12 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const {
Object *obj = _get_obj().obj;
if (obj) {
#ifdef DEBUG_ENABLED
- if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) {
- //only if debugging!
- if (!ObjectDB::instance_validate(obj)) {
- WARN_PRINT("Attempted get_property list on stray pointer.");
- return;
- }
+
+ if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ WARN_PRINT("Attempted get_property list on previously freed instance.");
+ return;
}
+
#endif
obj->get_property_list(p_list);
@@ -2961,16 +2955,18 @@ bool Variant::iter_init(Variant &r_iter, bool &valid) const {
} break;
case OBJECT: {
-#ifdef DEBUG_ENABLED
if (!_get_obj().obj) {
valid = false;
return false;
}
- if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null() && !ObjectDB::instance_validate(_get_obj().obj)) {
+#ifdef DEBUG_ENABLED
+
+ if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
valid = false;
return false;
}
+
#endif
Variant::CallError ce;
ce.error = Variant::CallError::CALL_OK;
@@ -3129,16 +3125,18 @@ bool Variant::iter_next(Variant &r_iter, bool &valid) const {
} break;
case OBJECT: {
-#ifdef DEBUG_ENABLED
if (!_get_obj().obj) {
valid = false;
return false;
}
- if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null() && !ObjectDB::instance_validate(_get_obj().obj)) {
+#ifdef DEBUG_ENABLED
+
+ if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
valid = false;
return false;
}
+
#endif
Variant::CallError ce;
ce.error = Variant::CallError::CALL_OK;
@@ -3288,16 +3286,16 @@ Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const {
} break;
case OBJECT: {
-#ifdef DEBUG_ENABLED
if (!_get_obj().obj) {
r_valid = false;
return Variant();
}
-
- if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null() && !ObjectDB::instance_validate(_get_obj().obj)) {
+#ifdef DEBUG_ENABLED
+ if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
r_valid = false;
return Variant();
}
+
#endif
Variant::CallError ce;
ce.error = Variant::CallError::CALL_OK;
diff --git a/core/variant_parser.cpp b/core/variant_parser.cpp
index 6ca9d6c246..996b25308e 100644
--- a/core/variant_parser.cpp
+++ b/core/variant_parser.cpp
@@ -51,10 +51,16 @@ bool VariantParser::StreamFile::is_eof() const {
CharType VariantParser::StreamString::get_char() {
- if (pos >= s.length())
+ if (pos > s.length()) {
return 0;
- else
+ } else if (pos == s.length()) {
+ // You need to try to read again when you have reached the end for EOF to be reported,
+ // so this works the same as files (like StreamFile does)
+ pos++;
+ return 0;
+ } else {
return s[pos++];
+ }
}
bool VariantParser::StreamString::is_utf8() const {
@@ -229,8 +235,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
case 'f': res = 12; break;
case 'r': res = 13; break;
case 'u': {
- //hexnumbarh - oct is deprecated
-
+ //hex number
for (int j = 0; j < 4; j++) {
CharType c = p_stream->get_char();
if (c == 0) {
@@ -254,7 +259,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
v = c - 'A';
v += 10;
} else {
- ERR_PRINT("BUG");
+ ERR_PRINT("Bug parsing hex constant.");
v = 0;
}
@@ -263,13 +268,8 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
}
} break;
- //case '\"': res='\"'; break;
- //case '\\': res='\\'; break;
- //case '/': res='/'; break;
default: {
res = next;
- //r_err_str="Invalid escape sequence";
- //return ERR_PARSE_ERROR;
} break;
}
@@ -860,198 +860,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
return ERR_PARSE_ERROR;
}
}
-#ifndef DISABLE_DEPRECATED
- } else if (id == "InputEvent") {
-
- get_token(p_stream, token, line, r_err_str);
- if (token.type != TK_PARENTHESIS_OPEN) {
- r_err_str = "Expected '('";
- return ERR_PARSE_ERROR;
- }
-
- get_token(p_stream, token, line, r_err_str);
-
- if (token.type != TK_IDENTIFIER) {
- r_err_str = "Expected identifier";
- return ERR_PARSE_ERROR;
- }
-
- String id2 = token.value;
-
- Ref<InputEvent> ie;
-
- if (id2 == "NONE") {
-
- 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 (id2 == "KEY") {
-
- Ref<InputEventKey> key;
- key.instance();
- ie = key;
-
- get_token(p_stream, token, line, r_err_str);
- if (token.type != TK_COMMA) {
- r_err_str = "Expected ','";
- return ERR_PARSE_ERROR;
- }
-
- get_token(p_stream, token, line, r_err_str);
- if (token.type == TK_IDENTIFIER) {
- String name = token.value;
- key->set_scancode(find_keycode(name));
- } else if (token.type == TK_NUMBER) {
-
- key->set_scancode(token.value);
- } else {
-
- r_err_str = "Expected string or integer for keycode";
- return ERR_PARSE_ERROR;
- }
-
- get_token(p_stream, token, line, r_err_str);
-
- if (token.type == TK_COMMA) {
-
- get_token(p_stream, token, line, r_err_str);
-
- if (token.type != TK_IDENTIFIER) {
- r_err_str = "Expected identifier with modifier flas";
- return ERR_PARSE_ERROR;
- }
-
- String mods = token.value;
-
- if (mods.findn("C") != -1)
- key->set_control(true);
- if (mods.findn("A") != -1)
- key->set_alt(true);
- if (mods.findn("S") != -1)
- key->set_shift(true);
- if (mods.findn("M") != -1)
- key->set_metakey(true);
-
- 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 (token.type != TK_PARENTHESIS_CLOSE) {
-
- r_err_str = "Expected ')' or modifier flags.";
- return ERR_PARSE_ERROR;
- }
-
- } else if (id2 == "MBUTTON") {
-
- Ref<InputEventMouseButton> mb;
- mb.instance();
- ie = mb;
-
- get_token(p_stream, token, line, r_err_str);
- if (token.type != TK_COMMA) {
- r_err_str = "Expected ','";
- return ERR_PARSE_ERROR;
- }
-
- get_token(p_stream, token, line, r_err_str);
- if (token.type != TK_NUMBER) {
- r_err_str = "Expected button index";
- return ERR_PARSE_ERROR;
- }
-
- mb->set_button_index(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 (id2 == "JBUTTON") {
-
- Ref<InputEventJoypadButton> jb;
- jb.instance();
- ie = jb;
-
- get_token(p_stream, token, line, r_err_str);
- if (token.type != TK_COMMA) {
- r_err_str = "Expected ','";
- return ERR_PARSE_ERROR;
- }
-
- get_token(p_stream, token, line, r_err_str);
- if (token.type != TK_NUMBER) {
- r_err_str = "Expected button index";
- return ERR_PARSE_ERROR;
- }
-
- jb->set_button_index(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 (id2 == "JAXIS") {
-
- Ref<InputEventJoypadMotion> jm;
- jm.instance();
- ie = jm;
-
- get_token(p_stream, token, line, r_err_str);
- if (token.type != TK_COMMA) {
- r_err_str = "Expected ','";
- return ERR_PARSE_ERROR;
- }
-
- get_token(p_stream, token, line, r_err_str);
- if (token.type != TK_NUMBER) {
- r_err_str = "Expected axis index";
- return ERR_PARSE_ERROR;
- }
-
- jm->set_axis(token.value);
-
- get_token(p_stream, token, line, r_err_str);
-
- if (token.type != TK_COMMA) {
- r_err_str = "Expected ',' after axis index";
- return ERR_PARSE_ERROR;
- }
-
- get_token(p_stream, token, line, r_err_str);
- if (token.type != TK_NUMBER) {
- r_err_str = "Expected axis sign";
- return ERR_PARSE_ERROR;
- }
-
- jm->set_axis_value(token.value);
-
- get_token(p_stream, token, line, r_err_str);
-
- if (token.type != TK_PARENTHESIS_CLOSE) {
- r_err_str = "Expected ')' for jaxis";
- return ERR_PARSE_ERROR;
- }
-
- } else {
-
- r_err_str = "Invalid input event type.";
- return ERR_PARSE_ERROR;
- }
-
- value = ie;
- return OK;
-#endif
} else if (id == "PoolByteArray" || id == "ByteArray") {
Vector<uint8_t> args;