summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/bind/core_bind.cpp13
-rw-r--r--core/bind/core_bind.h9
-rw-r--r--core/command_queue_mt.cpp4
-rw-r--r--core/command_queue_mt.h19
-rw-r--r--core/engine.cpp1
-rw-r--r--core/error_macros.h34
-rw-r--r--core/image.cpp10
-rw-r--r--core/io/file_access_zip.cpp6
-rw-r--r--core/io/http_client.cpp98
-rw-r--r--core/io/http_client.h1
-rw-r--r--core/io/logger.h12
-rw-r--r--core/io/marshalls.cpp6
-rw-r--r--core/io/packet_peer.cpp2
-rw-r--r--core/io/resource_format_binary.cpp51
-rw-r--r--core/io/resource_format_binary.h9
-rw-r--r--core/io/resource_importer.cpp52
-rw-r--r--core/io/resource_importer.h18
-rw-r--r--core/io/resource_loader.cpp46
-rw-r--r--core/io/resource_loader.h20
-rw-r--r--core/io/resource_saver.cpp6
-rw-r--r--core/math/a_star.cpp23
-rw-r--r--core/math/basis.cpp68
-rw-r--r--core/math/expression.cpp62
-rw-r--r--core/math/face3.h10
-rw-r--r--core/math/math_defs.h9
-rw-r--r--core/math/math_funcs.h30
-rw-r--r--core/math/octree.h20
-rw-r--r--core/math/quat.cpp2
-rw-r--r--core/math/quick_hull.cpp14
-rw-r--r--core/math/quick_hull.h2
-rw-r--r--core/math/random_pcg.h5
-rw-r--r--core/math/vector2.cpp2
-rw-r--r--core/math/vector3.h2
-rw-r--r--core/object.cpp32
-rw-r--r--core/object.h1
-rw-r--r--core/os/input_event.cpp14
-rw-r--r--core/os/input_event.h1
-rw-r--r--core/os/os.cpp11
-rw-r--r--core/os/os.h8
-rw-r--r--core/project_settings.cpp6
-rw-r--r--core/script_debugger_remote.cpp9
-rw-r--r--core/script_debugger_remote.h4
-rw-r--r--core/script_language.cpp4
-rw-r--r--core/translation.cpp2
-rw-r--r--core/typedefs.h10
-rw-r--r--core/ustring.cpp21
-rw-r--r--core/ustring.h8
-rw-r--r--core/variant.h10
-rw-r--r--core/variant_call.cpp36
-rw-r--r--core/variant_op.cpp18
-rw-r--r--core/variant_parser.cpp34
-rw-r--r--core/version.h5
52 files changed, 587 insertions, 313 deletions
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index f17d7372e2..f6828ea76a 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -225,8 +225,12 @@ int _OS::get_video_driver_count() const {
return OS::get_singleton()->get_video_driver_count();
}
-String _OS::get_video_driver_name(int p_driver) const {
- return OS::get_singleton()->get_video_driver_name(p_driver);
+String _OS::get_video_driver_name(VideoDriver p_driver) const {
+ return OS::get_singleton()->get_video_driver_name((int)p_driver);
+}
+
+_OS::VideoDriver _OS::get_current_video_driver() const {
+ return (VideoDriver)OS::get_singleton()->get_current_video_driver();
}
int _OS::get_audio_driver_count() const {
@@ -1108,6 +1112,8 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_video_driver_count"), &_OS::get_video_driver_count);
ClassDB::bind_method(D_METHOD("get_video_driver_name", "driver"), &_OS::get_video_driver_name);
+ ClassDB::bind_method(D_METHOD("get_current_video_driver"), &_OS::get_current_video_driver);
+
ClassDB::bind_method(D_METHOD("get_audio_driver_count"), &_OS::get_audio_driver_count);
ClassDB::bind_method(D_METHOD("get_audio_driver_name", "driver"), &_OS::get_audio_driver_name);
ClassDB::bind_method(D_METHOD("get_connected_midi_inputs"), &_OS::get_connected_midi_inputs);
@@ -1276,6 +1282,9 @@ void _OS::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "window_position"), "set_window_position", "get_window_position");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "window_size"), "set_window_size", "get_window_size");
+ BIND_ENUM_CONSTANT(VIDEO_DRIVER_GLES2);
+ BIND_ENUM_CONSTANT(VIDEO_DRIVER_GLES3);
+
BIND_ENUM_CONSTANT(DAY_SUNDAY);
BIND_ENUM_CONSTANT(DAY_MONDAY);
BIND_ENUM_CONSTANT(DAY_TUESDAY);
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index 1c8b985d73..f3bc4644d8 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -103,6 +103,11 @@ protected:
static _OS *singleton;
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 */
@@ -152,7 +157,8 @@ public:
Array get_fullscreen_mode_list(int p_screen = 0) const;
virtual int get_video_driver_count() const;
- virtual String get_video_driver_name(int p_driver) const;
+ virtual String get_video_driver_name(VideoDriver p_driver) const;
+ virtual VideoDriver get_current_video_driver() const;
virtual int get_audio_driver_count() const;
virtual String get_audio_driver_name(int p_driver) const;
@@ -355,6 +361,7 @@ public:
_OS();
};
+VARIANT_ENUM_CAST(_OS::VideoDriver);
VARIANT_ENUM_CAST(_OS::PowerState);
VARIANT_ENUM_CAST(_OS::Weekday);
VARIANT_ENUM_CAST(_OS::Month);
diff --git a/core/command_queue_mt.cpp b/core/command_queue_mt.cpp
index 36d4b0a32f..2bdf02295c 100644
--- a/core/command_queue_mt.cpp
+++ b/core/command_queue_mt.cpp
@@ -97,7 +97,7 @@ tryagain:
return false;
}
- dealloc_ptr += (size >> 1) + sizeof(uint32_t);
+ dealloc_ptr += (size >> 1) + 8;
return true;
}
@@ -107,6 +107,7 @@ CommandQueueMT::CommandQueueMT(bool p_sync) {
write_ptr = 0;
dealloc_ptr = 0;
mutex = Mutex::create();
+ command_mem = (uint8_t *)memalloc(COMMAND_MEM_SIZE);
for (int i = 0; i < SYNC_SEMAPHORES; i++) {
@@ -128,4 +129,5 @@ CommandQueueMT::~CommandQueueMT() {
memdelete(sync_sems[i].sem);
}
+ memfree(command_mem);
}
diff --git a/core/command_queue_mt.h b/core/command_queue_mt.h
index 3fd660a3db..59eabd8786 100644
--- a/core/command_queue_mt.h
+++ b/core/command_queue_mt.h
@@ -36,6 +36,7 @@
#include "core/os/semaphore.h"
#include "core/simple_type.h"
#include "core/typedefs.h"
+
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
@@ -254,6 +255,7 @@
unlock(); \
if (sync) sync->post(); \
ss->sem->wait(); \
+ ss->in_use = false; \
}
#define CMD_SYNC_TYPE(N) CommandSync##N<T, M COMMA(N) COMMA_SEP_LIST(TYPE_ARG, N)>
@@ -270,6 +272,7 @@
unlock(); \
if (sync) sync->post(); \
ss->sem->wait(); \
+ ss->in_use = false; \
}
#define MAX_CMD_PARAMS 13
@@ -295,7 +298,6 @@ class CommandQueueMT {
virtual void post() {
sync_sem->sem->post();
- sync_sem->in_use = false;
}
};
@@ -318,7 +320,7 @@ class CommandQueueMT {
SYNC_SEMAPHORES = 8
};
- uint8_t command_mem[COMMAND_MEM_SIZE];
+ uint8_t *command_mem;
uint32_t read_ptr;
uint32_t write_ptr;
uint32_t dealloc_ptr;
@@ -330,7 +332,7 @@ class CommandQueueMT {
T *allocate() {
// alloc size is size+T+safeguard
- uint32_t alloc_size = sizeof(T) + sizeof(uint32_t);
+ uint32_t alloc_size = ((sizeof(T) + 8 - 1) & ~(8 - 1)) + 8;
tryagain:
@@ -360,7 +362,7 @@ class CommandQueueMT {
}
// if this happens, it's a bug
- ERR_FAIL_COND_V((COMMAND_MEM_SIZE - write_ptr) < sizeof(uint32_t), NULL);
+ ERR_FAIL_COND_V((COMMAND_MEM_SIZE - write_ptr) < 8, NULL);
// zero means, wrap to beginning
uint32_t *p = (uint32_t *)&command_mem[write_ptr];
@@ -372,12 +374,13 @@ class CommandQueueMT {
// Allocate the size and the 'in use' bit.
// First bit used to mark if command is still in use (1)
// or if it has been destroyed and can be deallocated (0).
+ uint32_t size = (sizeof(T) + 8 - 1) & ~(8 - 1);
uint32_t *p = (uint32_t *)&command_mem[write_ptr];
- *p = (sizeof(T) << 1) | 1;
- write_ptr += sizeof(uint32_t);
+ *p = (size << 1) | 1;
+ write_ptr += 8;
// allocate the command
T *cmd = memnew_placement(&command_mem[write_ptr], T);
- write_ptr += sizeof(T);
+ write_ptr += size;
return cmd;
}
@@ -415,7 +418,7 @@ class CommandQueueMT {
goto tryagain;
}
- read_ptr += sizeof(uint32_t);
+ read_ptr += 8;
CommandBase *cmd = reinterpret_cast<CommandBase *>(&command_mem[read_ptr]);
diff --git a/core/engine.cpp b/core/engine.cpp
index 4dba41853d..9607dedb3c 100644
--- a/core/engine.cpp
+++ b/core/engine.cpp
@@ -98,6 +98,7 @@ Dictionary Engine::get_version_info() const {
#else
dict["patch"] = 0;
#endif
+ dict["hex"] = VERSION_HEX;
dict["status"] = VERSION_STATUS;
dict["build"] = VERSION_BUILD;
dict["year"] = VERSION_YEAR;
diff --git a/core/error_macros.h b/core/error_macros.h
index 60a0e8a7dc..ca5ccd24cf 100644
--- a/core/error_macros.h
+++ b/core/error_macros.h
@@ -154,6 +154,20 @@ extern bool _err_error_exists;
_err_error_exists = false; \
} while (0); // (*)
+/** An index has failed if m_index >=m_size, the function exists.
+* This function returns an error value, if returning Error, please select the most
+* appropriate error condition from error_macros.h
+*/
+
+#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; \
+ } else \
+ _err_error_exists = false; \
+ } while (0); // (*)
+
/** Use this one if there is no sensible fallback, that is, the error is unrecoverable.
* We'll return a null reference and try to keep running.
*/
@@ -296,6 +310,16 @@ extern bool _err_error_exists;
_err_error_exists = false; \
}
+#define ERR_PRINT_ONCE(m_string) \
+ { \
+ static bool first_print = true; \
+ if (first_print) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string); \
+ _err_error_exists = false; \
+ first_print = false; \
+ } \
+ }
+
/** Print a warning string.
*/
@@ -311,6 +335,16 @@ extern bool _err_error_exists;
_err_error_exists = false; \
}
+#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); \
+ _err_error_exists = false; \
+ first_print = false; \
+ } \
+ }
+
#define WARN_DEPRECATED \
{ \
static volatile bool warning_shown = false; \
diff --git a/core/image.cpp b/core/image.cpp
index 3d48db872c..5a287ca50e 100644
--- a/core/image.cpp
+++ b/core/image.cpp
@@ -2901,15 +2901,15 @@ void Image::fix_alpha_edges() {
if (dist >= closest_dist)
continue;
- const uint8_t *rp = &srcptr[(k * width + l) << 2];
+ const uint8_t *rp2 = &srcptr[(k * width + l) << 2];
- if (rp[3] < alpha_threshold)
+ if (rp2[3] < alpha_threshold)
continue;
closest_dist = dist;
- closest_color[0] = rp[0];
- closest_color[1] = rp[1];
- closest_color[2] = rp[2];
+ closest_color[0] = rp2[0];
+ closest_color[1] = rp2[1];
+ closest_color[2] = rp2[2];
}
}
diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp
index 66b911e83c..be28c9a877 100644
--- a/core/io/file_access_zip.cpp
+++ b/core/io/file_access_zip.cpp
@@ -168,10 +168,10 @@ bool ZipArchive::try_open_pack(const String &p_path) {
zlib_filefunc_def io;
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
- if (!f)
+ FileAccess *fa = FileAccess::open(p_path, FileAccess::READ);
+ if (!fa)
return false;
- io.opaque = f;
+ io.opaque = fa;
io.zopen_file = godot_open;
io.zread_file = godot_read;
io.zwrite_file = godot_write;
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index b3fd814870..e5c6d2a4f2 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -278,6 +278,7 @@ void HTTPClient::close() {
body_size = -1;
body_left = 0;
chunk_left = 0;
+ chunk_trailer_part = 0;
read_until_eof = false;
response_num = 0;
handshaking = false;
@@ -421,16 +422,16 @@ Error HTTPClient::poll() {
chunked = false;
body_left = 0;
chunk_left = 0;
+ chunk_trailer_part = false;
read_until_eof = false;
response_str.clear();
response_headers.clear();
response_num = RESPONSE_OK;
- // Per the HTTP 1.1 spec, keep-alive is the default, but in practice
- // it's safe to assume it only if the explicit header is found, allowing
- // to handle body-up-to-EOF responses on naive servers; that's what Curl
- // and browsers do
- bool keep_alive = false;
+ // Per the HTTP 1.1 spec, keep-alive is the default.
+ // Not following that specification breaks standard implemetations.
+ // Broken web servers should be fixed.
+ bool keep_alive = true;
for (int i = 0; i < responses.size(); i++) {
@@ -447,8 +448,8 @@ Error HTTPClient::poll() {
if (encoding == "chunked") {
chunked = true;
}
- } else if (s.begins_with("connection: keep-alive")) {
- keep_alive = true;
+ } else if (s.begins_with("connection: close")) {
+ keep_alive = false;
}
if (i == 0 && responses[i].begins_with("HTTP")) {
@@ -505,13 +506,37 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
ERR_FAIL_COND_V(status != STATUS_BODY, PoolByteArray());
+ PoolByteArray ret;
Error err = OK;
if (chunked) {
while (true) {
- if (chunk_left == 0) {
+ if (chunk_trailer_part) {
+ // We need to consume the trailer part too or keep-alive will break
+ uint8_t b;
+ int rec = 0;
+ err = _get_http_data(&b, 1, rec);
+
+ if (rec == 0)
+ break;
+
+ chunk.push_back(b);
+ int cs = chunk.size();
+ if ((cs >= 2 && chunk[cs - 2] == '\r' && chunk[cs - 1] == '\n')) {
+ if (cs == 2) {
+ // Finally over
+ chunk_trailer_part = false;
+ status = STATUS_CONNECTED;
+ chunk.clear();
+ break;
+ } else {
+ // We do not process nor return the trailer data
+ chunk.clear();
+ }
+ }
+ } else if (chunk_left == 0) {
// Reading length
uint8_t b;
int rec = 0;
@@ -525,7 +550,7 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
if (chunk.size() > 32) {
ERR_PRINT("HTTP Invalid chunk hex len");
status = STATUS_CONNECTION_ERROR;
- return PoolByteArray();
+ break;
}
if (chunk.size() > 2 && chunk[chunk.size() - 2] == '\r' && chunk[chunk.size() - 1] == '\n') {
@@ -543,22 +568,22 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
else {
ERR_PRINT("HTTP Chunk len not in hex!!");
status = STATUS_CONNECTION_ERROR;
- return PoolByteArray();
+ break;
}
len <<= 4;
len |= v;
if (len > (1 << 24)) {
ERR_PRINT("HTTP Chunk too big!! >16mb");
status = STATUS_CONNECTION_ERROR;
- return PoolByteArray();
+ break;
}
}
if (len == 0) {
// End reached!
- status = STATUS_CONNECTED;
+ chunk_trailer_part = true;
chunk.clear();
- return PoolByteArray();
+ break;
}
chunk_left = len + 2;
@@ -578,18 +603,13 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
if (chunk[chunk.size() - 2] != '\r' || chunk[chunk.size() - 1] != '\n') {
ERR_PRINT("HTTP Invalid chunk terminator (not \\r\\n)");
status = STATUS_CONNECTION_ERROR;
- return PoolByteArray();
+ break;
}
- PoolByteArray ret;
ret.resize(chunk.size() - 2);
- {
- PoolByteArray::Write w = ret.write();
- copymem(w.ptr(), chunk.ptr(), chunk.size() - 2);
- }
+ PoolByteArray::Write w = ret.write();
+ copymem(w.ptr(), chunk.ptr(), chunk.size() - 2);
chunk.clear();
-
- return ret;
}
break;
@@ -599,45 +619,26 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
} else {
int to_read = !read_until_eof ? MIN(body_left, read_chunk_size) : read_chunk_size;
- PoolByteArray ret;
ret.resize(to_read);
int _offset = 0;
- while (read_until_eof || to_read > 0) {
+ while (to_read > 0) {
int rec = 0;
{
PoolByteArray::Write w = ret.write();
err = _get_http_data(w.ptr() + _offset, to_read, rec);
}
- if (rec < 0) {
- if (to_read > 0) // Ended up reading less
- ret.resize(_offset);
+ if (rec <= 0) { // Ended up reading less
+ ret.resize(_offset);
break;
} else {
_offset += rec;
+ to_read -= rec;
if (!read_until_eof) {
body_left -= rec;
- to_read -= rec;
- } else {
- if (rec < to_read) {
- ret.resize(_offset);
- err = ERR_FILE_EOF;
- break;
- }
- ret.resize(_offset + to_read);
}
}
- }
- if (!read_until_eof) {
- if (body_left == 0) {
- status = STATUS_CONNECTED;
- }
- return ret;
- } else {
- if (err == ERR_FILE_EOF) {
- err = OK; // EOF is expected here
- close();
- return ret;
- }
+ if (err != OK)
+ break;
}
}
@@ -652,12 +653,12 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
status = STATUS_CONNECTION_ERROR;
}
- } else if (body_left == 0 && !chunked) {
+ } else if (body_left == 0 && !chunked && !read_until_eof) {
status = STATUS_CONNECTED;
}
- return PoolByteArray();
+ return ret;
}
HTTPClient::Status HTTPClient::get_status() const {
@@ -719,6 +720,7 @@ HTTPClient::HTTPClient() {
body_left = 0;
read_until_eof = false;
chunk_left = 0;
+ chunk_trailer_part = false;
response_num = 0;
ssl = false;
blocking = false;
diff --git a/core/io/http_client.h b/core/io/http_client.h
index 1ad44d5f01..85ee1959a2 100644
--- a/core/io/http_client.h
+++ b/core/io/http_client.h
@@ -172,6 +172,7 @@ private:
bool chunked;
Vector<uint8_t> chunk;
int chunk_left;
+ bool chunk_trailer_part;
int body_size;
int body_left;
bool read_until_eof;
diff --git a/core/io/logger.h b/core/io/logger.h
index 0b871a13de..ff5b8ce489 100644
--- a/core/io/logger.h
+++ b/core/io/logger.h
@@ -49,11 +49,11 @@ public:
ERR_SHADER
};
- virtual void logv(const char *p_format, va_list p_list, bool p_err) = 0;
+ virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0 = 0;
virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR);
- void logf(const char *p_format, ...);
- void logf_error(const char *p_format, ...);
+ void logf(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
+ void logf_error(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
virtual ~Logger();
};
@@ -64,7 +64,7 @@ public:
class StdLogger : public Logger {
public:
- virtual void logv(const char *p_format, va_list p_list, bool p_err);
+ virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0;
virtual ~StdLogger();
};
@@ -88,7 +88,7 @@ class RotatedFileLogger : public Logger {
public:
RotatedFileLogger(const String &p_base_path, int p_max_files = 10);
- virtual void logv(const char *p_format, va_list p_list, bool p_err);
+ virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0;
virtual ~RotatedFileLogger();
};
@@ -99,7 +99,7 @@ class CompositeLogger : public Logger {
public:
CompositeLogger(Vector<Logger *> p_loggers);
- virtual void logv(const char *p_format, va_list p_list, bool p_err);
+ virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0;
virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR);
void add_logger(Logger *p_logger);
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index eec1c55744..5087a63b68 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -889,11 +889,11 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
if (buf) {
encode_uint32(uint32_t(np.get_name_count()) | 0x80000000, buf); //for compatibility with the old format
encode_uint32(np.get_subname_count(), buf + 4);
- uint32_t flags = 0;
+ uint32_t np_flags = 0;
if (np.is_absolute())
- flags |= 1;
+ np_flags |= 1;
- encode_uint32(flags, buf + 8);
+ encode_uint32(np_flags, buf + 8);
buf += 12;
}
diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp
index 3aa1fcfd8f..d7bfdbbb37 100644
--- a/core/io/packet_peer.cpp
+++ b/core/io/packet_peer.cpp
@@ -224,7 +224,7 @@ Error PacketPeerStream::get_packet(const uint8_t **r_buffer, int &r_buffer_size)
uint32_t len = decode_uint32(lbuf);
ERR_FAIL_COND_V(remaining < (int)len, ERR_UNAVAILABLE);
- ERR_FAIL_COND_V(input_buffer.size() < len, ERR_UNAVAILABLE);
+ ERR_FAIL_COND_V(input_buffer.size() < (int)len, ERR_UNAVAILABLE);
ring_buffer.read(lbuf, 4); //get rid of first 4 bytes
ring_buffer.read(input_buffer.ptrw(), len); // read packet
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 4d4134b745..42070cd132 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -106,7 +106,7 @@ StringName ResourceInteractiveLoaderBinary::_get_string() {
uint32_t id = f->get_32();
if (id & 0x80000000) {
uint32_t len = id & 0x7FFFFFFF;
- if (len > str_buf.size()) {
+ if ((int)len > str_buf.size()) {
str_buf.resize(len);
}
if (len == 0)
@@ -296,9 +296,9 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
} break;
case VARIANT_OBJECT: {
- uint32_t type = f->get_32();
+ uint32_t objtype = f->get_32();
- switch (type) {
+ switch (objtype) {
case OBJECT_EMPTY: {
//do none
@@ -317,7 +317,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
case OBJECT_EXTERNAL_RESOURCE: {
//old file format, still around for compatibility
- String type = get_unicode_string();
+ String exttype = get_unicode_string();
String path = get_unicode_string();
if (path.find("://") == -1 && path.is_rel_path()) {
@@ -329,7 +329,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
path = remaps[path];
}
- RES res = ResourceLoader::load(path, type);
+ RES res = ResourceLoader::load(path, exttype);
if (res.is_null()) {
WARN_PRINT(String("Couldn't load resource: " + path).utf8().get_data());
@@ -346,7 +346,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
r_v = Variant();
} else {
- String type = external_resources[erindex].type;
+ String exttype = external_resources[erindex].type;
String path = external_resources[erindex].path;
if (path.find("://") == -1 && path.is_rel_path()) {
@@ -354,7 +354,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
path = ProjectSettings::get_singleton()->localize_path(res_path.get_base_dir().plus_file(path));
}
- RES res = ResourceLoader::load(path, type);
+ RES res = ResourceLoader::load(path, exttype);
if (res.is_null()) {
WARN_PRINT(String("Couldn't load resource: " + path).utf8().get_data());
@@ -1314,8 +1314,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
} else {
f->store_32(VARIANT_INT);
- int val = p_property;
- f->store_32(int32_t(val));
+ f->store_32(int32_t(p_property));
}
} break;
@@ -1502,7 +1501,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
if (!resource_set.has(res)) {
f->store_32(OBJECT_EMPTY);
- ERR_EXPLAIN("Resource was not pre cached for the resource section, bug?");
+ ERR_EXPLAIN("Resource was not pre cached for the resource section, most likely due to circular refedence.");
ERR_FAIL();
}
@@ -1651,6 +1650,10 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
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.");
+ return;
+ }
int idx = external_resources.size();
external_resources[res] = idx;
return;
@@ -1667,7 +1670,20 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
if (E->get().usage & PROPERTY_USAGE_STORAGE) {
- _find_resources(res->get(E->get().name));
+ Variant value = res->get(E->get().name);
+ if (E->get().usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) {
+ RES sres = value;
+ if (sres.is_valid()) {
+ NonPersistentKey npk;
+ npk.base = res;
+ npk.property = E->get().name;
+ non_persistent_map[npk] = sres;
+ resource_set.insert(sres);
+ saved_resources.push_back(sres);
+ }
+ } else {
+ _find_resources(value);
+ }
}
}
@@ -1762,6 +1778,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
takeover_paths = false;
local_path = p_path.get_base_dir();
+ path = ProjectSettings::get_singleton()->localize_path(p_path);
_find_resources(p_resource, true);
@@ -1811,7 +1828,17 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
if ((F->get().usage & PROPERTY_USAGE_STORAGE)) {
Property p;
p.name_idx = get_string_index(F->get().name);
- p.value = E->get()->get(F->get().name);
+
+ if (F->get().usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) {
+ NonPersistentKey npk;
+ npk.base = E->get();
+ npk.property = F->get().name;
+ if (non_persistent_map.has(npk)) {
+ p.value = non_persistent_map[npk];
+ }
+ } else {
+ p.value = E->get()->get(F->get().name);
+ }
Variant default_value = ClassDB::class_get_default_property_value(E->get()->get_class(), F->get().name);
diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h
index 8f2c48e745..a4894e4033 100644
--- a/core/io/resource_format_binary.h
+++ b/core/io/resource_format_binary.h
@@ -114,6 +114,7 @@ public:
class ResourceFormatSaverBinaryInstance {
String local_path;
+ String path;
bool relative_paths;
bool bundle_resources;
@@ -123,6 +124,14 @@ class ResourceFormatSaverBinaryInstance {
FileAccess *f;
String magic;
Set<RES> resource_set;
+
+ struct NonPersistentKey { //for resource properties generated on the fly
+ RES base;
+ StringName property;
+ bool operator<(const NonPersistentKey &p_key) const { return base == p_key.base ? property < p_key.property : base < p_key.base; }
+ };
+
+ Map<NonPersistentKey, RES> non_persistent_map;
Map<StringName, int> string_map;
Vector<StringName> strings;
diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp
index 9327e000f5..69907a710a 100644
--- a/core/io/resource_importer.cpp
+++ b/core/io/resource_importer.cpp
@@ -33,6 +33,10 @@
#include "core/os/os.h"
#include "core/variant_parser.h"
+bool ResourceFormatImporter::SortImporterByName::operator()(const Ref<ResourceImporter> &p_a, const Ref<ResourceImporter> &p_b) const {
+ return p_a->get_importer_name() < p_b->get_importer_name();
+}
+
Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid) const {
Error err;
@@ -90,6 +94,8 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy
r_path_and_type.type = value;
} else if (assign == "importer") {
r_path_and_type.importer = value;
+ } else if (assign == "metadata") {
+ r_path_and_type.metadata = value;
} else if (assign == "valid") {
if (r_valid) {
*r_valid = value;
@@ -304,6 +310,18 @@ String ResourceFormatImporter::get_resource_type(const String &p_path) const {
return pat.type;
}
+Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) const {
+ PathAndType pat;
+ Error err = _get_path_and_type(p_path, pat);
+
+ if (err != OK) {
+
+ return Variant();
+ }
+
+ return pat.metadata;
+}
+
void ResourceFormatImporter::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
PathAndType pat;
@@ -366,6 +384,40 @@ String ResourceFormatImporter::get_import_base_path(const String &p_for_file) co
return "res://.import/" + p_for_file.get_file() + "-" + p_for_file.md5_text();
}
+bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) const {
+
+ bool valid = true;
+ PathAndType pat;
+ _get_path_and_type(p_path, pat, &valid);
+
+ if (!valid) {
+ return false;
+ }
+
+ for (int i = 0; i < importers.size(); i++) {
+ if (importers[i]->get_importer_name() == pat.importer) {
+ if (!importers[i]->are_import_settings_valid(p_path)) { //importer thinks this is not valid
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+String ResourceFormatImporter::get_import_settings_hash() const {
+
+ Vector<Ref<ResourceImporter> > sorted_importers = importers;
+
+ sorted_importers.sort_custom<SortImporterByName>();
+
+ String hash;
+ for (int i = 0; i < sorted_importers.size(); i++) {
+ hash += ":" + sorted_importers[i]->get_importer_name() + ":" + sorted_importers[i]->get_import_settings_string();
+ }
+ return hash.md5_text();
+}
+
ResourceFormatImporter *ResourceFormatImporter::singleton = NULL;
ResourceFormatImporter::ResourceFormatImporter() {
diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h
index 32c39a8085..1d27d4dec3 100644
--- a/core/io/resource_importer.h
+++ b/core/io/resource_importer.h
@@ -43,12 +43,18 @@ class ResourceFormatImporter : public ResourceFormatLoader {
String path;
String type;
String importer;
+ Variant metadata;
};
Error _get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid = NULL) const;
static ResourceFormatImporter *singleton;
+ //need them to stay in order to compute the settings hash
+ struct SortImporterByName {
+ bool operator()(const Ref<ResourceImporter> &p_a, const Ref<ResourceImporter> &p_b) const;
+ };
+
Vector<Ref<ResourceImporter> > importers;
public:
@@ -59,6 +65,7 @@ public:
virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
+ virtual Variant get_resource_metadata(const String &p_path) const;
virtual bool is_import_valid(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
@@ -68,12 +75,17 @@ public:
String get_internal_resource_path(const String &p_path) const;
void get_internal_resource_path_list(const String &p_path, List<String> *r_paths);
- void add_importer(const Ref<ResourceImporter> &p_importer) { importers.push_back(p_importer); }
+ void add_importer(const Ref<ResourceImporter> &p_importer) {
+ importers.push_back(p_importer);
+ }
void remove_importer(const Ref<ResourceImporter> &p_importer) { importers.erase(p_importer); }
Ref<ResourceImporter> get_importer_by_name(const String &p_name) const;
Ref<ResourceImporter> get_importer_by_extension(const String &p_extension) const;
void get_importers_for_extension(const String &p_extension, List<Ref<ResourceImporter> > *r_importers);
+ bool are_import_settings_valid(const String &p_path) const;
+ String get_import_settings_hash() const;
+
String get_import_base_path(const String &p_for_file) const;
ResourceFormatImporter();
};
@@ -107,7 +119,9 @@ public:
virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const = 0;
virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const = 0;
- virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL) = 0;
+ virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL) = 0;
+ virtual bool are_import_settings_valid(const String &p_path) const { return true; }
+ virtual String get_import_settings_string() const { return String(); }
};
#endif // RESOURCE_IMPORTER_H
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 4aa002e9a2..98309048bb 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -55,7 +55,7 @@ Error ResourceInteractiveLoader::wait() {
ResourceInteractiveLoader::~ResourceInteractiveLoader() {
if (path_loading != String()) {
- ResourceLoader::_remove_from_loading_map(path_loading);
+ ResourceLoader::_remove_from_loading_map_and_thread(path_loading, path_loading_thread);
}
}
@@ -293,10 +293,14 @@ bool ResourceLoader::_add_to_loading_map(const String &p_path) {
loading_map_mutex->lock();
}
- if (loading_map.has(p_path)) {
+ LoadingMapKey key;
+ key.path = p_path;
+ key.thread = Thread::get_caller_id();
+
+ if (loading_map.has(key)) {
success = false;
} else {
- loading_map[p_path] = true;
+ loading_map[key] = true;
success = true;
}
@@ -312,7 +316,27 @@ void ResourceLoader::_remove_from_loading_map(const String &p_path) {
loading_map_mutex->lock();
}
- loading_map.erase(p_path);
+ LoadingMapKey key;
+ key.path = p_path;
+ key.thread = Thread::get_caller_id();
+
+ loading_map.erase(key);
+
+ if (loading_map_mutex) {
+ loading_map_mutex->unlock();
+ }
+}
+
+void ResourceLoader::_remove_from_loading_map_and_thread(const String &p_path, Thread::ID p_thread) {
+ if (loading_map_mutex) {
+ loading_map_mutex->lock();
+ }
+
+ LoadingMapKey key;
+ key.path = p_path;
+ key.thread = p_thread;
+
+ loading_map.erase(key);
if (loading_map_mutex) {
loading_map_mutex->unlock();
@@ -471,6 +495,7 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_
ril->resource = res_cached;
ril->path_loading = local_path;
+ ril->path_loading_thread = Thread::get_caller_id();
return ril;
}
}
@@ -499,6 +524,7 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_
if (!p_no_cache) {
ril->set_local_path(local_path);
ril->path_loading = local_path;
+ ril->path_loading_thread = Thread::get_caller_id();
}
if (xl_remapped)
@@ -879,9 +905,9 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) {
void ResourceLoader::remove_custom_resource_format_loader(String script_path) {
- Ref<ResourceFormatLoader> loader = _find_custom_resource_format_loader(script_path);
- if (loader.is_valid())
- remove_resource_format_loader(loader);
+ Ref<ResourceFormatLoader> custom_loader = _find_custom_resource_format_loader(script_path);
+ if (custom_loader.is_valid())
+ remove_resource_format_loader(custom_loader);
}
void ResourceLoader::add_custom_loaders() {
@@ -919,7 +945,7 @@ void ResourceLoader::remove_custom_loaders() {
}
Mutex *ResourceLoader::loading_map_mutex = NULL;
-HashMap<String, int> ResourceLoader::loading_map;
+HashMap<ResourceLoader::LoadingMapKey, int, ResourceLoader::LoadingMapKeyHasher> ResourceLoader::loading_map;
void ResourceLoader::initialize() {
#ifndef NO_THREADS
@@ -929,9 +955,9 @@ void ResourceLoader::initialize() {
void ResourceLoader::finalize() {
#ifndef NO_THREADS
- const String *K = NULL;
+ const LoadingMapKey *K = NULL;
while ((K = loading_map.next(K))) {
- ERR_PRINTS("Exited while resource is being loaded: " + *K);
+ ERR_PRINTS("Exited while resource is being loaded: " + K->path);
}
loading_map.clear();
memdelete(loading_map_mutex);
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index 622b74a998..70eb1a3de0 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -31,8 +31,8 @@
#ifndef RESOURCE_LOADER_H
#define RESOURCE_LOADER_H
+#include "core/os/thread.h"
#include "core/resource.h"
-
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
@@ -42,6 +42,7 @@ class ResourceInteractiveLoader : public Reference {
GDCLASS(ResourceInteractiveLoader, Reference);
friend class ResourceLoader;
String path_loading;
+ Thread::ID path_loading_thread;
protected:
static void _bind_methods();
@@ -121,10 +122,25 @@ class ResourceLoader {
static Ref<ResourceFormatLoader> _find_custom_resource_format_loader(String path);
static Mutex *loading_map_mutex;
- static HashMap<String, int> loading_map;
+
+ //used to track paths being loaded in a thread, avoids cyclic recursion
+ struct LoadingMapKey {
+ String path;
+ Thread::ID thread;
+ bool operator==(const LoadingMapKey &p_key) const {
+ return (thread == p_key.thread && path == p_key.path);
+ }
+ };
+ struct LoadingMapKeyHasher {
+
+ static _FORCE_INLINE_ uint32_t hash(const LoadingMapKey &p_key) { return p_key.path.hash() + HashMapHasherDefault::hash(p_key.thread); }
+ };
+
+ static HashMap<LoadingMapKey, int, LoadingMapKeyHasher> loading_map;
static bool _add_to_loading_map(const String &p_path);
static void _remove_from_loading_map(const String &p_path);
+ static void _remove_from_loading_map_and_thread(const String &p_path, Thread::ID p_thread);
public:
static Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = NULL);
diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp
index 279788796c..c992e2bf94 100644
--- a/core/io/resource_saver.cpp
+++ b/core/io/resource_saver.cpp
@@ -233,9 +233,9 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) {
void ResourceSaver::remove_custom_resource_format_saver(String script_path) {
- Ref<ResourceFormatSaver> saver = _find_custom_resource_format_saver(script_path);
- if (saver.is_valid())
- remove_resource_format_saver(saver);
+ Ref<ResourceFormatSaver> custom_saver = _find_custom_resource_format_saver(script_path);
+ if (custom_saver.is_valid())
+ remove_resource_format_saver(custom_saver);
}
void ResourceSaver::add_custom_savers() {
diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp
index b885a06834..6c3b84d49a 100644
--- a/core/math/a_star.cpp
+++ b/core/math/a_star.cpp
@@ -97,11 +97,14 @@ void AStar::remove_point(int p_id) {
Point *p = points[p_id];
- for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) {
-
- Segment s(p_id, E->get()->id);
- segments.erase(s);
- E->get()->neighbours.erase(p);
+ Map<int, Point *>::Element *PE = points.front();
+ while (PE) {
+ for (Set<Point *>::Element *E = PE->get()->neighbours.front(); E; E = E->next()) {
+ Segment s(p_id, E->get()->id);
+ segments.erase(s);
+ E->get()->neighbours.erase(p);
+ }
+ PE = PE->next();
}
memdelete(p);
@@ -371,14 +374,14 @@ PoolVector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) {
{
PoolVector<Vector3>::Write w = path.write();
- Point *p = end_point;
+ Point *p2 = end_point;
int idx = pc - 1;
- while (p != begin_point) {
- w[idx--] = p->pos;
- p = p->prev_point;
+ while (p2 != begin_point) {
+ w[idx--] = p2->pos;
+ p2 = p2->prev_point;
}
- w[0] = p->pos; // Assign first
+ w[0] = p2->pos; // Assign first
}
return path;
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index 7f60b7962c..8816e3639a 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -76,9 +76,11 @@ void Basis::invert() {
}
void Basis::orthonormalize() {
+
#ifdef MATH_CHECKS
ERR_FAIL_COND(determinant() == 0);
#endif
+
// Gram-Schmidt Process
Vector3 x = get_axis(0);
@@ -118,16 +120,16 @@ bool Basis::is_diagonal() const {
}
bool Basis::is_rotation() const {
- return Math::is_equal_approx(determinant(), 1) && is_orthogonal();
+ return Math::is_equal_approx(determinant(), 1, UNIT_EPSILON) && is_orthogonal();
}
bool Basis::is_symmetric() const {
- if (!Math::is_equal_approx(elements[0][1], elements[1][0]))
+ if (!Math::is_equal_approx_ratio(elements[0][1], elements[1][0], UNIT_EPSILON))
return false;
- if (!Math::is_equal_approx(elements[0][2], elements[2][0]))
+ if (!Math::is_equal_approx_ratio(elements[0][2], elements[2][0], UNIT_EPSILON))
return false;
- if (!Math::is_equal_approx(elements[1][2], elements[2][1]))
+ if (!Math::is_equal_approx_ratio(elements[1][2], elements[2][1], UNIT_EPSILON))
return false;
return true;
@@ -488,6 +490,11 @@ void Basis::set_euler_xyz(const Vector3 &p_euler) {
// as the x, y, and z components of a Vector3 respectively.
Vector3 Basis::get_euler_yxz() const {
+ /* checking this is a bad idea, because obtaining from scaled transform is a valid use case
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND(!is_rotation());
+#endif
+*/
// Euler angles in YXZ convention.
// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
//
@@ -496,9 +503,7 @@ Vector3 Basis::get_euler_yxz() const {
// cy*sx*sz-cz*sy cy*cz*sx+sy*sz cy*cx
Vector3 euler;
-#ifdef MATH_CHECKS
- ERR_FAIL_COND_V(!is_rotation(), euler);
-#endif
+
real_t m12 = elements[1][2];
if (m12 < 1) {
@@ -556,7 +561,7 @@ bool Basis::is_equal_approx(const Basis &a, const Basis &b) const {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
- if (!Math::is_equal_approx(a.elements[i][j], b.elements[i][j]))
+ if (!Math::is_equal_approx_ratio(a.elements[i][j], b.elements[i][j], UNIT_EPSILON))
return false;
}
}
@@ -599,10 +604,14 @@ Basis::operator String() const {
}
Quat Basis::get_quat() const {
-#ifdef MATH_CHECKS
- ERR_FAIL_COND_V(!is_rotation(), Quat());
-#endif
- real_t trace = elements[0][0] + elements[1][1] + elements[2][2];
+
+ /* Allow getting a quaternion from an unnormalized transform */
+ Basis m = *this;
+ m.elements[0].normalize();
+ m.elements[1].normalize();
+ m.elements[2].normalize();
+
+ real_t trace = m.elements[0][0] + m.elements[1][1] + m.elements[2][2];
real_t temp[4];
if (trace > 0.0) {
@@ -610,23 +619,23 @@ Quat Basis::get_quat() const {
temp[3] = (s * 0.5);
s = 0.5 / s;
- temp[0] = ((elements[2][1] - elements[1][2]) * s);
- temp[1] = ((elements[0][2] - elements[2][0]) * s);
- temp[2] = ((elements[1][0] - elements[0][1]) * s);
+ temp[0] = ((m.elements[2][1] - m.elements[1][2]) * s);
+ temp[1] = ((m.elements[0][2] - m.elements[2][0]) * s);
+ temp[2] = ((m.elements[1][0] - m.elements[0][1]) * s);
} else {
- int i = elements[0][0] < elements[1][1] ?
- (elements[1][1] < elements[2][2] ? 2 : 1) :
- (elements[0][0] < elements[2][2] ? 2 : 0);
+ int i = m.elements[0][0] < m.elements[1][1] ?
+ (m.elements[1][1] < m.elements[2][2] ? 2 : 1) :
+ (m.elements[0][0] < m.elements[2][2] ? 2 : 0);
int j = (i + 1) % 3;
int k = (i + 2) % 3;
- real_t s = Math::sqrt(elements[i][i] - elements[j][j] - elements[k][k] + 1.0);
+ real_t s = Math::sqrt(m.elements[i][i] - m.elements[j][j] - m.elements[k][k] + 1.0);
temp[i] = s * 0.5;
s = 0.5 / s;
- temp[3] = (elements[k][j] - elements[j][k]) * s;
- temp[j] = (elements[j][i] + elements[i][j]) * s;
- temp[k] = (elements[k][i] + elements[i][k]) * s;
+ temp[3] = (m.elements[k][j] - m.elements[j][k]) * s;
+ temp[j] = (m.elements[j][i] + m.elements[i][j]) * s;
+ temp[k] = (m.elements[k][i] + m.elements[i][k]) * s;
}
return Quat(temp[0], temp[1], temp[2], temp[3]);
@@ -696,9 +705,11 @@ void Basis::set_orthogonal_index(int p_index) {
}
void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
+ /* checking this is a bad idea, because obtaining from scaled transform is a valid use case
#ifdef MATH_CHECKS
ERR_FAIL_COND(!is_rotation());
#endif
+*/
real_t angle, x, y, z; // variables for result
real_t epsilon = 0.01; // margin to allow for rounding errors
real_t epsilon2 = 0.1; // margin to distinguish between 0 and 180 degrees
@@ -835,14 +846,15 @@ void Basis::set_diagonal(const Vector3 p_diag) {
}
Basis Basis::slerp(const Basis &target, const real_t &t) const {
-// TODO: implement this directly without using quaternions to make it more efficient
-#ifdef MATH_CHECKS
- ERR_FAIL_COND_V(!is_rotation(), Basis());
- ERR_FAIL_COND_V(!target.is_rotation(), Basis());
-#endif
+ //consider scale
Quat from(*this);
Quat to(target);
- return Basis(from.slerp(to, t));
+ Basis b(from.slerp(to, t));
+ b.elements[0] *= Math::lerp(elements[0].length(), target.elements[0].length(), t);
+ b.elements[1] *= Math::lerp(elements[1].length(), target.elements[1].length(), t);
+ b.elements[2] *= Math::lerp(elements[2].length(), target.elements[2].length(), t);
+
+ return b;
}
diff --git a/core/math/expression.cpp b/core/math/expression.cpp
index 61e5154f44..99251d80e3 100644
--- a/core/math/expression.cpp
+++ b/core/math/expression.cpp
@@ -1264,10 +1264,10 @@ Expression::ENode *Expression::_parse_expression() {
}
str_ofs = cofs; //revert
//parse an expression
- ENode *expr = _parse_expression();
- if (!expr)
+ ENode *subexpr = _parse_expression();
+ if (!subexpr)
return NULL;
- dn->dict.push_back(expr);
+ dn->dict.push_back(subexpr);
_get_token(tk);
if (tk.type != TK_COLON) {
@@ -1275,11 +1275,11 @@ Expression::ENode *Expression::_parse_expression() {
return NULL;
}
- expr = _parse_expression();
- if (!expr)
+ subexpr = _parse_expression();
+ if (!subexpr)
return NULL;
- dn->dict.push_back(expr);
+ dn->dict.push_back(subexpr);
cofs = str_ofs;
_get_token(tk);
@@ -1308,10 +1308,10 @@ Expression::ENode *Expression::_parse_expression() {
}
str_ofs = cofs; //revert
//parse an expression
- ENode *expr = _parse_expression();
- if (!expr)
+ ENode *subexpr = _parse_expression();
+ if (!subexpr)
return NULL;
- an->array.push_back(expr);
+ an->array.push_back(subexpr);
cofs = str_ofs;
_get_token(tk);
@@ -1355,25 +1355,25 @@ Expression::ENode *Expression::_parse_expression() {
while (true) {
- int cofs = str_ofs;
+ int cofs2 = str_ofs;
_get_token(tk);
if (tk.type == TK_PARENTHESIS_CLOSE) {
break;
}
- str_ofs = cofs; //revert
+ str_ofs = cofs2; //revert
//parse an expression
- ENode *expr = _parse_expression();
- if (!expr)
+ ENode *subexpr = _parse_expression();
+ if (!subexpr)
return NULL;
- func_call->arguments.push_back(expr);
+ func_call->arguments.push_back(subexpr);
- cofs = str_ofs;
+ cofs2 = str_ofs;
_get_token(tk);
if (tk.type == TK_COMMA) {
//all good
} else if (tk.type == TK_PARENTHESIS_CLOSE) {
- str_ofs = cofs;
+ str_ofs = cofs2;
} else {
_set_error("Expected ',' or ')'");
}
@@ -1444,11 +1444,11 @@ Expression::ENode *Expression::_parse_expression() {
}
str_ofs = cofs; //revert
//parse an expression
- ENode *expr = _parse_expression();
- if (!expr)
+ ENode *subexpr = _parse_expression();
+ if (!subexpr)
return NULL;
- constructor->arguments.push_back(expr);
+ constructor->arguments.push_back(subexpr);
cofs = str_ofs;
_get_token(tk);
@@ -1485,11 +1485,11 @@ Expression::ENode *Expression::_parse_expression() {
}
str_ofs = cofs; //revert
//parse an expression
- ENode *expr = _parse_expression();
- if (!expr)
+ ENode *subexpr = _parse_expression();
+ if (!subexpr)
return NULL;
- bifunc->arguments.push_back(expr);
+ bifunc->arguments.push_back(subexpr);
cofs = str_ofs;
_get_token(tk);
@@ -1584,25 +1584,25 @@ Expression::ENode *Expression::_parse_expression() {
while (true) {
- int cofs = str_ofs;
+ int cofs3 = str_ofs;
_get_token(tk);
if (tk.type == TK_PARENTHESIS_CLOSE) {
break;
}
- str_ofs = cofs; //revert
+ str_ofs = cofs3; //revert
//parse an expression
- ENode *expr = _parse_expression();
- if (!expr)
+ ENode *subexpr = _parse_expression();
+ if (!subexpr)
return NULL;
- func_call->arguments.push_back(expr);
+ func_call->arguments.push_back(subexpr);
- cofs = str_ofs;
+ cofs3 = str_ofs;
_get_token(tk);
if (tk.type == TK_COMMA) {
//all good
} else if (tk.type == TK_PARENTHESIS_CLOSE) {
- str_ofs = cofs;
+ str_ofs = cofs3;
} else {
_set_error("Expected ',' or ')'");
}
@@ -1902,7 +1902,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
Variant b;
if (op->nodes[1]) {
- bool ret = _execute(p_inputs, p_instance, op->nodes[1], b, r_error_str);
+ ret = _execute(p_inputs, p_instance, op->nodes[1], b, r_error_str);
if (ret)
return true;
}
@@ -2070,7 +2070,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
for (int i = 0; i < call->arguments.size(); i++) {
Variant value;
- bool ret = _execute(p_inputs, p_instance, call->arguments[i], value, r_error_str);
+ ret = _execute(p_inputs, p_instance, call->arguments[i], value, r_error_str);
if (ret)
return true;
diff --git a/core/math/face3.h b/core/math/face3.h
index 1a00851eab..184e80ff77 100644
--- a/core/math/face3.h
+++ b/core/math/face3.h
@@ -241,13 +241,13 @@ bool Face3::intersects_aabb2(const AABB &p_aabb) const {
real_t minT = 1e20, maxT = -1e20;
for (int k = 0; k < 3; k++) {
- real_t d = axis.dot(vertex[k]);
+ real_t vert_d = axis.dot(vertex[k]);
- if (d > maxT)
- maxT = d;
+ if (vert_d > maxT)
+ maxT = vert_d;
- if (d < minT)
- minT = d;
+ if (vert_d < minT)
+ minT = vert_d;
}
if (maxB < minT || maxT < minB)
diff --git a/core/math/math_defs.h b/core/math/math_defs.h
index 48533ba659..c54d3cc96f 100644
--- a/core/math/math_defs.h
+++ b/core/math/math_defs.h
@@ -33,6 +33,7 @@
#define CMP_EPSILON 0.00001
#define CMP_EPSILON2 (CMP_EPSILON * CMP_EPSILON)
+
#define CMP_NORMALIZE_TOLERANCE 0.000001
#define CMP_POINT_IN_PLANE_EPSILON 0.00001
@@ -49,6 +50,14 @@
#define MATH_CHECKS
#endif
+//this epsilon is for values related to a unit size (scalar or vector len)
+#ifdef PRECISE_MATH_CHECKS
+#define UNIT_EPSILON 0.00001
+#else
+//tolerate some more floating point error normally
+#define UNIT_EPSILON 0.001
+#endif
+
#define USEC_TO_SEC(m_usec) ((m_usec) / 1000000.0)
enum ClockDirection {
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index ea0bfd88cc..17112d8940 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -217,17 +217,17 @@ public:
static _ALWAYS_INLINE_ double round(double p_val) { return (p_val >= 0) ? Math::floor(p_val + 0.5) : -Math::floor(-p_val + 0.5); }
static _ALWAYS_INLINE_ float round(float p_val) { return (p_val >= 0) ? Math::floor(p_val + 0.5) : -Math::floor(-p_val + 0.5); }
- static _ALWAYS_INLINE_ int wrapi(int value, int min, int max) {
- int rng = max - min;
- return min + ((((value - min) % rng) + rng) % rng);
+ static _ALWAYS_INLINE_ int64_t wrapi(int64_t value, int64_t min, int64_t max) {
+ int64_t rng = max - min;
+ return (rng != 0) ? min + ((((value - min) % rng) + rng) % rng) : min;
}
static _ALWAYS_INLINE_ double wrapf(double value, double min, double max) {
double rng = max - min;
- return value - (rng * Math::floor((value - min) / rng));
+ return (!is_equal_approx(rng, 0.0)) ? value - (rng * Math::floor((value - min) / rng)) : min;
}
static _ALWAYS_INLINE_ float wrapf(float value, float min, float max) {
float rng = max - min;
- return value - (rng * Math::floor((value - min) / rng));
+ return (!is_equal_approx(rng, 0.0f)) ? value - (rng * Math::floor((value - min) / rng)) : min;
}
// double only, as these functions are mainly used by the editor and not performance-critical,
@@ -242,20 +242,32 @@ public:
static void randomize();
static uint32_t rand_from_seed(uint64_t *seed);
static uint32_t rand();
- static _ALWAYS_INLINE_ double randf() { return (double)rand() / (double)Math::RANDOM_MAX; }
- static _ALWAYS_INLINE_ float randd() { return (float)rand() / (float)Math::RANDOM_MAX; }
+ static _ALWAYS_INLINE_ double randd() { return (double)rand() / (double)Math::RANDOM_MAX; }
+ static _ALWAYS_INLINE_ float randf() { return (float)rand() / (float)Math::RANDOM_MAX; }
static double random(double from, double to);
static float random(float from, float to);
static real_t random(int from, int to) { return (real_t)random((real_t)from, (real_t)to); }
- static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b) {
+ static _ALWAYS_INLINE_ bool is_equal_approx_ratio(real_t a, real_t b, real_t epsilon = CMP_EPSILON) {
+ // this is an approximate way to check that numbers are close, as a ratio of their average size
+ // helps compare approximate numbers that may be very big or very small
+ real_t diff = abs(a - b);
+ if (diff == 0.0) {
+ return true;
+ }
+ real_t avg_size = (abs(a) + abs(b)) / 2.0;
+ diff /= avg_size;
+ return diff < epsilon;
+ }
+
+ static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b, real_t epsilon = CMP_EPSILON) {
// TODO: Comparing floats for approximate-equality is non-trivial.
// Using epsilon should cover the typical cases in Godot (where a == b is used to compare two reals), such as matrix and vector comparison operators.
// A proper implementation in terms of ULPs should eventually replace the contents of this function.
// See https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ for details.
- return abs(a - b) < CMP_EPSILON;
+ return abs(a - b) < epsilon;
}
static _ALWAYS_INLINE_ float absf(float g) {
diff --git a/core/math/octree.h b/core/math/octree.h
index 36a77663f4..d6fc9776bc 100644
--- a/core/math/octree.h
+++ b/core/math/octree.h
@@ -916,34 +916,34 @@ void Octree<T, use_pairs, AL>::move(OctreeElementID p_id, const AABB &p_aabb) {
pass++;
- for (typename List<typename Element::OctantOwner, AL>::Element *E = owners.front(); E;) {
+ for (typename List<typename Element::OctantOwner, AL>::Element *F = owners.front(); F;) {
- Octant *o = E->get().octant;
- typename List<typename Element::OctantOwner, AL>::Element *N = E->next();
+ Octant *o = F->get().octant;
+ typename List<typename Element::OctantOwner, AL>::Element *N = F->next();
/*
if (!use_pairs)
- o->elements.erase( E->get().E );
+ o->elements.erase( F->get().E );
*/
if (use_pairs && e.pairable)
- o->pairable_elements.erase(E->get().E);
+ o->pairable_elements.erase(F->get().E);
else
- o->elements.erase(E->get().E);
+ o->elements.erase(F->get().E);
if (_remove_element_from_octant(&e, o, common_parent->parent)) {
- owners.erase(E);
+ owners.erase(F);
}
- E = N;
+ F = N;
}
if (use_pairs) {
//unpair child elements in anything that survived
- for (typename List<typename Element::OctantOwner, AL>::Element *E = owners.front(); E; E = E->next()) {
+ for (typename List<typename Element::OctantOwner, AL>::Element *F = owners.front(); F; F = F->next()) {
- Octant *o = E->get().octant;
+ Octant *o = F->get().octant;
// erase children pairs, unref ONCE
pass++;
diff --git a/core/math/quat.cpp b/core/math/quat.cpp
index 6833d5de55..1a67be7384 100644
--- a/core/math/quat.cpp
+++ b/core/math/quat.cpp
@@ -135,7 +135,7 @@ Quat Quat::normalized() const {
}
bool Quat::is_normalized() const {
- return Math::is_equal_approx(length_squared(), 1.0);
+ return Math::is_equal_approx(length_squared(), 1.0, UNIT_EPSILON); //use less epsilon
}
Quat Quat::inverse() const {
diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp
index 1aa345db1f..fc2eb1454d 100644
--- a/core/math/quick_hull.cpp
+++ b/core/math/quick_hull.cpp
@@ -36,8 +36,6 @@ uint32_t QuickHull::debug_stop_after = 0xFFFFFFFF;
Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_mesh) {
- static const real_t over_tolerance = 0.0001;
-
/* CREATE AABB VOLUME */
AABB aabb;
@@ -180,6 +178,8 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me
faces.push_back(f);
}
+ real_t over_tolerance = 3 * UNIT_EPSILON * (aabb.size.x + aabb.size.y + aabb.size.z);
+
/* COMPUTE AVAILABLE VERTICES */
for (int i = 0; i < p_points.size(); i++) {
@@ -438,12 +438,12 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me
}
// remove all edge connections to this face
- for (Map<Edge, RetFaceConnect>::Element *E = ret_edges.front(); E; E = E->next()) {
- if (E->get().left == O)
- E->get().left = NULL;
+ for (Map<Edge, RetFaceConnect>::Element *G = ret_edges.front(); G; G = G->next()) {
+ if (G->get().left == O)
+ G->get().left = NULL;
- if (E->get().right == O)
- E->get().right = NULL;
+ if (G->get().right == O)
+ G->get().right = NULL;
}
ret_edges.erase(F); //remove the edge
diff --git a/core/math/quick_hull.h b/core/math/quick_hull.h
index 2e659cab6e..a445a47cbe 100644
--- a/core/math/quick_hull.h
+++ b/core/math/quick_hull.h
@@ -64,7 +64,7 @@ public:
struct Face {
Plane plane;
- int vertices[3];
+ uint32_t vertices[3];
Vector<int> points_over;
bool operator<(const Face &p_face) const {
diff --git a/core/math/random_pcg.h b/core/math/random_pcg.h
index ef69bf7120..2a69d43904 100644
--- a/core/math/random_pcg.h
+++ b/core/math/random_pcg.h
@@ -45,7 +45,10 @@ public:
RandomPCG(uint64_t seed = DEFAULT_SEED, uint64_t inc = PCG_DEFAULT_INC_64);
- _FORCE_INLINE_ void seed(uint64_t seed) { pcg.state = seed; }
+ _FORCE_INLINE_ void seed(uint64_t seed) {
+ pcg.state = seed;
+ pcg32_random_r(&pcg); // Force changing internal state to avoid initial 0
+ }
_FORCE_INLINE_ uint64_t get_seed() { return pcg.state; }
void randomize();
diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp
index e580057950..5c1ea5943d 100644
--- a/core/math/vector2.cpp
+++ b/core/math/vector2.cpp
@@ -65,7 +65,7 @@ Vector2 Vector2::normalized() const {
bool Vector2::is_normalized() const {
// use length_squared() instead of length() to avoid sqrt(), makes it more stringent.
- return Math::is_equal_approx(length_squared(), 1.0);
+ return Math::is_equal_approx(length_squared(), 1.0, UNIT_EPSILON);
}
real_t Vector2::distance_to(const Vector2 &p_vector2) const {
diff --git a/core/math/vector3.h b/core/math/vector3.h
index 8d6e093c4c..b11838d16e 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -414,7 +414,7 @@ Vector3 Vector3::normalized() const {
bool Vector3::is_normalized() const {
// use length_squared() instead of length() to avoid sqrt(), makes it more stringent.
- return Math::is_equal_approx(length_squared(), 1.0);
+ return Math::is_equal_approx(length_squared(), 1.0, UNIT_EPSILON);
}
Vector3 Vector3::inverse() const {
diff --git a/core/object.cpp b/core/object.cpp
index 54a9bb0d7c..c46ecc5193 100644
--- a/core/object.cpp
+++ b/core/object.cpp
@@ -703,40 +703,38 @@ Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Vari
}
#ifdef DEBUG_ENABLED
-static bool _test_call_error(const StringName &p_func, const Variant::CallError &error) {
+static void _test_call_error(const StringName &p_func, const Variant::CallError &error) {
switch (error.error) {
case Variant::CallError::CALL_OK:
- return true;
case Variant::CallError::CALL_ERROR_INVALID_METHOD:
- return false;
+ break;
case Variant::CallError::CALL_ERROR_INVALID_ARGUMENT: {
ERR_EXPLAIN("Error Calling Function: " + String(p_func) + " - Invalid type for argument " + itos(error.argument) + ", expected " + Variant::get_type_name(error.expected));
- ERR_FAIL_V(true);
- } break;
+ ERR_FAIL();
+ break;
+ }
case Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: {
ERR_EXPLAIN("Error Calling Function: " + String(p_func) + " - Too many arguments, expected " + itos(error.argument));
- ERR_FAIL_V(true);
-
- } break;
+ ERR_FAIL();
+ break;
+ }
case Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: {
ERR_EXPLAIN("Error Calling Function: " + String(p_func) + " - Too few arguments, expected " + itos(error.argument));
- ERR_FAIL_V(true);
-
- } break;
- case Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL: {
- } //?
+ ERR_FAIL();
+ break;
+ }
+ case Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL:
+ break;
}
-
- return true;
}
#else
-#define _test_call_error(m_str, m_err) ((m_err.error == Variant::CallError::CALL_ERROR_INVALID_METHOD) ? false : true)
+#define _test_call_error(m_str, m_err)
#endif
@@ -1016,7 +1014,7 @@ void Object::set_script(const RefPtr &p_script) {
}
}
- _change_notify("script");
+ _change_notify(); //scripts may add variables, so refresh is desired
emit_signal(CoreStringNames::get_singleton()->script_changed);
}
diff --git a/core/object.h b/core/object.h
index 9a5217e3de..e8a6178052 100644
--- a/core/object.h
+++ b/core/object.h
@@ -119,6 +119,7 @@ enum PropertyUsageFlags {
PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE = 1 << 21, // If the object is duplicated also this property will be duplicated
PROPERTY_USAGE_HIGH_END_GFX = 1 << 22,
PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 23,
+ PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 24,
PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK,
PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED,
diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp
index 1e0e83c8d2..24ec8a1963 100644
--- a/core/os/input_event.cpp
+++ b/core/os/input_event.cpp
@@ -749,6 +749,15 @@ bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool *
return match;
}
+bool InputEventJoypadButton::shortcut_match(const Ref<InputEvent> &p_event) const {
+
+ Ref<InputEventJoypadButton> button = p_event;
+ if (button.is_null())
+ return false;
+
+ return button_index == button->button_index;
+}
+
String InputEventJoypadButton::as_text() const {
return "InputEventJoypadButton : button_index=" + itos(button_index) + ", pressed=" + (pressed ? "true" : "false") + ", pressure=" + String(Variant(pressure));
@@ -950,11 +959,10 @@ bool InputEventAction::is_pressed() const {
}
bool InputEventAction::shortcut_match(const Ref<InputEvent> &p_event) const {
- Ref<InputEventKey> event = p_event;
- if (event.is_null())
+ if (p_event.is_null())
return false;
- return event->is_action(action);
+ return p_event->is_action(action);
}
bool InputEventAction::is_action(const StringName &p_action) const {
diff --git a/core/os/input_event.h b/core/os/input_event.h
index db31055b5f..a6a7012298 100644
--- a/core/os/input_event.h
+++ b/core/os/input_event.h
@@ -400,6 +400,7 @@ public:
float get_pressure() const;
virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const;
+ virtual bool shortcut_match(const Ref<InputEvent> &p_event) const;
virtual bool is_action_type() const { return true; }
virtual String as_text() const;
diff --git a/core/os/os.cpp b/core/os/os.cpp
index d2d39d253a..03e63f636e 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -569,6 +569,11 @@ int OS::get_power_percent_left() {
return -1;
}
+void OS::set_has_server_feature_callback(HasServerFeatureCallback p_callback) {
+
+ has_server_feature_callback = p_callback;
+}
+
bool OS::has_feature(const String &p_feature) {
if (p_feature == get_name())
@@ -625,6 +630,10 @@ bool OS::has_feature(const String &p_feature) {
if (_check_internal_feature_support(p_feature))
return true;
+ if (has_server_feature_callback && has_server_feature_callback(p_feature)) {
+ return true;
+ }
+
if (ProjectSettings::get_singleton()->has_custom_feature(p_feature))
return true;
@@ -729,6 +738,8 @@ OS::OS() {
_logger = NULL;
+ has_server_feature_callback = NULL;
+
Vector<Logger *> loggers;
loggers.push_back(memnew(StdLogger));
_set_logger(memnew(CompositeLogger(loggers)));
diff --git a/core/os/os.h b/core/os/os.h
index 396555970a..d6541034fd 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -77,6 +77,7 @@ protected:
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 */
@@ -121,6 +122,7 @@ public:
protected:
friend class Main;
+ HasServerFeatureCallback has_server_feature_callback;
RenderThreadMode _render_thread_mode;
// functions used by main to initialize/deinitialize the OS
@@ -146,8 +148,8 @@ public:
static OS *get_singleton();
void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, Logger::ErrorType p_type = Logger::ERR_ERROR);
- void print(const char *p_format, ...);
- void printerr(const char *p_format, ...);
+ void print(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
+ void printerr(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") = 0;
virtual String get_stdin_string(bool p_block = true) = 0;
@@ -507,6 +509,8 @@ public:
virtual void force_process_input(){};
bool has_feature(const String &p_feature);
+ void set_has_server_feature_callback(HasServerFeatureCallback p_callback);
+
bool is_layered_allowed() const { return _allow_layered; }
bool is_hidpi_allowed() const { return _allow_hidpi; }
diff --git a/core/project_settings.cpp b/core/project_settings.cpp
index 8d05d7cc74..6b4895d688 100644
--- a/core/project_settings.cpp
+++ b/core/project_settings.cpp
@@ -501,7 +501,7 @@ Error ProjectSettings::_load_settings_binary(const String p_path) {
d.resize(vlen);
f->get_buffer(d.ptrw(), vlen);
Variant value;
- Error err = decode_variant(value, d.ptr(), d.size());
+ err = decode_variant(value, d.ptr(), d.size());
ERR_EXPLAIN("Error decoding property: " + key);
ERR_CONTINUE(err != OK);
set(key, value);
@@ -656,7 +656,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str
file->store_string(key);
int len;
- Error err = encode_variant(p_custom_features, NULL, len);
+ err = encode_variant(p_custom_features, NULL, len);
if (err != OK) {
memdelete(file);
ERR_FAIL_V(err);
@@ -694,7 +694,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str
file->store_string(key);
int len;
- Error err = encode_variant(value, NULL, len);
+ err = encode_variant(value, NULL, len);
if (err != OK)
memdelete(file);
ERR_FAIL_COND_V(err != OK, ERR_INVALID_DATA);
diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp
index ee6133f374..3ed25f118d 100644
--- a/core/script_debugger_remote.cpp
+++ b/core/script_debugger_remote.cpp
@@ -139,10 +139,6 @@ void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue)
ERR_FAIL();
}
- if (allow_focus_steal_pid) {
- OS::get_singleton()->enable_for_stealing_focus(allow_focus_steal_pid);
- }
-
packet_peer_stream->put_var("debug_enter");
packet_peer_stream->put_var(2);
packet_peer_stream->put_var(p_can_continue);
@@ -1058,10 +1054,6 @@ void ScriptDebuggerRemote::profiling_set_frame_times(float p_frame_time, float p
physics_frame_time = p_physics_frame_time;
}
-void ScriptDebuggerRemote::set_allow_focus_steal_pid(OS::ProcessID p_pid) {
- allow_focus_steal_pid = p_pid;
-}
-
ScriptDebuggerRemote::ResourceUsageFunc ScriptDebuggerRemote::resource_usage_func = NULL;
ScriptDebuggerRemote::ScriptDebuggerRemote() :
@@ -1083,7 +1075,6 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() :
char_count(0),
last_msec(0),
msec_count(0),
- allow_focus_steal_pid(0),
locking(false),
poll_every(0),
request_scene_tree(NULL),
diff --git a/core/script_debugger_remote.h b/core/script_debugger_remote.h
index 7afc90428d..1fc9d7c7f1 100644
--- a/core/script_debugger_remote.h
+++ b/core/script_debugger_remote.h
@@ -99,8 +99,6 @@ class ScriptDebuggerRemote : public ScriptDebugger {
uint64_t last_msec;
uint64_t msec_count;
- OS::ProcessID allow_focus_steal_pid;
-
bool locking; //hack to avoid a deadloop
static void _print_handler(void *p_this, const String &p_string, bool p_error);
@@ -174,8 +172,6 @@ public:
virtual void profiling_end();
virtual void profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time);
- void set_allow_focus_steal_pid(OS::ProcessID p_pid);
-
ScriptDebuggerRemote();
~ScriptDebuggerRemote();
};
diff --git a/core/script_language.cpp b/core/script_language.cpp
index 2746c015d6..f0310ffc31 100644
--- a/core/script_language.cpp
+++ b/core/script_language.cpp
@@ -527,8 +527,8 @@ void PlaceHolderScriptInstance::property_set_fallback(const StringName &p_name,
}
bool found = false;
- for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
- if (E->get().name == p_name) {
+ for (const List<PropertyInfo>::Element *F = properties.front(); F; F = F->next()) {
+ if (F->get().name == p_name) {
found = true;
break;
}
diff --git a/core/translation.cpp b/core/translation.cpp
index a402df3eea..6921f1d9f1 100644
--- a/core/translation.cpp
+++ b/core/translation.cpp
@@ -1052,7 +1052,7 @@ StringName TranslationServer::translate(const StringName &p_message) const {
if (fallback.length() >= 2) {
const CharType *fptr = &fallback[0];
- bool near_match = false;
+ near_match = false;
for (const Set<Ref<Translation> >::Element *E = translations.front(); E; E = E->next()) {
const Ref<Translation> &t = E->get();
diff --git a/core/typedefs.h b/core/typedefs.h
index 0005e5e6ee..966360d4f2 100644
--- a/core/typedefs.h
+++ b/core/typedefs.h
@@ -116,6 +116,8 @@ T *_nullptr() {
#define ABS(m_v) (((m_v) < 0) ? (-(m_v)) : (m_v))
#endif
+#define ABSDIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y)))
+
#ifndef SGN
#define SGN(m_v) (((m_v) < 0) ? (-1.0) : (+1.0))
#endif
@@ -305,4 +307,12 @@ struct _GlobalLock {
#define unlikely(x) x
#endif
+#if defined(__GNUC__)
+#define _PRINTF_FORMAT_ATTRIBUTE_2_0 __attribute__((format(printf, 2, 0)))
+#define _PRINTF_FORMAT_ATTRIBUTE_2_3 __attribute__((format(printf, 2, 3)))
+#else
+#define _PRINTF_FORMAT_ATTRIBUTE_2_0
+#define _PRINTF_FORMAT_ATTRIBUTE_2_3
+#endif
+
#endif // TYPEDEFS_H
diff --git a/core/ustring.cpp b/core/ustring.cpp
index c1888c87a7..ff8fcaaaaf 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -603,13 +603,13 @@ String String::camelcase_to_underscore(bool lowercase) const {
is_next_number = cstr[i + 1] >= '0' && cstr[i + 1] <= '9';
}
- const bool a = is_upper && !was_precedent_upper && !was_precedent_number;
- const bool b = was_precedent_upper && is_upper && are_next_2_lower;
- const bool c = is_number && !was_precedent_number;
+ const bool cond_a = is_upper && !was_precedent_upper && !was_precedent_number;
+ const bool cond_b = was_precedent_upper && is_upper && are_next_2_lower;
+ const bool cond_c = is_number && !was_precedent_number;
const bool can_break_number_letter = is_number && !was_precedent_number && is_next_lower;
const bool can_break_letter_number = !is_number && was_precedent_number && (is_next_lower || is_next_number);
- bool should_split = a || b || c || can_break_number_letter || can_break_letter_number;
+ bool should_split = cond_a || cond_b || cond_c || can_break_number_letter || can_break_letter_number;
if (should_split) {
new_string += this->substr(start_index, i - start_index) + "_";
start_index = i;
@@ -1359,6 +1359,9 @@ bool String::parse_utf8(const char *p_utf8, int p_len) {
#define _UNICERROR(m_err) print_line("Unicode error: " + String(m_err));
+ if (!p_utf8)
+ return true;
+
String aux;
int cstr_size = 0;
@@ -2393,7 +2396,7 @@ int String::find(const char *p_str, int p_from) const {
return -1;
}
-int String::find_char(CharType p_char, int p_from) const {
+int String::find_char(const CharType &p_char, int p_from) const {
return _cowdata.find(p_char, p_from);
}
@@ -2945,12 +2948,12 @@ String String::left(int p_pos) const {
String String::right(int p_pos) const {
- if (p_pos >= size())
- return *this;
-
- if (p_pos < 0)
+ if (p_pos >= length())
return "";
+ if (p_pos <= 0)
+ return *this;
+
return substr(p_pos, (length() - p_pos));
}
diff --git a/core/ustring.h b/core/ustring.h
index 5ec5c79e2d..cb3d87378a 100644
--- a/core/ustring.h
+++ b/core/ustring.h
@@ -85,8 +85,7 @@ public:
_FORCE_INLINE_ int size() const { return _cowdata.size(); }
Error resize(int p_size) { return _cowdata.resize(p_size); }
- _FORCE_INLINE_ char get(int p_index) { return _cowdata.get(p_index); }
- _FORCE_INLINE_ const char get(int p_index) const { return _cowdata.get(p_index); }
+ _FORCE_INLINE_ char get(int p_index) const { return _cowdata.get(p_index); }
_FORCE_INLINE_ void set(int p_index, const char &p_elem) { _cowdata.set(p_index, p_elem); }
_FORCE_INLINE_ const char &operator[](int p_index) const {
if (unlikely(p_index == _cowdata.size()))
@@ -143,8 +142,7 @@ public:
_FORCE_INLINE_ void clear() { resize(0); }
- _FORCE_INLINE_ CharType get(int p_index) { return _cowdata.get(p_index); }
- _FORCE_INLINE_ const CharType get(int p_index) const { return _cowdata.get(p_index); }
+ _FORCE_INLINE_ CharType get(int p_index) const { return _cowdata.get(p_index); }
_FORCE_INLINE_ void set(int p_index, const CharType &p_elem) { _cowdata.set(p_index, p_elem); }
_FORCE_INLINE_ int size() const { return _cowdata.size(); }
Error resize(int p_size) { return _cowdata.resize(p_size); }
@@ -197,7 +195,7 @@ public:
String substr(int p_from, int p_chars) const;
int find(const String &p_str, int p_from = 0) const; ///< return <0 if failed
int find(const char *p_str, int p_from = 0) const; ///< return <0 if failed
- int find_char(CharType p_char, int p_from = 0) const; ///< return <0 if failed
+ int find_char(const CharType &p_char, int p_from = 0) const; ///< return <0 if failed
int find_last(const String &p_str) const; ///< return <0 if failed
int findn(const String &p_str, int p_from = 0) const; ///< return <0 if failed, case insensitive
int rfind(const String &p_str, int p_from = -1) const; ///< return <0 if failed
diff --git a/core/variant.h b/core/variant.h
index a819ba1f8c..9215d15bf0 100644
--- a/core/variant.h
+++ b/core/variant.h
@@ -69,6 +69,13 @@ typedef PoolVector<Vector2> PoolVector2Array;
typedef PoolVector<Vector3> PoolVector3Array;
typedef PoolVector<Color> PoolColorArray;
+// Temporary workaround until c++11 alignas()
+#ifdef __GNUC__
+#define GCC_ALIGNED_8 __attribute__((aligned(8)))
+#else
+#define GCC_ALIGNED_8
+#endif
+
class Variant {
public:
// If this changes the table in variant_op must be updated
@@ -132,7 +139,6 @@ private:
_FORCE_INLINE_ const ObjData &_get_obj() const;
union {
-
bool _bool;
int64_t _int;
double _real;
@@ -142,7 +148,7 @@ private:
Transform *_transform;
void *_ptr; //generic pointer
uint8_t _mem[sizeof(ObjData) > (sizeof(real_t) * 4) ? sizeof(ObjData) : (sizeof(real_t) * 4)];
- } _data;
+ } _data GCC_ALIGNED_8;
void reference(const Variant &p_variant);
void clear();
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index f6f4569dfa..25a0f3957c 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -480,7 +480,7 @@ struct _VariantCall {
VCALL_LOCALMEM0(Dictionary, clear);
VCALL_LOCALMEM1R(Dictionary, has);
VCALL_LOCALMEM1R(Dictionary, has_all);
- VCALL_LOCALMEM1(Dictionary, erase);
+ VCALL_LOCALMEM1R(Dictionary, erase);
VCALL_LOCALMEM0R(Dictionary, hash);
VCALL_LOCALMEM0R(Dictionary, keys);
VCALL_LOCALMEM0R(Dictionary, values);
@@ -1227,15 +1227,15 @@ bool Variant::has_method(const StringName &p_method) const {
#endif
}
- const _VariantCall::TypeFunc &fd = _VariantCall::type_funcs[type];
- return fd.functions.has(p_method);
+ const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[type];
+ return tf.functions.has(p_method);
}
Vector<Variant::Type> Variant::get_method_argument_types(Variant::Type p_type, const StringName &p_method) {
- const _VariantCall::TypeFunc &fd = _VariantCall::type_funcs[p_type];
+ const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[p_type];
- const Map<StringName, _VariantCall::FuncData>::Element *E = fd.functions.find(p_method);
+ const Map<StringName, _VariantCall::FuncData>::Element *E = tf.functions.find(p_method);
if (!E)
return Vector<Variant::Type>();
@@ -1244,9 +1244,9 @@ Vector<Variant::Type> Variant::get_method_argument_types(Variant::Type p_type, c
bool Variant::is_method_const(Variant::Type p_type, const StringName &p_method) {
- const _VariantCall::TypeFunc &fd = _VariantCall::type_funcs[p_type];
+ const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[p_type];
- const Map<StringName, _VariantCall::FuncData>::Element *E = fd.functions.find(p_method);
+ const Map<StringName, _VariantCall::FuncData>::Element *E = tf.functions.find(p_method);
if (!E)
return false;
@@ -1255,9 +1255,9 @@ bool Variant::is_method_const(Variant::Type p_type, const StringName &p_method)
Vector<StringName> Variant::get_method_argument_names(Variant::Type p_type, const StringName &p_method) {
- const _VariantCall::TypeFunc &fd = _VariantCall::type_funcs[p_type];
+ const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[p_type];
- const Map<StringName, _VariantCall::FuncData>::Element *E = fd.functions.find(p_method);
+ const Map<StringName, _VariantCall::FuncData>::Element *E = tf.functions.find(p_method);
if (!E)
return Vector<StringName>();
@@ -1266,9 +1266,9 @@ Vector<StringName> Variant::get_method_argument_names(Variant::Type p_type, cons
Variant::Type Variant::get_method_return_type(Variant::Type p_type, const StringName &p_method, bool *r_has_return) {
- const _VariantCall::TypeFunc &fd = _VariantCall::type_funcs[p_type];
+ const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[p_type];
- const Map<StringName, _VariantCall::FuncData>::Element *E = fd.functions.find(p_method);
+ const Map<StringName, _VariantCall::FuncData>::Element *E = tf.functions.find(p_method);
if (!E)
return Variant::NIL;
@@ -1280,9 +1280,9 @@ Variant::Type Variant::get_method_return_type(Variant::Type p_type, const String
Vector<Variant> Variant::get_method_default_arguments(Variant::Type p_type, const StringName &p_method) {
- const _VariantCall::TypeFunc &fd = _VariantCall::type_funcs[p_type];
+ const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[p_type];
- const Map<StringName, _VariantCall::FuncData>::Element *E = fd.functions.find(p_method);
+ const Map<StringName, _VariantCall::FuncData>::Element *E = tf.functions.find(p_method);
if (!E)
return Vector<Variant>();
@@ -1291,9 +1291,9 @@ Vector<Variant> Variant::get_method_default_arguments(Variant::Type p_type, cons
void Variant::get_method_list(List<MethodInfo> *p_list) const {
- const _VariantCall::TypeFunc &fd = _VariantCall::type_funcs[type];
+ const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[type];
- for (const Map<StringName, _VariantCall::FuncData>::Element *E = fd.functions.front(); E; E = E->next()) {
+ for (const Map<StringName, _VariantCall::FuncData>::Element *E = tf.functions.front(); E; E = E->next()) {
const _VariantCall::FuncData &fd = E->get();
@@ -1405,11 +1405,11 @@ Variant Variant::get_constant_value(Variant::Type p_type, const StringName &p_va
Map<StringName, int>::Element *E = cd.value.find(p_value);
if (!E) {
- Map<StringName, Variant>::Element *E = cd.variant_value.find(p_value);
- if (E) {
+ Map<StringName, Variant>::Element *F = cd.variant_value.find(p_value);
+ if (F) {
if (r_valid)
*r_valid = true;
- return E->get();
+ return F->get();
} else {
return -1;
}
diff --git a/core/variant_op.cpp b/core/variant_op.cpp
index cbc8f0e224..b40b6ce4a6 100644
--- a/core/variant_op.cpp
+++ b/core/variant_op.cpp
@@ -3496,15 +3496,15 @@ void Variant::blend(const Variant &a, const Variant &b, float c, Variant &r_dst)
case COLOR: {
const Color *ca = reinterpret_cast<const Color *>(a._data._mem);
const Color *cb = reinterpret_cast<const Color *>(b._data._mem);
- float r = ca->r + cb->r * c;
- float g = ca->g + cb->g * c;
- float b = ca->b + cb->b * c;
- float a = ca->a + cb->a * c;
- r = r > 1.0 ? 1.0 : r;
- g = g > 1.0 ? 1.0 : g;
- b = b > 1.0 ? 1.0 : b;
- a = a > 1.0 ? 1.0 : a;
- r_dst = Color(r, g, b, a);
+ float new_r = ca->r + cb->r * c;
+ float new_g = ca->g + cb->g * c;
+ float new_b = ca->b + cb->b * c;
+ float new_a = ca->a + cb->a * c;
+ new_r = new_r > 1.0 ? 1.0 : new_r;
+ new_g = new_g > 1.0 ? 1.0 : new_g;
+ new_b = new_b > 1.0 ? 1.0 : new_b;
+ new_a = new_a > 1.0 ? 1.0 : new_a;
+ r_dst = Color(new_r, new_g, new_b, new_a);
}
return;
default: {
diff --git a/core/variant_parser.cpp b/core/variant_parser.cpp
index 84fb85e812..0056fc75b6 100644
--- a/core/variant_parser.cpp
+++ b/core/variant_parser.cpp
@@ -728,7 +728,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
bool at_key = true;
String key;
- Token token;
+ Token token2;
bool need_comma = false;
while (true) {
@@ -740,11 +740,11 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
if (at_key) {
- Error err = get_token(p_stream, token, line, r_err_str);
+ Error err = get_token(p_stream, token2, line, r_err_str);
if (err != OK)
return err;
- if (token.type == TK_PARENTHESIS_CLOSE) {
+ if (token2.type == TK_PARENTHESIS_CLOSE) {
Reference *reference = Object::cast_to<Reference>(obj);
if (reference) {
value = REF(reference);
@@ -756,7 +756,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
if (need_comma) {
- if (token.type != TK_COMMA) {
+ if (token2.type != TK_COMMA) {
r_err_str = "Expected '}' or ','";
return ERR_PARSE_ERROR;
@@ -766,18 +766,18 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
}
}
- if (token.type != TK_STRING) {
+ if (token2.type != TK_STRING) {
r_err_str = "Expected property name as string";
return ERR_PARSE_ERROR;
}
- key = token.value;
+ key = token2.value;
- err = get_token(p_stream, token, line, r_err_str);
+ err = get_token(p_stream, token2, line, r_err_str);
if (err != OK)
return err;
- if (token.type != TK_COLON) {
+ if (token2.type != TK_COLON) {
r_err_str = "Expected ':'";
return ERR_PARSE_ERROR;
@@ -785,12 +785,12 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
at_key = false;
} else {
- Error err = get_token(p_stream, token, line, r_err_str);
+ Error err = get_token(p_stream, token2, line, r_err_str);
if (err != OK)
return err;
Variant v;
- err = parse_value(token, v, p_stream, line, r_err_str, p_res_parser);
+ err = parse_value(token2, v, p_stream, line, r_err_str, p_res_parser);
if (err)
return err;
obj->set(key, v);
@@ -882,11 +882,11 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
return ERR_PARSE_ERROR;
}
- String id = token.value;
+ String id2 = token.value;
Ref<InputEvent> ie;
- if (id == "NONE") {
+ if (id2 == "NONE") {
get_token(p_stream, token, line, r_err_str);
@@ -895,7 +895,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
return ERR_PARSE_ERROR;
}
- } else if (id == "KEY") {
+ } else if (id2 == "KEY") {
Ref<InputEventKey> key;
key.instance();
@@ -954,7 +954,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
return ERR_PARSE_ERROR;
}
- } else if (id == "MBUTTON") {
+ } else if (id2 == "MBUTTON") {
Ref<InputEventMouseButton> mb;
mb.instance();
@@ -980,7 +980,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
return ERR_PARSE_ERROR;
}
- } else if (id == "JBUTTON") {
+ } else if (id2 == "JBUTTON") {
Ref<InputEventJoypadButton> jb;
jb.instance();
@@ -1006,7 +1006,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
return ERR_PARSE_ERROR;
}
- } else if (id == "JAXIS") {
+ } else if (id2 == "JAXIS") {
Ref<InputEventJoypadMotion> jm;
jm.instance();
@@ -1597,7 +1597,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
} break;
case Variant::INT: {
- p_store_string_func(p_store_string_ud, itos(p_variant.operator int()));
+ p_store_string_func(p_store_string_ud, itos(p_variant.operator int64_t()));
} break;
case Variant::REAL: {
diff --git a/core/version.h b/core/version.h
index 46eecf6125..05fec9d0a6 100644
--- a/core/version.h
+++ b/core/version.h
@@ -41,9 +41,14 @@
#ifdef VERSION_PATCH
// Example: "3.1.4"
#define VERSION_NUMBER "" VERSION_BRANCH "." _MKSTR(VERSION_PATCH)
+// Version number encoded as hexadecimal int with one byte for each number,
+// for easy comparison from code.
+// Example: 3.1.4 will be 0x030104, making comparison easy from script.
+#define VERSION_HEX 0x10000 * VERSION_MAJOR + 0x100 * VERSION_MINOR + VERSION_PATCH
#else
// Example: "3.1"
#define VERSION_NUMBER "" VERSION_BRANCH
+#define VERSION_HEX 0x10000 * VERSION_MAJOR + 0x100 * VERSION_MINOR
#endif // VERSION_PATCH
// Describes the full configuration of that Godot version, including the version number,