summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/io/file_access_network.cpp1
-rw-r--r--core/io/http_client_tcp.cpp2
-rw-r--r--core/io/image.cpp2
-rw-r--r--core/io/ip_address.cpp4
-rw-r--r--core/io/json.cpp14
-rw-r--r--core/io/pck_packer.cpp4
-rw-r--r--core/io/resource_uid.cpp4
-rw-r--r--core/math/aabb.h8
-rw-r--r--core/math/basis.h11
-rw-r--r--core/math/camera_matrix.cpp4
-rw-r--r--core/math/camera_matrix.h10
-rw-r--r--core/math/delaunay_2d.h1
-rw-r--r--core/math/dynamic_bvh.h6
-rw-r--r--core/math/expression.cpp36
-rw-r--r--core/math/face3.h6
-rw-r--r--core/math/geometry_2d.h4
-rw-r--r--core/math/plane.h5
-rw-r--r--core/math/quaternion.h3
-rw-r--r--core/math/rect2.cpp10
-rw-r--r--core/math/rect2.h218
-rw-r--r--core/math/rect2i.cpp42
-rw-r--r--core/math/rect2i.h245
-rw-r--r--core/math/transform_2d.cpp2
-rw-r--r--core/math/transform_2d.h7
-rw-r--r--core/math/transform_3d.h9
-rw-r--r--core/math/triangulate.h1
-rw-r--r--core/math/vector2.cpp92
-rw-r--r--core/math/vector2.h112
-rw-r--r--core/math/vector2i.cpp125
-rw-r--r--core/math/vector2i.h141
-rw-r--r--core/math/vector3.h3
-rw-r--r--core/multiplayer/multiplayer_api.cpp133
-rw-r--r--core/multiplayer/multiplayer_api.h41
-rw-r--r--core/multiplayer/multiplayer_replicator.cpp791
-rw-r--r--core/multiplayer/multiplayer_replicator.h138
-rw-r--r--core/multiplayer/rpc_manager.cpp56
-rw-r--r--core/register_core_types.cpp2
-rw-r--r--core/string/char_utils.h92
-rw-r--r--core/string/translation.cpp22
-rw-r--r--core/string/ustring.cpp46
-rw-r--r--core/string/ustring.h2
-rw-r--r--core/variant/method_ptrcall.h1
-rw-r--r--core/variant/variant.h4
-rw-r--r--core/variant/variant_parser.cpp16
44 files changed, 929 insertions, 1547 deletions
diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp
index 307004b1c2..cb38ac0928 100644
--- a/core/io/file_access_network.cpp
+++ b/core/io/file_access_network.cpp
@@ -487,7 +487,6 @@ FileAccessNetwork::~FileAccessNetwork() {
FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton;
nc->lock_mutex();
- id = nc->last_id++;
nc->accesses.erase(id);
nc->unlock_mutex();
}
diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp
index e61833ce7c..f920799677 100644
--- a/core/io/http_client_tcp.cpp
+++ b/core/io/http_client_tcp.cpp
@@ -614,7 +614,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() {
for (int i = 0; i < chunk.size() - 2; i++) {
char c = chunk[i];
int v = 0;
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
v = c - '0';
} else if (c >= 'a' && c <= 'f') {
v = c - 'a' + 10;
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 9df2b6835c..4f72599faf 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -2056,7 +2056,7 @@ void Image::create(const char **p_xpm) {
for (int i = 0; i < 6; i++) {
char v = line_ptr[i];
- if (v >= '0' && v <= '9') {
+ if (is_digit(v)) {
v -= '0';
} else if (v >= 'A' && v <= 'F') {
v = (v - 'A') + 10;
diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp
index 38f99a08a4..d183c60798 100644
--- a/core/io/ip_address.cpp
+++ b/core/io/ip_address.cpp
@@ -71,7 +71,7 @@ static void _parse_hex(const String &p_string, int p_start, uint8_t *p_dst) {
int n = 0;
char32_t c = p_string[i];
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
n = c - '0';
} else if (c >= 'a' && c <= 'f') {
n = 10 + (c - 'a');
@@ -113,7 +113,7 @@ void IPAddress::_parse_ipv6(const String &p_string) {
} else if (c == '.') {
part_ipv4 = true;
- } else if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
+ } else if (is_hex_digit(c)) {
if (!part_found) {
parts[parts_idx++] = i;
part_found = true;
diff --git a/core/io/json.cpp b/core/io/json.cpp
index 7b642f6a59..4b745dff44 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -229,12 +229,12 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to
r_err_str = "Unterminated String";
return ERR_PARSE_ERROR;
}
- if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
+ if (!is_hex_digit(c)) {
r_err_str = "Malformed hex constant in string";
return ERR_PARSE_ERROR;
}
char32_t v;
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
v = c - '0';
} else if (c >= 'a' && c <= 'f') {
v = c - 'a';
@@ -265,12 +265,12 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to
r_err_str = "Unterminated String";
return ERR_PARSE_ERROR;
}
- if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
+ if (!is_hex_digit(c)) {
r_err_str = "Malformed hex constant in string";
return ERR_PARSE_ERROR;
}
char32_t v;
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
v = c - '0';
} else if (c >= 'a' && c <= 'f') {
v = c - 'a';
@@ -326,7 +326,7 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to
break;
}
- if (p_str[index] == '-' || (p_str[index] >= '0' && p_str[index] <= '9')) {
+ if (p_str[index] == '-' || is_digit(p_str[index])) {
//a number
const char32_t *rptr;
double number = String::to_float(&p_str[index], &rptr);
@@ -335,10 +335,10 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to
r_token.value = number;
return OK;
- } else if ((p_str[index] >= 'A' && p_str[index] <= 'Z') || (p_str[index] >= 'a' && p_str[index] <= 'z')) {
+ } else if (is_ascii_char(p_str[index])) {
String id;
- while ((p_str[index] >= 'A' && p_str[index] <= 'Z') || (p_str[index] >= 'a' && p_str[index] <= 'z')) {
+ while (is_ascii_char(p_str[index])) {
id += p_str[index];
index++;
}
diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp
index 221a680130..272ace3438 100644
--- a/core/io/pck_packer.cpp
+++ b/core/io/pck_packer.cpp
@@ -62,7 +62,7 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String &
int v = 0;
if (i * 2 < _key.length()) {
char32_t ct = _key[i * 2];
- if (ct >= '0' && ct <= '9') {
+ if (is_digit(ct)) {
ct = ct - '0';
} else if (ct >= 'a' && ct <= 'f') {
ct = 10 + ct - 'a';
@@ -72,7 +72,7 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String &
if (i * 2 + 1 < _key.length()) {
char32_t ct = _key[i * 2 + 1];
- if (ct >= '0' && ct <= '9') {
+ if (is_digit(ct)) {
ct = ct - '0';
} else if (ct >= 'a' && ct <= 'f') {
ct = 10 + ct - 'a';
diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp
index 1a16d5b47a..776756e64e 100644
--- a/core/io/resource_uid.cpp
+++ b/core/io/resource_uid.cpp
@@ -71,9 +71,9 @@ ResourceUID::ID ResourceUID::text_to_id(const String &p_text) const {
for (uint32_t i = 6; i < l; i++) {
uid *= base;
uint32_t c = p_text[i];
- if (c >= 'a' && c <= 'z') {
+ if (is_ascii_lower_case(c)) {
uid += c - 'a';
- } else if (c >= '0' && c <= '9') {
+ } else if (is_digit(c)) {
uid += c - '0' + char_count;
} else {
return INVALID_ID;
diff --git a/core/math/aabb.h b/core/math/aabb.h
index 3d19410ddf..cb6f05e9ea 100644
--- a/core/math/aabb.h
+++ b/core/math/aabb.h
@@ -36,13 +36,13 @@
#include "core/math/vector3.h"
/**
- * AABB / AABB (Axis Aligned Bounding Box)
- * This is implemented by a point (position) and the box size
+ * AABB (Axis Aligned Bounding Box)
+ * This is implemented by a point (position) and the box size.
*/
+
class Variant;
-class _NO_DISCARD_ AABB {
-public:
+struct _NO_DISCARD_ AABB {
Vector3 position;
Vector3 size;
diff --git a/core/math/basis.h b/core/math/basis.h
index 802da82089..683f05150c 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -34,11 +34,7 @@
#include "core/math/quaternion.h"
#include "core/math/vector3.h"
-class _NO_DISCARD_ Basis {
-private:
- void _set_diagonal(const Vector3 &p_diag);
-
-public:
+struct _NO_DISCARD_ Basis {
Vector3 elements[3] = {
Vector3(1, 0, 0),
Vector3(0, 1, 0),
@@ -263,6 +259,10 @@ public:
}
_FORCE_INLINE_ Basis() {}
+
+private:
+ // Helper method.
+ void _set_diagonal(const Vector3 &p_diag);
};
_FORCE_INLINE_ void Basis::operator*=(const Basis &p_matrix) {
@@ -334,4 +334,5 @@ real_t Basis::determinant() const {
elements[1][0] * (elements[0][1] * elements[2][2] - elements[2][1] * elements[0][2]) +
elements[2][0] * (elements[0][1] * elements[1][2] - elements[1][1] * elements[0][2]);
}
+
#endif // BASIS_H
diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp
index 2902ca59b9..f5d746ef0f 100644
--- a/core/math/camera_matrix.cpp
+++ b/core/math/camera_matrix.cpp
@@ -30,7 +30,11 @@
#include "camera_matrix.h"
+#include "core/math/aabb.h"
#include "core/math/math_funcs.h"
+#include "core/math/plane.h"
+#include "core/math/rect2.h"
+#include "core/math/transform_3d.h"
#include "core/string/print_string.h"
float CameraMatrix::determinant() const {
diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h
index da1aba7562..285d2ae384 100644
--- a/core/math/camera_matrix.h
+++ b/core/math/camera_matrix.h
@@ -31,8 +31,14 @@
#ifndef CAMERA_MATRIX_H
#define CAMERA_MATRIX_H
-#include "core/math/rect2.h"
-#include "core/math/transform_3d.h"
+#include "core/math/math_defs.h"
+#include "core/math/vector3.h"
+
+struct AABB;
+struct Plane;
+struct Rect2;
+struct Transform3D;
+struct Vector2;
struct CameraMatrix {
enum Planes {
diff --git a/core/math/delaunay_2d.h b/core/math/delaunay_2d.h
index 08f5df8472..c39997d6a9 100644
--- a/core/math/delaunay_2d.h
+++ b/core/math/delaunay_2d.h
@@ -32,6 +32,7 @@
#define DELAUNAY_2D_H
#include "core/math/rect2.h"
+#include "core/templates/vector.h"
class Delaunay2D {
public:
diff --git a/core/math/dynamic_bvh.h b/core/math/dynamic_bvh.h
index 3041cdf268..50ec2c2b30 100644
--- a/core/math/dynamic_bvh.h
+++ b/core/math/dynamic_bvh.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef DYNAMICBVH_H
-#define DYNAMICBVH_H
+#ifndef DYNAMIC_BVH_H
+#define DYNAMIC_BVH_H
#include "core/math/aabb.h"
#include "core/templates/list.h"
@@ -474,4 +474,4 @@ void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResu
} while (depth > 0);
}
-#endif // DYNAMICBVH_H
+#endif // DYNAMIC_BVH_H
diff --git a/core/math/expression.cpp b/core/math/expression.cpp
index 87f8a95970..0ddac9744e 100644
--- a/core/math/expression.cpp
+++ b/core/math/expression.cpp
@@ -37,18 +37,6 @@
#include "core/os/os.h"
#include "core/variant/variant_parser.h"
-static bool _is_number(char32_t c) {
- return (c >= '0' && c <= '9');
-}
-
-static bool _is_hex_digit(char32_t c) {
- return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
-}
-
-static bool _is_binary_digit(char32_t c) {
- return (c == '0' || c == '1');
-}
-
Error Expression::_get_token(Token &r_token) {
while (true) {
#define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++])
@@ -96,7 +84,7 @@ Error Expression::_get_token(Token &r_token) {
r_token.type = TK_INPUT;
int index = 0;
do {
- if (!_is_number(expression[str_ofs])) {
+ if (!is_digit(expression[str_ofs])) {
_set_error("Expected number after '$'");
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
@@ -105,7 +93,7 @@ Error Expression::_get_token(Token &r_token) {
index += expression[str_ofs] - '0';
str_ofs++;
- } while (_is_number(expression[str_ofs]));
+ } while (is_digit(expression[str_ofs]));
r_token.value = index;
return OK;
@@ -255,13 +243,13 @@ Error Expression::_get_token(Token &r_token) {
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
- if (!(_is_number(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
+ if (!is_hex_digit(c)) {
_set_error("Malformed hex constant in string");
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
char32_t v;
- if (_is_number(c)) {
+ if (is_digit(c)) {
v = c - '0';
} else if (c >= 'a' && c <= 'f') {
v = c - 'a';
@@ -336,7 +324,7 @@ Error Expression::_get_token(Token &r_token) {
}
char32_t next_char = (str_ofs >= expression.length()) ? 0 : expression[str_ofs];
- if (_is_number(cchar) || (cchar == '.' && _is_number(next_char))) {
+ if (is_digit(cchar) || (cchar == '.' && is_digit(next_char))) {
//a number
String num;
@@ -360,7 +348,7 @@ Error Expression::_get_token(Token &r_token) {
while (true) {
switch (reading) {
case READING_INT: {
- if (_is_number(c)) {
+ if (is_digit(c)) {
if (is_first_char && c == '0') {
if (next_char == 'b') {
reading = READING_BIN;
@@ -380,7 +368,7 @@ Error Expression::_get_token(Token &r_token) {
} break;
case READING_BIN: {
- if (bin_beg && !_is_binary_digit(c)) {
+ if (bin_beg && !is_binary_digit(c)) {
reading = READING_DONE;
} else if (c == 'b') {
bin_beg = true;
@@ -388,7 +376,7 @@ Error Expression::_get_token(Token &r_token) {
} break;
case READING_HEX: {
- if (hex_beg && !_is_hex_digit(c)) {
+ if (hex_beg && !is_hex_digit(c)) {
reading = READING_DONE;
} else if (c == 'x') {
hex_beg = true;
@@ -396,7 +384,7 @@ Error Expression::_get_token(Token &r_token) {
} break;
case READING_DEC: {
- if (_is_number(c)) {
+ if (is_digit(c)) {
} else if (c == 'e') {
reading = READING_EXP;
@@ -406,7 +394,7 @@ Error Expression::_get_token(Token &r_token) {
} break;
case READING_EXP: {
- if (_is_number(c)) {
+ if (is_digit(c)) {
exp_beg = true;
} else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) {
@@ -441,11 +429,11 @@ Error Expression::_get_token(Token &r_token) {
}
return OK;
- } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') {
+ } else if (is_ascii_char(cchar) || is_underscore(cchar)) {
String id;
bool first = true;
- while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && _is_number(cchar))) {
+ while (is_ascii_char(cchar) || is_underscore(cchar) || (!first && is_digit(cchar))) {
id += String::chr(cchar);
cchar = GET_CHAR();
first = false;
diff --git a/core/math/face3.h b/core/math/face3.h
index 3dbbca09e0..8b123f078c 100644
--- a/core/math/face3.h
+++ b/core/math/face3.h
@@ -36,8 +36,7 @@
#include "core/math/transform_3d.h"
#include "core/math/vector3.h"
-class _NO_DISCARD_ Face3 {
-public:
+struct _NO_DISCARD_ Face3 {
enum Side {
SIDE_OVER,
SIDE_UNDER,
@@ -48,14 +47,11 @@ public:
Vector3 vertex[3];
/**
- *
* @param p_plane plane used to split the face
* @param p_res array of at least 3 faces, amount used in function return
* @param p_is_point_over array of at least 3 booleans, determining which face is over the plane, amount used in function return
- * @param _epsilon constant used for numerical error rounding, to add "thickness" to the plane (so coplanar points can happen)
* @return amount of faces generated by the split, either 0 (means no split possible), 2 or 3
*/
-
int split_by_plane(const Plane &p_plane, Face3 *p_res, bool *p_is_point_over) const;
Plane get_plane(ClockDirection p_dir = CLOCKWISE) const;
diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h
index 7385dba438..a2881d5f60 100644
--- a/core/math/geometry_2d.h
+++ b/core/math/geometry_2d.h
@@ -32,7 +32,11 @@
#define GEOMETRY_2D_H
#include "core/math/delaunay_2d.h"
+#include "core/math/math_funcs.h"
#include "core/math/triangulate.h"
+#include "core/math/vector2.h"
+#include "core/math/vector2i.h"
+#include "core/math/vector3.h"
#include "core/math/vector3i.h"
#include "core/templates/vector.h"
diff --git a/core/math/plane.h b/core/math/plane.h
index 8cb6f62b3b..66c1741662 100644
--- a/core/math/plane.h
+++ b/core/math/plane.h
@@ -35,13 +35,12 @@
class Variant;
-class _NO_DISCARD_ Plane {
-public:
+struct _NO_DISCARD_ Plane {
Vector3 normal;
real_t d = 0;
void set_normal(const Vector3 &p_normal);
- _FORCE_INLINE_ Vector3 get_normal() const { return normal; }; ///Point is coplanar, CMP_EPSILON for precision
+ _FORCE_INLINE_ Vector3 get_normal() const { return normal; };
void normalize();
Plane normalized() const;
diff --git a/core/math/quaternion.h b/core/math/quaternion.h
index 2575d7d229..7874e4f428 100644
--- a/core/math/quaternion.h
+++ b/core/math/quaternion.h
@@ -36,8 +36,7 @@
#include "core/math/vector3.h"
#include "core/string/ustring.h"
-class _NO_DISCARD_ Quaternion {
-public:
+struct _NO_DISCARD_ Quaternion {
union {
struct {
real_t x;
diff --git a/core/math/rect2.cpp b/core/math/rect2.cpp
index 9047c19434..d6e20bdc3c 100644
--- a/core/math/rect2.cpp
+++ b/core/math/rect2.cpp
@@ -28,7 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "core/math/transform_2d.h" // Includes rect2.h but Rect2 needs Transform2D
+#include "rect2.h"
+
+#include "core/math/rect2i.h"
+#include "core/math/transform_2d.h"
+#include "core/string/ustring.h"
bool Rect2::is_equal_approx(const Rect2 &p_rect) const {
return position.is_equal_approx(p_rect.position) && size.is_equal_approx(p_rect.size);
@@ -278,6 +282,6 @@ Rect2::operator String() const {
return "[P: " + position.operator String() + ", S: " + size + "]";
}
-Rect2i::operator String() const {
- return "[P: " + position.operator String() + ", S: " + size + "]";
+Rect2::operator Rect2i() const {
+ return Rect2i(position, size);
}
diff --git a/core/math/rect2.h b/core/math/rect2.h
index b14c69302c..6ecc02336c 100644
--- a/core/math/rect2.h
+++ b/core/math/rect2.h
@@ -31,8 +31,11 @@
#ifndef RECT2_H
#define RECT2_H
-#include "core/math/vector2.h" // also includes math_funcs and ustring
+#include "core/error/error_macros.h"
+#include "core/math/vector2.h"
+class String;
+struct Rect2i;
struct Transform2D;
struct _NO_DISCARD_ Rect2 {
@@ -179,6 +182,7 @@ struct _NO_DISCARD_ Rect2 {
return new_rect;
}
+
inline bool has_point(const Point2 &p_point) const {
#ifdef MATH_CHECKS
if (unlikely(size.x < 0 || size.y < 0)) {
@@ -201,6 +205,7 @@ struct _NO_DISCARD_ Rect2 {
return true;
}
+
bool is_equal_approx(const Rect2 &p_rect) const;
bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; }
@@ -351,6 +356,7 @@ struct _NO_DISCARD_ Rect2 {
}
operator String() const;
+ operator Rect2i() const;
Rect2() {}
Rect2(real_t p_x, real_t p_y, real_t p_width, real_t p_height) :
@@ -363,214 +369,4 @@ struct _NO_DISCARD_ Rect2 {
}
};
-struct _NO_DISCARD_ Rect2i {
- Point2i position;
- Size2i size;
-
- const Point2i &get_position() const { return position; }
- void set_position(const Point2i &p_position) { position = p_position; }
- const Size2i &get_size() const { return size; }
- void set_size(const Size2i &p_size) { size = p_size; }
-
- int get_area() const { return size.width * size.height; }
-
- _FORCE_INLINE_ Vector2i get_center() const { return position + (size / 2); }
-
- inline bool intersects(const Rect2i &p_rect) const {
-#ifdef MATH_CHECKS
- if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) {
- ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
- }
-#endif
- 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 bool encloses(const Rect2i &p_rect) const {
-#ifdef MATH_CHECKS
- if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) {
- ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
- }
-#endif
- return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) &&
- ((p_rect.position.x + p_rect.size.x) <= (position.x + size.x)) &&
- ((p_rect.position.y + p_rect.size.y) <= (position.y + size.y));
- }
-
- _FORCE_INLINE_ bool has_no_area() const {
- return (size.x <= 0 || size.y <= 0);
- }
-
- // Returns the instersection between two Rect2is or an empty Rect2i if there is no intersection
- inline Rect2i intersection(const Rect2i &p_rect) const {
- Rect2i new_rect = p_rect;
-
- if (!intersects(new_rect)) {
- return Rect2i();
- }
-
- new_rect.position.x = MAX(p_rect.position.x, position.x);
- new_rect.position.y = MAX(p_rect.position.y, position.y);
-
- Point2i p_rect_end = p_rect.position + p_rect.size;
- Point2i end = position + size;
-
- new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x;
- new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y;
-
- return new_rect;
- }
-
- inline Rect2i merge(const Rect2i &p_rect) const { ///< return a merged rect
-#ifdef MATH_CHECKS
- if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) {
- ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
- }
-#endif
- Rect2i new_rect;
-
- new_rect.position.x = MIN(p_rect.position.x, position.x);
- new_rect.position.y = MIN(p_rect.position.y, position.y);
-
- new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x);
- new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y);
-
- new_rect.size = new_rect.size - new_rect.position; //make relative again
-
- return new_rect;
- }
- bool has_point(const Point2i &p_point) const {
-#ifdef MATH_CHECKS
- if (unlikely(size.x < 0 || size.y < 0)) {
- ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
- }
-#endif
- if (p_point.x < position.x) {
- return false;
- }
- if (p_point.y < position.y) {
- return false;
- }
-
- if (p_point.x >= (position.x + size.x)) {
- return false;
- }
- if (p_point.y >= (position.y + size.y)) {
- return false;
- }
-
- return true;
- }
-
- bool operator==(const Rect2i &p_rect) const { return position == p_rect.position && size == p_rect.size; }
- bool operator!=(const Rect2i &p_rect) const { return position != p_rect.position || size != p_rect.size; }
-
- Rect2i grow(int p_amount) const {
- Rect2i g = *this;
- g.position.x -= p_amount;
- g.position.y -= p_amount;
- g.size.width += p_amount * 2;
- g.size.height += p_amount * 2;
- return g;
- }
-
- inline Rect2i grow_side(Side p_side, int p_amount) const {
- Rect2i g = *this;
- g = g.grow_individual((SIDE_LEFT == p_side) ? p_amount : 0,
- (SIDE_TOP == p_side) ? p_amount : 0,
- (SIDE_RIGHT == p_side) ? p_amount : 0,
- (SIDE_BOTTOM == p_side) ? p_amount : 0);
- return g;
- }
-
- inline Rect2i grow_side_bind(uint32_t p_side, int p_amount) const {
- return grow_side(Side(p_side), p_amount);
- }
-
- inline Rect2i grow_individual(int p_left, int p_top, int p_right, int p_bottom) const {
- Rect2i g = *this;
- g.position.x -= p_left;
- g.position.y -= p_top;
- g.size.width += p_left + p_right;
- g.size.height += p_top + p_bottom;
-
- return g;
- }
-
- _FORCE_INLINE_ Rect2i expand(const Vector2i &p_vector) const {
- Rect2i r = *this;
- r.expand_to(p_vector);
- return r;
- }
-
- inline void expand_to(const Point2i &p_vector) {
-#ifdef MATH_CHECKS
- if (unlikely(size.x < 0 || size.y < 0)) {
- ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
- }
-#endif
- Point2i begin = position;
- Point2i end = position + size;
-
- if (p_vector.x < begin.x) {
- begin.x = p_vector.x;
- }
- if (p_vector.y < begin.y) {
- begin.y = p_vector.y;
- }
-
- if (p_vector.x > end.x) {
- end.x = p_vector.x;
- }
- if (p_vector.y > end.y) {
- end.y = p_vector.y;
- }
-
- position = begin;
- size = end - begin;
- }
-
- _FORCE_INLINE_ Rect2i abs() const {
- return Rect2i(Point2i(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs());
- }
-
- _FORCE_INLINE_ void set_end(const Vector2i &p_end) {
- size = p_end - position;
- }
-
- _FORCE_INLINE_ Vector2i get_end() const {
- return position + size;
- }
-
- operator String() const;
-
- operator Rect2() const { return Rect2(position, size); }
-
- Rect2i() {}
- Rect2i(const Rect2 &p_r2) :
- position(p_r2.position),
- size(p_r2.size) {
- }
- Rect2i(int p_x, int p_y, int p_width, int p_height) :
- position(Point2i(p_x, p_y)),
- size(Size2i(p_width, p_height)) {
- }
- Rect2i(const Point2i &p_pos, const Size2i &p_size) :
- position(p_pos),
- size(p_size) {
- }
-};
-
#endif // RECT2_H
diff --git a/core/math/rect2i.cpp b/core/math/rect2i.cpp
new file mode 100644
index 0000000000..0782c450d0
--- /dev/null
+++ b/core/math/rect2i.cpp
@@ -0,0 +1,42 @@
+/*************************************************************************/
+/* rect2i.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 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 "rect2i.h"
+
+#include "core/math/rect2.h"
+#include "core/string/ustring.h"
+
+Rect2i::operator String() const {
+ return "[P: " + position.operator String() + ", S: " + size + "]";
+}
+
+Rect2i::operator Rect2() const {
+ return Rect2(position, size);
+}
diff --git a/core/math/rect2i.h b/core/math/rect2i.h
new file mode 100644
index 0000000000..db1459a3e6
--- /dev/null
+++ b/core/math/rect2i.h
@@ -0,0 +1,245 @@
+/*************************************************************************/
+/* rect2i.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 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 RECT2I_H
+#define RECT2I_H
+
+#include "core/error/error_macros.h"
+#include "core/math/vector2i.h"
+
+class String;
+struct Rect2;
+
+struct _NO_DISCARD_ Rect2i {
+ Point2i position;
+ Size2i size;
+
+ const Point2i &get_position() const { return position; }
+ void set_position(const Point2i &p_position) { position = p_position; }
+ const Size2i &get_size() const { return size; }
+ void set_size(const Size2i &p_size) { size = p_size; }
+
+ int get_area() const { return size.width * size.height; }
+
+ _FORCE_INLINE_ Vector2i get_center() const { return position + (size / 2); }
+
+ inline bool intersects(const Rect2i &p_rect) const {
+#ifdef MATH_CHECKS
+ if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) {
+ ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
+ }
+#endif
+ 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 bool encloses(const Rect2i &p_rect) const {
+#ifdef MATH_CHECKS
+ if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) {
+ ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
+ }
+#endif
+ return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) &&
+ ((p_rect.position.x + p_rect.size.x) <= (position.x + size.x)) &&
+ ((p_rect.position.y + p_rect.size.y) <= (position.y + size.y));
+ }
+
+ _FORCE_INLINE_ bool has_no_area() const {
+ return (size.x <= 0 || size.y <= 0);
+ }
+
+ // Returns the instersection between two Rect2is or an empty Rect2i if there is no intersection
+ inline Rect2i intersection(const Rect2i &p_rect) const {
+ Rect2i new_rect = p_rect;
+
+ if (!intersects(new_rect)) {
+ return Rect2i();
+ }
+
+ new_rect.position.x = MAX(p_rect.position.x, position.x);
+ new_rect.position.y = MAX(p_rect.position.y, position.y);
+
+ Point2i p_rect_end = p_rect.position + p_rect.size;
+ Point2i end = position + size;
+
+ new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x;
+ new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y;
+
+ return new_rect;
+ }
+
+ inline Rect2i merge(const Rect2i &p_rect) const { ///< return a merged rect
+#ifdef MATH_CHECKS
+ if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) {
+ ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
+ }
+#endif
+ Rect2i new_rect;
+
+ new_rect.position.x = MIN(p_rect.position.x, position.x);
+ new_rect.position.y = MIN(p_rect.position.y, position.y);
+
+ new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x);
+ new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y);
+
+ new_rect.size = new_rect.size - new_rect.position; //make relative again
+
+ return new_rect;
+ }
+ bool has_point(const Point2i &p_point) const {
+#ifdef MATH_CHECKS
+ if (unlikely(size.x < 0 || size.y < 0)) {
+ ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
+ }
+#endif
+ if (p_point.x < position.x) {
+ return false;
+ }
+ if (p_point.y < position.y) {
+ return false;
+ }
+
+ if (p_point.x >= (position.x + size.x)) {
+ return false;
+ }
+ if (p_point.y >= (position.y + size.y)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool operator==(const Rect2i &p_rect) const { return position == p_rect.position && size == p_rect.size; }
+ bool operator!=(const Rect2i &p_rect) const { return position != p_rect.position || size != p_rect.size; }
+
+ Rect2i grow(int p_amount) const {
+ Rect2i g = *this;
+ g.position.x -= p_amount;
+ g.position.y -= p_amount;
+ g.size.width += p_amount * 2;
+ g.size.height += p_amount * 2;
+ return g;
+ }
+
+ inline Rect2i grow_side(Side p_side, int p_amount) const {
+ Rect2i g = *this;
+ g = g.grow_individual((SIDE_LEFT == p_side) ? p_amount : 0,
+ (SIDE_TOP == p_side) ? p_amount : 0,
+ (SIDE_RIGHT == p_side) ? p_amount : 0,
+ (SIDE_BOTTOM == p_side) ? p_amount : 0);
+ return g;
+ }
+
+ inline Rect2i grow_side_bind(uint32_t p_side, int p_amount) const {
+ return grow_side(Side(p_side), p_amount);
+ }
+
+ inline Rect2i grow_individual(int p_left, int p_top, int p_right, int p_bottom) const {
+ Rect2i g = *this;
+ g.position.x -= p_left;
+ g.position.y -= p_top;
+ g.size.width += p_left + p_right;
+ g.size.height += p_top + p_bottom;
+
+ return g;
+ }
+
+ _FORCE_INLINE_ Rect2i expand(const Vector2i &p_vector) const {
+ Rect2i r = *this;
+ r.expand_to(p_vector);
+ return r;
+ }
+
+ inline void expand_to(const Point2i &p_vector) {
+#ifdef MATH_CHECKS
+ if (unlikely(size.x < 0 || size.y < 0)) {
+ ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
+ }
+#endif
+ Point2i begin = position;
+ Point2i end = position + size;
+
+ if (p_vector.x < begin.x) {
+ begin.x = p_vector.x;
+ }
+ if (p_vector.y < begin.y) {
+ begin.y = p_vector.y;
+ }
+
+ if (p_vector.x > end.x) {
+ end.x = p_vector.x;
+ }
+ if (p_vector.y > end.y) {
+ end.y = p_vector.y;
+ }
+
+ position = begin;
+ size = end - begin;
+ }
+
+ _FORCE_INLINE_ Rect2i abs() const {
+ return Rect2i(Point2i(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs());
+ }
+
+ _FORCE_INLINE_ void set_end(const Vector2i &p_end) {
+ size = p_end - position;
+ }
+
+ _FORCE_INLINE_ Vector2i get_end() const {
+ return position + size;
+ }
+
+ operator String() const;
+ operator Rect2() const;
+
+ Rect2i() {}
+ Rect2i(int p_x, int p_y, int p_width, int p_height) :
+ position(Point2i(p_x, p_y)),
+ size(Size2i(p_width, p_height)) {
+ }
+ Rect2i(const Point2i &p_pos, const Size2i &p_size) :
+ position(p_pos),
+ size(p_size) {
+ }
+};
+
+#endif // RECT2I_H
diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp
index 0201cf575c..e6e24e9b32 100644
--- a/core/math/transform_2d.cpp
+++ b/core/math/transform_2d.cpp
@@ -30,6 +30,8 @@
#include "transform_2d.h"
+#include "core/string/ustring.h"
+
void Transform2D::invert() {
// FIXME: this function assumes the basis is a rotation matrix, with no scaling.
// Transform2D::affine_inverse can handle matrices with scaling, so GDScript should eventually use that.
diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h
index 6c2d51bd9b..f4546c13c8 100644
--- a/core/math/transform_2d.h
+++ b/core/math/transform_2d.h
@@ -31,7 +31,12 @@
#ifndef TRANSFORM_2D_H
#define TRANSFORM_2D_H
-#include "core/math/rect2.h" // also includes vector2, math_funcs, and ustring
+#include "core/math/math_funcs.h"
+#include "core/math/rect2.h"
+#include "core/math/vector2.h"
+#include "core/templates/vector.h"
+
+class String;
struct _NO_DISCARD_ Transform2D {
// Warning #1: basis of Transform2D is stored differently from Basis. In terms of elements array, the basis matrix looks like "on paper":
diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h
index c16c278e74..3b4762e221 100644
--- a/core/math/transform_3d.h
+++ b/core/math/transform_3d.h
@@ -28,15 +28,14 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef TRANSFORM_H
-#define TRANSFORM_H
+#ifndef TRANSFORM_3D_H
+#define TRANSFORM_3D_H
#include "core/math/aabb.h"
#include "core/math/basis.h"
#include "core/math/plane.h"
-class _NO_DISCARD_ Transform3D {
-public:
+struct _NO_DISCARD_ Transform3D {
Basis basis;
Vector3 origin;
@@ -265,4 +264,4 @@ _FORCE_INLINE_ Plane Transform3D::xform_inv_fast(const Plane &p_plane, const Tra
return Plane(normal, d);
}
-#endif // TRANSFORM_H
+#endif // TRANSFORM_3D_H
diff --git a/core/math/triangulate.h b/core/math/triangulate.h
index d96bdb8cab..0bfcfcb978 100644
--- a/core/math/triangulate.h
+++ b/core/math/triangulate.h
@@ -32,6 +32,7 @@
#define TRIANGULATE_H
#include "core/math/vector2.h"
+#include "core/templates/vector.h"
/*
https://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp
index 676a0004ea..40149e8cc1 100644
--- a/core/math/vector2.cpp
+++ b/core/math/vector2.cpp
@@ -30,6 +30,9 @@
#include "vector2.h"
+#include "core/math/vector2i.h"
+#include "core/string/ustring.h"
+
real_t Vector2::angle() const {
return Math::atan2(y, x);
}
@@ -202,91 +205,6 @@ Vector2::operator String() const {
return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ")";
}
-/* Vector2i */
-
-Vector2i Vector2i::clamp(const Vector2i &p_min, const Vector2i &p_max) const {
- return Vector2i(
- CLAMP(x, p_min.x, p_max.x),
- CLAMP(y, p_min.y, p_max.y));
-}
-
-int64_t Vector2i::length_squared() const {
- return x * (int64_t)x + y * (int64_t)y;
-}
-
-double Vector2i::length() const {
- return Math::sqrt((double)length_squared());
-}
-
-Vector2i Vector2i::operator+(const Vector2i &p_v) const {
- return Vector2i(x + p_v.x, y + p_v.y);
-}
-
-void Vector2i::operator+=(const Vector2i &p_v) {
- x += p_v.x;
- y += p_v.y;
-}
-
-Vector2i Vector2i::operator-(const Vector2i &p_v) const {
- return Vector2i(x - p_v.x, y - p_v.y);
-}
-
-void Vector2i::operator-=(const Vector2i &p_v) {
- x -= p_v.x;
- y -= p_v.y;
-}
-
-Vector2i Vector2i::operator*(const Vector2i &p_v1) const {
- return Vector2i(x * p_v1.x, y * p_v1.y);
-}
-
-Vector2i Vector2i::operator*(const int32_t &rvalue) const {
- return Vector2i(x * rvalue, y * rvalue);
-}
-
-void Vector2i::operator*=(const int32_t &rvalue) {
- x *= rvalue;
- y *= rvalue;
-}
-
-Vector2i Vector2i::operator/(const Vector2i &p_v1) const {
- return Vector2i(x / p_v1.x, y / p_v1.y);
-}
-
-Vector2i Vector2i::operator/(const int32_t &rvalue) const {
- return Vector2i(x / rvalue, y / rvalue);
-}
-
-void Vector2i::operator/=(const int32_t &rvalue) {
- x /= rvalue;
- y /= rvalue;
-}
-
-Vector2i Vector2i::operator%(const Vector2i &p_v1) const {
- return Vector2i(x % p_v1.x, y % p_v1.y);
-}
-
-Vector2i Vector2i::operator%(const int32_t &rvalue) const {
- return Vector2i(x % rvalue, y % rvalue);
-}
-
-void Vector2i::operator%=(const int32_t &rvalue) {
- x %= rvalue;
- y %= rvalue;
-}
-
-Vector2i Vector2i::operator-() const {
- return Vector2i(-x, -y);
-}
-
-bool Vector2i::operator==(const Vector2i &p_vec2) const {
- return x == p_vec2.x && y == p_vec2.y;
-}
-
-bool Vector2i::operator!=(const Vector2i &p_vec2) const {
- return x != p_vec2.x || y != p_vec2.y;
-}
-
-Vector2i::operator String() const {
- return "(" + itos(x) + ", " + itos(y) + ")";
+Vector2::operator Vector2i() const {
+ return Vector2i(x, y);
}
diff --git a/core/math/vector2.h b/core/math/vector2.h
index af40b9e68d..9edaaebf89 100644
--- a/core/math/vector2.h
+++ b/core/math/vector2.h
@@ -32,8 +32,8 @@
#define VECTOR2_H
#include "core/math/math_funcs.h"
-#include "core/string/ustring.h"
+class String;
struct Vector2i;
struct _NO_DISCARD_ Vector2 {
@@ -167,6 +167,7 @@ struct _NO_DISCARD_ Vector2 {
real_t aspect() const { return width / height; }
operator String() const;
+ operator Vector2i() const;
_FORCE_INLINE_ Vector2() {}
_FORCE_INLINE_ Vector2(const real_t p_x, const real_t p_y) {
@@ -282,113 +283,4 @@ Vector2 Vector2::direction_to(const Vector2 &p_to) const {
typedef Vector2 Size2;
typedef Vector2 Point2;
-/* INTEGER STUFF */
-
-struct _NO_DISCARD_ Vector2i {
- enum Axis {
- AXIS_X,
- AXIS_Y,
- };
-
- union {
- int32_t x = 0;
- int32_t width;
- };
- union {
- int32_t y = 0;
- int32_t height;
- };
-
- _FORCE_INLINE_ int32_t &operator[](int p_idx) {
- return p_idx ? y : x;
- }
- _FORCE_INLINE_ const int32_t &operator[](int p_idx) const {
- return p_idx ? y : x;
- }
-
- _FORCE_INLINE_ Vector2i::Axis min_axis_index() const {
- return x < y ? Vector2i::AXIS_X : Vector2i::AXIS_Y;
- }
-
- _FORCE_INLINE_ Vector2i::Axis max_axis_index() const {
- return x < y ? Vector2i::AXIS_Y : Vector2i::AXIS_X;
- }
-
- Vector2i min(const Vector2i &p_vector2i) const {
- return Vector2(MIN(x, p_vector2i.x), MIN(y, p_vector2i.y));
- }
-
- Vector2i max(const Vector2i &p_vector2i) const {
- return Vector2(MAX(x, p_vector2i.x), MAX(y, p_vector2i.y));
- }
-
- Vector2i operator+(const Vector2i &p_v) const;
- void operator+=(const Vector2i &p_v);
- Vector2i operator-(const Vector2i &p_v) const;
- void operator-=(const Vector2i &p_v);
- Vector2i operator*(const Vector2i &p_v1) const;
-
- Vector2i operator*(const int32_t &rvalue) const;
- void operator*=(const int32_t &rvalue);
-
- Vector2i operator/(const Vector2i &p_v1) const;
- Vector2i operator/(const int32_t &rvalue) const;
- void operator/=(const int32_t &rvalue);
-
- Vector2i operator%(const Vector2i &p_v1) const;
- Vector2i operator%(const int32_t &rvalue) const;
- void operator%=(const int32_t &rvalue);
-
- Vector2i operator-() const;
- bool operator<(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); }
- bool operator>(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y > p_vec2.y) : (x > p_vec2.x); }
-
- bool operator<=(const Vector2i &p_vec2) const { return x == p_vec2.x ? (y <= p_vec2.y) : (x < p_vec2.x); }
- bool operator>=(const Vector2i &p_vec2) const { return x == p_vec2.x ? (y >= p_vec2.y) : (x > p_vec2.x); }
-
- bool operator==(const Vector2i &p_vec2) const;
- bool operator!=(const Vector2i &p_vec2) const;
-
- int64_t length_squared() const;
- double length() const;
-
- real_t aspect() const { return width / (real_t)height; }
- Vector2i sign() const { return Vector2i(SIGN(x), SIGN(y)); }
- Vector2i abs() const { return Vector2i(ABS(x), ABS(y)); }
- Vector2i clamp(const Vector2i &p_min, const Vector2i &p_max) const;
-
- operator String() const;
-
- operator Vector2() const { return Vector2(x, y); }
-
- inline Vector2i() {}
- inline Vector2i(const Vector2 &p_vec2) {
- x = (int32_t)p_vec2.x;
- y = (int32_t)p_vec2.y;
- }
- inline Vector2i(const int32_t p_x, const int32_t p_y) {
- x = p_x;
- y = p_y;
- }
-};
-
-_FORCE_INLINE_ Vector2i operator*(const int32_t &p_scalar, const Vector2i &p_vector) {
- return p_vector * p_scalar;
-}
-
-_FORCE_INLINE_ Vector2i operator*(const int64_t &p_scalar, const Vector2i &p_vector) {
- return p_vector * p_scalar;
-}
-
-_FORCE_INLINE_ Vector2i operator*(const float &p_scalar, const Vector2i &p_vector) {
- return p_vector * p_scalar;
-}
-
-_FORCE_INLINE_ Vector2i operator*(const double &p_scalar, const Vector2i &p_vector) {
- return p_vector * p_scalar;
-}
-
-typedef Vector2i Size2i;
-typedef Vector2i Point2i;
-
#endif // VECTOR2_H
diff --git a/core/math/vector2i.cpp b/core/math/vector2i.cpp
new file mode 100644
index 0000000000..dfed42e4d6
--- /dev/null
+++ b/core/math/vector2i.cpp
@@ -0,0 +1,125 @@
+/*************************************************************************/
+/* vector2i.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 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 "vector2i.h"
+
+#include "core/math/vector2.h"
+#include "core/string/ustring.h"
+
+Vector2i Vector2i::clamp(const Vector2i &p_min, const Vector2i &p_max) const {
+ return Vector2i(
+ CLAMP(x, p_min.x, p_max.x),
+ CLAMP(y, p_min.y, p_max.y));
+}
+
+int64_t Vector2i::length_squared() const {
+ return x * (int64_t)x + y * (int64_t)y;
+}
+
+double Vector2i::length() const {
+ return Math::sqrt((double)length_squared());
+}
+
+Vector2i Vector2i::operator+(const Vector2i &p_v) const {
+ return Vector2i(x + p_v.x, y + p_v.y);
+}
+
+void Vector2i::operator+=(const Vector2i &p_v) {
+ x += p_v.x;
+ y += p_v.y;
+}
+
+Vector2i Vector2i::operator-(const Vector2i &p_v) const {
+ return Vector2i(x - p_v.x, y - p_v.y);
+}
+
+void Vector2i::operator-=(const Vector2i &p_v) {
+ x -= p_v.x;
+ y -= p_v.y;
+}
+
+Vector2i Vector2i::operator*(const Vector2i &p_v1) const {
+ return Vector2i(x * p_v1.x, y * p_v1.y);
+}
+
+Vector2i Vector2i::operator*(const int32_t &rvalue) const {
+ return Vector2i(x * rvalue, y * rvalue);
+}
+
+void Vector2i::operator*=(const int32_t &rvalue) {
+ x *= rvalue;
+ y *= rvalue;
+}
+
+Vector2i Vector2i::operator/(const Vector2i &p_v1) const {
+ return Vector2i(x / p_v1.x, y / p_v1.y);
+}
+
+Vector2i Vector2i::operator/(const int32_t &rvalue) const {
+ return Vector2i(x / rvalue, y / rvalue);
+}
+
+void Vector2i::operator/=(const int32_t &rvalue) {
+ x /= rvalue;
+ y /= rvalue;
+}
+
+Vector2i Vector2i::operator%(const Vector2i &p_v1) const {
+ return Vector2i(x % p_v1.x, y % p_v1.y);
+}
+
+Vector2i Vector2i::operator%(const int32_t &rvalue) const {
+ return Vector2i(x % rvalue, y % rvalue);
+}
+
+void Vector2i::operator%=(const int32_t &rvalue) {
+ x %= rvalue;
+ y %= rvalue;
+}
+
+Vector2i Vector2i::operator-() const {
+ return Vector2i(-x, -y);
+}
+
+bool Vector2i::operator==(const Vector2i &p_vec2) const {
+ return x == p_vec2.x && y == p_vec2.y;
+}
+
+bool Vector2i::operator!=(const Vector2i &p_vec2) const {
+ return x != p_vec2.x || y != p_vec2.y;
+}
+
+Vector2i::operator String() const {
+ return "(" + itos(x) + ", " + itos(y) + ")";
+}
+
+Vector2i::operator Vector2() const {
+ return Vector2((int32_t)x, (int32_t)y);
+}
diff --git a/core/math/vector2i.h b/core/math/vector2i.h
new file mode 100644
index 0000000000..446e05f5dd
--- /dev/null
+++ b/core/math/vector2i.h
@@ -0,0 +1,141 @@
+/*************************************************************************/
+/* vector2i.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 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 VECTOR2I_H
+#define VECTOR2I_H
+
+#include "core/math/math_funcs.h"
+
+class String;
+struct Vector2;
+
+struct _NO_DISCARD_ Vector2i {
+ enum Axis {
+ AXIS_X,
+ AXIS_Y,
+ };
+
+ union {
+ int32_t x = 0;
+ int32_t width;
+ };
+ union {
+ int32_t y = 0;
+ int32_t height;
+ };
+
+ _FORCE_INLINE_ int32_t &operator[](int p_idx) {
+ return p_idx ? y : x;
+ }
+ _FORCE_INLINE_ const int32_t &operator[](int p_idx) const {
+ return p_idx ? y : x;
+ }
+
+ _FORCE_INLINE_ Vector2i::Axis min_axis_index() const {
+ return x < y ? Vector2i::AXIS_X : Vector2i::AXIS_Y;
+ }
+
+ _FORCE_INLINE_ Vector2i::Axis max_axis_index() const {
+ return x < y ? Vector2i::AXIS_Y : Vector2i::AXIS_X;
+ }
+
+ Vector2i min(const Vector2i &p_vector2i) const {
+ return Vector2i(MIN(x, p_vector2i.x), MIN(y, p_vector2i.y));
+ }
+
+ Vector2i max(const Vector2i &p_vector2i) const {
+ return Vector2i(MAX(x, p_vector2i.x), MAX(y, p_vector2i.y));
+ }
+
+ Vector2i operator+(const Vector2i &p_v) const;
+ void operator+=(const Vector2i &p_v);
+ Vector2i operator-(const Vector2i &p_v) const;
+ void operator-=(const Vector2i &p_v);
+ Vector2i operator*(const Vector2i &p_v1) const;
+
+ Vector2i operator*(const int32_t &rvalue) const;
+ void operator*=(const int32_t &rvalue);
+
+ Vector2i operator/(const Vector2i &p_v1) const;
+ Vector2i operator/(const int32_t &rvalue) const;
+ void operator/=(const int32_t &rvalue);
+
+ Vector2i operator%(const Vector2i &p_v1) const;
+ Vector2i operator%(const int32_t &rvalue) const;
+ void operator%=(const int32_t &rvalue);
+
+ Vector2i operator-() const;
+ bool operator<(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); }
+ bool operator>(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y > p_vec2.y) : (x > p_vec2.x); }
+
+ bool operator<=(const Vector2i &p_vec2) const { return x == p_vec2.x ? (y <= p_vec2.y) : (x < p_vec2.x); }
+ bool operator>=(const Vector2i &p_vec2) const { return x == p_vec2.x ? (y >= p_vec2.y) : (x > p_vec2.x); }
+
+ bool operator==(const Vector2i &p_vec2) const;
+ bool operator!=(const Vector2i &p_vec2) const;
+
+ int64_t length_squared() const;
+ double length() const;
+
+ real_t aspect() const { return width / (real_t)height; }
+ Vector2i sign() const { return Vector2i(SIGN(x), SIGN(y)); }
+ Vector2i abs() const { return Vector2i(ABS(x), ABS(y)); }
+ Vector2i clamp(const Vector2i &p_min, const Vector2i &p_max) const;
+
+ operator String() const;
+ operator Vector2() const;
+
+ inline Vector2i() {}
+ inline Vector2i(const int32_t p_x, const int32_t p_y) {
+ x = p_x;
+ y = p_y;
+ }
+};
+
+_FORCE_INLINE_ Vector2i operator*(const int32_t &p_scalar, const Vector2i &p_vector) {
+ return p_vector * p_scalar;
+}
+
+_FORCE_INLINE_ Vector2i operator*(const int64_t &p_scalar, const Vector2i &p_vector) {
+ return p_vector * p_scalar;
+}
+
+_FORCE_INLINE_ Vector2i operator*(const float &p_scalar, const Vector2i &p_vector) {
+ return p_vector * p_scalar;
+}
+
+_FORCE_INLINE_ Vector2i operator*(const double &p_scalar, const Vector2i &p_vector) {
+ return p_vector * p_scalar;
+}
+
+typedef Vector2i Size2i;
+typedef Vector2i Point2i;
+
+#endif // VECTOR2I_H
diff --git a/core/math/vector3.h b/core/math/vector3.h
index b62edef40f..79ba5c4f15 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -35,7 +35,8 @@
#include "core/math/vector2.h"
#include "core/math/vector3i.h"
#include "core/string/ustring.h"
-class Basis;
+
+struct Basis;
struct _NO_DISCARD_ Vector3 {
static const int AXIS_COUNT = 3;
diff --git a/core/multiplayer/multiplayer_api.cpp b/core/multiplayer/multiplayer_api.cpp
index 627825246a..41d6d14696 100644
--- a/core/multiplayer/multiplayer_api.cpp
+++ b/core/multiplayer/multiplayer_api.cpp
@@ -32,7 +32,6 @@
#include "core/debugger/engine_debugger.h"
#include "core/io/marshalls.h"
-#include "core/multiplayer/multiplayer_replicator.h"
#include "core/multiplayer/rpc_manager.h"
#include "scene/main/node.h"
@@ -42,6 +41,8 @@
#include "core/os/os.h"
#endif
+MultiplayerReplicationInterface *(*MultiplayerAPI::create_default_replication_interface)(MultiplayerAPI *p_multiplayer) = nullptr;
+
#ifdef DEBUG_ENABLED
void MultiplayerAPI::profile_bandwidth(const String &p_inout, int p_size) {
if (EngineDebugger::is_profiling("multiplayer")) {
@@ -74,7 +75,7 @@ void MultiplayerAPI::poll() {
Error err = multiplayer_peer->get_packet(&packet, len);
if (err != OK) {
ERR_PRINT("Error getting packet!");
- break; // Something is wrong!
+ return; // Something is wrong!
}
remote_sender_id = sender;
@@ -82,16 +83,13 @@ void MultiplayerAPI::poll() {
remote_sender_id = 0;
if (!multiplayer_peer.is_valid()) {
- break; // It's also possible that a packet or RPC caused a disconnection, so also check here.
+ return; // It's also possible that a packet or RPC caused a disconnection, so also check here.
}
}
- if (multiplayer_peer.is_valid() && multiplayer_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTED) {
- replicator->poll();
- }
+ replicator->on_network_process();
}
void MultiplayerAPI::clear() {
- replicator->clear();
connected_peers.clear();
path_get_cache.clear();
path_send_cache.clear();
@@ -133,6 +131,7 @@ void MultiplayerAPI::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) {
multiplayer_peer->connect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed));
multiplayer_peer->connect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected));
}
+ replicator->on_reset();
}
Ref<MultiplayerPeer> MultiplayerAPI::get_multiplayer_peer() const {
@@ -167,13 +166,13 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
_process_raw(p_from, p_packet, p_packet_len);
} break;
case NETWORK_COMMAND_SPAWN: {
- replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, true);
+ replicator->on_spawn_receive(p_from, p_packet, p_packet_len);
} break;
case NETWORK_COMMAND_DESPAWN: {
- replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, false);
+ replicator->on_despawn_receive(p_from, p_packet, p_packet_len);
} break;
case NETWORK_COMMAND_SYNC: {
- replicator->process_sync(p_from, p_packet, p_packet_len);
+ replicator->on_sync_receive(p_from, p_packet, p_packet_len);
} break;
}
}
@@ -324,7 +323,7 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC
#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) {
+Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_allow_object_decoding) {
// Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31
CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK);
@@ -385,7 +384,7 @@ Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint
} break;
default:
// Any other case is not yet compressed.
- Error err = encode_variant(p_variant, r_buffer, r_len, allow_object_decoding);
+ Error err = encode_variant(p_variant, r_buffer, r_len, p_allow_object_decoding);
if (err != OK) {
return err;
}
@@ -399,7 +398,7 @@ Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint
return OK;
}
-Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) {
+Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding) {
const uint8_t *buf = p_buffer;
int len = p_len;
@@ -458,7 +457,7 @@ Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const ui
}
} break;
default:
- Error err = decode_variant(r_variant, p_buffer, p_len, r_len, allow_object_decoding);
+ Error err = decode_variant(r_variant, p_buffer, p_len, r_len, p_allow_object_decoding);
if (err != OK) {
return err;
}
@@ -467,17 +466,84 @@ Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const ui
return OK;
}
+Error MultiplayerAPI::encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw, bool p_allow_object_decoding) {
+ r_len = 0;
+ int size = 0;
+
+ if (p_count == 0) {
+ if (r_raw) {
+ *r_raw = true;
+ }
+ return OK;
+ }
+
+ // Try raw encoding optimization.
+ if (r_raw && p_count == 1) {
+ *r_raw = false;
+ const Variant &v = *(p_variants[0]);
+ if (v.get_type() == Variant::PACKED_BYTE_ARRAY) {
+ *r_raw = true;
+ const PackedByteArray pba = v;
+ if (p_buffer) {
+ memcpy(p_buffer, pba.ptr(), pba.size());
+ }
+ r_len += pba.size();
+ } else {
+ encode_and_compress_variant(v, p_buffer, size, p_allow_object_decoding);
+ r_len += size;
+ }
+ return OK;
+ }
+
+ // Regular encoding.
+ for (int i = 0; i < p_count; i++) {
+ const Variant &v = *(p_variants[i]);
+ encode_and_compress_variant(v, p_buffer ? p_buffer + r_len : nullptr, size, p_allow_object_decoding);
+ r_len += size;
+ }
+ return OK;
+}
+
+Error MultiplayerAPI::decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw, bool p_allow_object_decoding) {
+ r_len = 0;
+ int argc = r_variants.size();
+ if (argc == 0 && p_raw) {
+ return OK;
+ }
+ ERR_FAIL_COND_V(p_raw && argc != 1, ERR_INVALID_DATA);
+ if (p_raw) {
+ r_len = p_len;
+ PackedByteArray pba;
+ pba.resize(p_len);
+ memcpy(pba.ptrw(), p_buffer, p_len);
+ r_variants.write[0] = pba;
+ return OK;
+ }
+
+ Vector<Variant> args;
+ Vector<const Variant *> argp;
+ args.resize(argc);
+
+ for (int i = 0; i < argc; i++) {
+ ERR_FAIL_COND_V_MSG(r_len >= p_len, ERR_INVALID_DATA, "Invalid packet received. Size too small.");
+
+ int vlen;
+ Error err = MultiplayerAPI::decode_and_decompress_variant(r_variants.write[i], &p_buffer[r_len], p_len - r_len, &vlen, p_allow_object_decoding);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid packet received. Unable to decode state variable.");
+ r_len += vlen;
+ }
+ return OK;
+}
+
void MultiplayerAPI::_add_peer(int p_id) {
connected_peers.insert(p_id);
path_get_cache.insert(p_id, PathGetCache());
- if (is_server()) {
- replicator->spawn_all(p_id);
- }
+ replicator->on_peer_change(p_id, true);
emit_signal(SNAME("peer_connected"), p_id);
}
void MultiplayerAPI::_del_peer(int p_id) {
- connected_peers.erase(p_id);
+ replicator->on_peer_change(p_id, false);
// Cleanup get cache.
path_get_cache.erase(p_id);
// Cleanup sent cache.
@@ -488,6 +554,7 @@ void MultiplayerAPI::_del_peer(int p_id) {
PathSentCache *psc = path_send_cache.getptr(E);
psc->confirmed_peers.erase(p_id);
}
+ connected_peers.erase(p_id);
emit_signal(SNAME("peer_disconnected"), p_id);
}
@@ -500,6 +567,7 @@ void MultiplayerAPI::_connection_failed() {
}
void MultiplayerAPI::_server_disconnected() {
+ replicator->on_reset();
emit_signal(SNAME("server_disconnected"));
}
@@ -612,14 +680,26 @@ bool MultiplayerAPI::is_object_decoding_allowed() const {
return allow_object_decoding;
}
-void MultiplayerAPI::scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter) {
- replicator->scene_enter_exit_notify(p_scene, p_node, p_enter);
-}
-
void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
rpc_manager->rpcp(p_node, p_peer_id, p_method, p_arg, p_argcount);
}
+Error MultiplayerAPI::spawn(Object *p_object, Variant p_config) {
+ return replicator->on_spawn(p_object, p_config);
+}
+
+Error MultiplayerAPI::despawn(Object *p_object, Variant p_config) {
+ return replicator->on_despawn(p_object, p_config);
+}
+
+Error MultiplayerAPI::replication_start(Object *p_object, Variant p_config) {
+ return replicator->on_replication_start(p_object, p_config);
+}
+
+Error MultiplayerAPI::replication_stop(Object *p_object, Variant p_config) {
+ return replicator->on_replication_stop(p_object, p_config);
+}
+
void MultiplayerAPI::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node);
ClassDB::bind_method(D_METHOD("get_root_node"), &MultiplayerAPI::get_root_node);
@@ -638,14 +718,12 @@ void MultiplayerAPI::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerAPI::is_refusing_new_connections);
ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &MultiplayerAPI::set_allow_object_decoding);
ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &MultiplayerAPI::is_object_decoding_allowed);
- ClassDB::bind_method(D_METHOD("get_replicator"), &MultiplayerAPI::get_replicator);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_multiplayer_peer", "get_multiplayer_peer");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_node", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_root_node", "get_root_node");
ADD_PROPERTY_DEFAULT("refuse_new_connections", false);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replicator", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerReplicator", PROPERTY_USAGE_NONE), "", "get_replicator");
ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id")));
@@ -656,13 +734,16 @@ void MultiplayerAPI::_bind_methods() {
}
MultiplayerAPI::MultiplayerAPI() {
- replicator = memnew(MultiplayerReplicator(this));
+ if (create_default_replication_interface) {
+ replicator = Ref<MultiplayerReplicationInterface>(create_default_replication_interface(this));
+ } else {
+ replicator.instantiate();
+ }
rpc_manager = memnew(RPCManager(this));
clear();
}
MultiplayerAPI::~MultiplayerAPI() {
clear();
- memdelete(replicator);
memdelete(rpc_manager);
}
diff --git a/core/multiplayer/multiplayer_api.h b/core/multiplayer/multiplayer_api.h
index 713035428d..f4fdafc323 100644
--- a/core/multiplayer/multiplayer_api.h
+++ b/core/multiplayer/multiplayer_api.h
@@ -35,7 +35,26 @@
#include "core/multiplayer/multiplayer_peer.h"
#include "core/object/ref_counted.h"
-class MultiplayerReplicator;
+class MultiplayerAPI;
+
+class MultiplayerReplicationInterface : public RefCounted {
+ GDCLASS(MultiplayerReplicationInterface, RefCounted);
+
+public:
+ virtual void on_peer_change(int p_id, bool p_connected) {}
+ virtual void on_reset() {}
+ virtual Error on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { return ERR_UNAVAILABLE; }
+ virtual Error on_despawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { return ERR_UNAVAILABLE; }
+ virtual Error on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { return ERR_UNAVAILABLE; }
+ virtual Error on_spawn(Object *p_obj, Variant p_config) { return ERR_UNAVAILABLE; }
+ virtual Error on_despawn(Object *p_obj, Variant p_config) { return ERR_UNAVAILABLE; }
+ virtual Error on_replication_start(Object *p_obj, Variant p_config) { return ERR_UNAVAILABLE; }
+ virtual Error on_replication_stop(Object *p_obj, Variant p_config) { return ERR_UNAVAILABLE; }
+ virtual void on_network_process() {}
+
+ MultiplayerReplicationInterface() {}
+};
+
class RPCManager;
class MultiplayerAPI : public RefCounted {
@@ -95,7 +114,7 @@ private:
Node *root_node = nullptr;
bool allow_object_decoding = false;
- MultiplayerReplicator *replicator = nullptr;
+ Ref<MultiplayerReplicationInterface> replicator;
RPCManager *rpc_manager = nullptr;
protected:
@@ -108,6 +127,13 @@ protected:
void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len);
public:
+ static MultiplayerReplicationInterface *(*create_default_replication_interface)(MultiplayerAPI *p_multiplayer);
+
+ static Error encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len, bool p_allow_object_decoding);
+ static Error decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding);
+ static Error encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw = nullptr, bool p_allow_object_decoding = false);
+ static Error decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw = false, bool p_allow_object_decoding = false);
+
void poll();
void clear();
void set_root_node(Node *p_node);
@@ -117,13 +143,13 @@ public:
Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, Multiplayer::TransferMode p_mode = Multiplayer::TRANSFER_MODE_RELIABLE, int p_channel = 0);
- 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);
-
// Called by Node.rpc
void rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
- // Called by Node._notification
- void scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter);
+ // Replication API
+ Error spawn(Object *p_object, Variant p_config);
+ Error despawn(Object *p_object, Variant p_config);
+ Error replication_start(Object *p_object, Variant p_config);
+ Error replication_stop(Object *p_object, Variant p_config);
// Called by replicator
bool send_confirm_path(Node *p_node, NodePath p_path, int p_target, int &p_id);
Node *get_cached_node(int p_from, uint32_t p_node_id);
@@ -148,7 +174,6 @@ public:
void set_allow_object_decoding(bool p_enable);
bool is_object_decoding_allowed() const;
- MultiplayerReplicator *get_replicator() const { return replicator; }
RPCManager *get_rpc_manager() const { return rpc_manager; }
#ifdef DEBUG_ENABLED
diff --git a/core/multiplayer/multiplayer_replicator.cpp b/core/multiplayer/multiplayer_replicator.cpp
deleted file mode 100644
index e7de8219c7..0000000000
--- a/core/multiplayer/multiplayer_replicator.cpp
+++ /dev/null
@@ -1,791 +0,0 @@
-/*************************************************************************/
-/* multiplayer_replicator.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "core/multiplayer/multiplayer_replicator.h"
-
-#include "core/io/marshalls.h"
-#include "scene/main/node.h"
-#include "scene/resources/packed_scene.h"
-
-#define MAKE_ROOM(m_amount) \
- if (packet_cache.size() < m_amount) \
- packet_cache.resize(m_amount);
-
-Error MultiplayerReplicator::_sync_all_default(const ResourceUID::ID &p_scene_id, int p_peer) {
- ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER);
- SceneConfig &cfg = replications[p_scene_id];
- int full_size = 0;
- bool same_size = true;
- int last_size = 0;
- bool all_raw = true;
- struct EncodeInfo {
- int size = 0;
- bool raw = false;
- List<Variant> state;
- };
- Map<ObjectID, struct EncodeInfo> state;
- if (tracked_objects.has(p_scene_id)) {
- for (const ObjectID &obj_id : tracked_objects[p_scene_id]) {
- Object *obj = ObjectDB::get_instance(obj_id);
- if (obj) {
- struct EncodeInfo info;
- Error err = _get_state(cfg.sync_properties, obj, info.state);
- ERR_CONTINUE(err);
- err = _encode_state(info.state, nullptr, info.size, &info.raw);
- ERR_CONTINUE(err);
- state[obj_id] = info;
- full_size += info.size;
- if (last_size && info.size != last_size) {
- same_size = false;
- }
- all_raw = all_raw && info.raw;
- last_size = info.size;
- }
- }
- }
- // Default implementation do not send empty updates.
- if (!full_size) {
- return OK;
- }
-#ifdef DEBUG_ENABLED
- if (full_size > 4096 && cfg.sync_interval) {
- WARN_PRINT_ONCE(vformat("The timed state update for scene %d is big (%d bytes) consider optimizing it", p_scene_id));
- }
-#endif
- if (same_size) {
- // This is fast and small. Should we allow more than 256 objects per type?
- // This costs us 1 byte.
- MAKE_ROOM(SYNC_CMD_OFFSET + 1 + 2 + 2 + full_size);
- } else {
- MAKE_ROOM(SYNC_CMD_OFFSET + 1 + 2 + state.size() * 2 + full_size);
- }
- int ofs = 0;
- uint8_t *ptr = packet_cache.ptrw();
- ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC | (same_size ? BYTE_OR_ZERO_FLAG : 0);
- ofs = 1;
- ofs += encode_uint64(p_scene_id, &ptr[ofs]);
- ptr[ofs] = cfg.sync_recv++;
- ofs += 1;
- ofs += encode_uint16(state.size(), &ptr[ofs]);
- if (same_size) {
- ofs += encode_uint16(last_size + (all_raw ? 1 << 15 : 0), &ptr[ofs]);
- }
- for (const ObjectID &obj_id : tracked_objects[p_scene_id]) {
- if (!state.has(obj_id)) {
- continue;
- }
- struct EncodeInfo &info = state[obj_id];
- Object *obj = ObjectDB::get_instance(obj_id);
- ERR_CONTINUE(!obj);
- int size = 0;
- if (!same_size) {
- // We need to encode the size of every object.
- ofs += encode_uint16(info.size + (info.raw ? 1 << 15 : 0), &ptr[ofs]);
- }
- Error err = _encode_state(info.state, &ptr[ofs], size, &info.raw);
- ERR_CONTINUE(err);
- ofs += size;
- }
- Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
- peer->set_target_peer(p_peer);
- peer->set_transfer_channel(0);
- peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_UNRELIABLE);
- return peer->put_packet(ptr, ofs);
-}
-
-void MultiplayerReplicator::_process_default_sync(const ResourceUID::ID &p_id, const uint8_t *p_packet, int p_packet_len) {
- ERR_FAIL_COND_MSG(p_packet_len < SYNC_CMD_OFFSET + 5, "Invalid spawn packet received");
- ERR_FAIL_COND_MSG(!replications.has(p_id), "Invalid spawn ID received " + itos(p_id));
- SceneConfig &cfg = replications[p_id];
- ERR_FAIL_COND_MSG(cfg.mode != REPLICATION_MODE_SERVER || multiplayer->is_server(), "The default implementation only allows sync packets from the server");
- const bool same_size = p_packet[0] & BYTE_OR_ZERO_FLAG;
- int ofs = SYNC_CMD_OFFSET;
- int time = p_packet[ofs];
- // Skip old update.
- if (time < cfg.sync_recv && cfg.sync_recv - time < 127) {
- return;
- }
- cfg.sync_recv = time;
- ofs += 1;
- int count = decode_uint16(&p_packet[ofs]);
- ofs += 2;
-#ifdef DEBUG_ENABLED
- ERR_FAIL_COND(!tracked_objects.has(p_id) || tracked_objects[p_id].size() != count);
-#else
- if (!tracked_objects.has(p_id) || tracked_objects[p_id].size() != count) {
- return;
- }
-#endif
- int data_size = 0;
- bool raw = false;
- if (same_size) {
- // This is fast and optimized.
- data_size = decode_uint16(&p_packet[ofs]);
- raw = (data_size & (1 << 15)) != 0;
- data_size = data_size & ~(1 << 15);
- ofs += 2;
- ERR_FAIL_COND(p_packet_len - ofs < data_size * count);
- }
- for (const ObjectID &obj_id : tracked_objects[p_id]) {
- Object *obj = ObjectDB::get_instance(obj_id);
- ERR_CONTINUE(!obj);
- if (!same_size) {
- // This is slow and wasteful.
- data_size = decode_uint16(&p_packet[ofs]);
- raw = (data_size & (1 << 15)) != 0;
- data_size = data_size & ~(1 << 15);
- ofs += 2;
- ERR_FAIL_COND(p_packet_len - ofs < data_size);
- }
- int size = 0;
- Error err = _decode_state(cfg.sync_properties, obj, &p_packet[ofs], data_size, size, raw);
- ofs += data_size;
- ERR_CONTINUE(err);
- ERR_CONTINUE(size != data_size);
- }
-}
-
-Error MultiplayerReplicator::_send_default_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, Object *p_obj, const NodePath &p_path, bool p_spawn) {
- ERR_FAIL_COND_V(p_spawn && !p_obj, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER);
- Error err;
- // Prepare state
- List<Variant> state_variants;
- int state_len = 0;
- const SceneConfig &cfg = replications[p_scene_id];
- if (p_spawn) {
- if ((err = _get_state(cfg.properties, p_obj, state_variants)) != OK) {
- return err;
- }
- }
-
- bool is_raw = false;
- if (state_variants.size() == 1 && state_variants[0].get_type() == Variant::PACKED_BYTE_ARRAY) {
- is_raw = true;
- const PackedByteArray pba = state_variants[0];
- state_len = pba.size();
- } else if (state_variants.size()) {
- err = _encode_state(state_variants, nullptr, state_len);
- ERR_FAIL_COND_V(err, err);
- } else {
- is_raw = true;
- }
-
- int ofs = 0;
-
- // Prepare simplified path
- const Node *root_node = multiplayer->get_root_node();
- ERR_FAIL_COND_V(!root_node, ERR_UNCONFIGURED);
- NodePath rel_path = (root_node->get_path()).rel_path_to(p_path);
- const Vector<StringName> names = rel_path.get_names();
- ERR_FAIL_COND_V(names.size() < 2, ERR_INVALID_PARAMETER);
-
- NodePath parent = NodePath(names.slice(0, names.size() - 1), false);
- ERR_FAIL_COND_V_MSG(!root_node->has_node(parent), ERR_INVALID_PARAMETER, "Path not found: " + parent);
-
- int path_id = 0;
- multiplayer->send_confirm_path(root_node->get_node(parent), parent, p_peer_id, path_id);
-
- // Encode name and parent ID.
- CharString cname = String(names[names.size() - 1]).utf8();
- int nlen = encode_cstring(cname.get_data(), nullptr);
- MAKE_ROOM(SPAWN_CMD_OFFSET + 4 + 4 + nlen + state_len);
- uint8_t *ptr = packet_cache.ptrw();
- ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) | (is_raw ? BYTE_OR_ZERO_FLAG : 0);
- ofs = 1;
- ofs += encode_uint64(p_scene_id, &ptr[ofs]);
- ofs += encode_uint32(path_id, &ptr[ofs]);
- ofs += encode_uint32(nlen, &ptr[ofs]);
- ofs += encode_cstring(cname.get_data(), &ptr[ofs]);
-
- // Encode state.
- if (!is_raw) {
- _encode_state(state_variants, &ptr[ofs], state_len);
- } else if (state_len) {
- PackedByteArray pba = state_variants[0];
- memcpy(&ptr[ofs], pba.ptr(), state_len);
- }
-
- Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
- peer->set_target_peer(p_peer_id);
- peer->set_transfer_channel(0);
- peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
- return peer->put_packet(ptr, ofs + state_len);
-}
-
-void MultiplayerReplicator::_process_default_spawn_despawn(int p_from, const ResourceUID::ID &p_scene_id, const uint8_t *p_packet, int p_packet_len, bool p_spawn) {
- ERR_FAIL_COND_MSG(p_packet_len < SPAWN_CMD_OFFSET + 9, "Invalid spawn packet received");
- int ofs = SPAWN_CMD_OFFSET;
- uint32_t node_target = decode_uint32(&p_packet[ofs]);
- Node *parent = multiplayer->get_cached_node(p_from, node_target);
- ofs += 4;
- ERR_FAIL_COND_MSG(parent == nullptr, "Invalid packet received. Requested node was not found.");
-
- uint32_t name_len = decode_uint32(&p_packet[ofs]);
- ofs += 4;
- ERR_FAIL_COND_MSG(name_len > uint32_t(p_packet_len - ofs), vformat("Invalid spawn packet size: %d, wants: %d", p_packet_len, ofs + name_len));
- ERR_FAIL_COND_MSG(name_len < 1, "Zero spawn name size.");
-
- const String name = String::utf8((const char *)&p_packet[ofs], name_len);
- // We need to make sure no trickery happens here (e.g. despawning a subpath), but we want to allow autogenerated ("@") node names.
- ERR_FAIL_COND_MSG(name.validate_node_name() != name.replace("@", ""), vformat("Invalid node name received: '%s'", name));
- ofs += name_len;
-
- const SceneConfig &cfg = replications[p_scene_id];
- if (cfg.mode == REPLICATION_MODE_SERVER && p_from == 1) {
- String scene_path = ResourceUID::get_singleton()->get_id_path(p_scene_id);
- if (p_spawn) {
- const bool is_raw = ((p_packet[0] & BYTE_OR_ZERO_FLAG) >> BYTE_OR_ZERO_SHIFT) == 1;
-
- ERR_FAIL_COND_MSG(parent->has_node(name), vformat("Unable to spawn node. Node already exists: %s/%s", parent->get_path(), name));
- RES res = ResourceLoader::load(scene_path);
- ERR_FAIL_COND_MSG(!res.is_valid(), "Unable to load scene to spawn at path: " + scene_path);
- PackedScene *scene = Object::cast_to<PackedScene>(res.ptr());
- ERR_FAIL_COND(!scene);
- Node *node = scene->instantiate();
- ERR_FAIL_COND(!node);
- replicated_nodes[node->get_instance_id()] = p_scene_id;
- _track(p_scene_id, node);
- int size;
- _decode_state(cfg.properties, node, &p_packet[ofs], p_packet_len - ofs, size, is_raw);
- parent->_add_child_nocheck(node, name);
- emit_signal(SNAME("spawned"), p_scene_id, node);
- } else {
- ERR_FAIL_COND_MSG(!parent->has_node(name), vformat("Path not found: %s/%s", parent->get_path(), name));
- Node *node = parent->get_node(name);
- ERR_FAIL_COND_MSG(!replicated_nodes.has(node->get_instance_id()), vformat("Trying to despawn a Node that was not replicated: %s/%s", parent->get_path(), name));
- emit_signal(SNAME("despawned"), p_scene_id, node);
- _untrack(p_scene_id, node);
- replicated_nodes.erase(node->get_instance_id());
- node->queue_delete();
- }
- } else {
- PackedByteArray data;
- if (p_packet_len > ofs) {
- data.resize(p_packet_len - ofs);
- memcpy(data.ptrw(), &p_packet[ofs], data.size());
- }
- if (p_spawn) {
- emit_signal(SNAME("spawn_requested"), p_from, p_scene_id, parent, name, data);
- } else {
- emit_signal(SNAME("despawn_requested"), p_from, p_scene_id, parent, name, data);
- }
- }
-}
-
-void MultiplayerReplicator::process_spawn_despawn(int p_from, const uint8_t *p_packet, int p_packet_len, bool p_spawn) {
- ERR_FAIL_COND_MSG(p_packet_len < SPAWN_CMD_OFFSET, "Invalid spawn packet received");
- ResourceUID::ID id = decode_uint64(&p_packet[1]);
- ERR_FAIL_COND_MSG(!replications.has(id), "Invalid spawn ID received " + itos(id));
-
- const SceneConfig &cfg = replications[id];
- if (cfg.on_spawn_despawn_receive.is_valid()) {
- int ofs = SPAWN_CMD_OFFSET;
- bool is_raw = ((p_packet[0] & BYTE_OR_ZERO_FLAG) >> BYTE_OR_ZERO_SHIFT) == 1;
- Variant data;
- int left = p_packet_len - ofs;
- if (is_raw && left) {
- PackedByteArray pba;
- pba.resize(left);
- memcpy(pba.ptrw(), &p_packet[ofs], pba.size());
- data = pba;
- } else if (left) {
- ERR_FAIL_COND(decode_variant(data, &p_packet[ofs], left) != OK);
- }
-
- Variant args[4];
- args[0] = p_from;
- args[1] = id;
- args[2] = data;
- args[3] = p_spawn;
- const Variant *argp[] = { &args[0], &args[1], &args[2], &args[3] };
- Callable::CallError ce;
- Variant ret;
- cfg.on_spawn_despawn_receive.call(argp, 4, ret, ce);
- ERR_FAIL_COND_MSG(ce.error != Callable::CallError::CALL_OK, "Custom receive function failed");
- } else {
- _process_default_spawn_despawn(p_from, id, p_packet, p_packet_len, p_spawn);
- }
-}
-
-void MultiplayerReplicator::process_sync(int p_from, const uint8_t *p_packet, int p_packet_len) {
- ERR_FAIL_COND_MSG(p_packet_len < SPAWN_CMD_OFFSET, "Invalid spawn packet received");
- ResourceUID::ID id = decode_uint64(&p_packet[1]);
- ERR_FAIL_COND_MSG(!replications.has(id), "Invalid spawn ID received " + itos(id));
- const SceneConfig &cfg = replications[id];
- if (cfg.on_sync_receive.is_valid()) {
- Array objs;
- if (tracked_objects.has(id)) {
- objs.resize(tracked_objects[id].size());
- int idx = 0;
- for (const ObjectID &obj_id : tracked_objects[id]) {
- objs[idx++] = ObjectDB::get_instance(obj_id);
- }
- }
- PackedByteArray pba;
- pba.resize(p_packet_len - SYNC_CMD_OFFSET);
- if (pba.size()) {
- memcpy(pba.ptrw(), p_packet + SYNC_CMD_OFFSET, p_packet_len - SYNC_CMD_OFFSET);
- }
- Variant args[4] = { p_from, id, objs, pba };
- Variant *argp[4] = { args, &args[1], &args[2], &args[3] };
- Callable::CallError ce;
- Variant ret;
- cfg.on_sync_receive.call((const Variant **)argp, 4, ret, ce);
- ERR_FAIL_COND_MSG(ce.error != Callable::CallError::CALL_OK, "Custom sync function failed");
- } else {
- ERR_FAIL_COND_MSG(p_from != 1, "Default sync implementation only allow syncing from server to client");
- _process_default_sync(id, p_packet, p_packet_len);
- }
-}
-
-Error MultiplayerReplicator::_get_state(const List<StringName> &p_properties, const Object *p_obj, List<Variant> &r_variant) {
- ERR_FAIL_COND_V_MSG(!p_obj, ERR_INVALID_PARAMETER, "Cannot encode null object");
- for (const StringName &prop : p_properties) {
- bool valid = false;
- const Variant v = p_obj->get(prop, &valid);
- ERR_FAIL_COND_V_MSG(!valid, ERR_INVALID_DATA, vformat("Property '%s' not found.", prop));
- r_variant.push_back(v);
- }
- return OK;
-}
-
-Error MultiplayerReplicator::_encode_state(const List<Variant> &p_variants, uint8_t *p_buffer, int &r_len, bool *r_raw) {
- r_len = 0;
- int size = 0;
-
- // Try raw encoding optimization.
- if (r_raw && p_variants.size() == 1) {
- *r_raw = false;
- const Variant v = p_variants[0];
- if (v.get_type() == Variant::PACKED_BYTE_ARRAY) {
- *r_raw = true;
- const PackedByteArray pba = v;
- if (p_buffer) {
- memcpy(p_buffer, pba.ptr(), pba.size());
- }
- r_len += pba.size();
- } else {
- multiplayer->encode_and_compress_variant(v, p_buffer, size);
- r_len += size;
- }
- return OK;
- }
-
- // Regular encoding.
- for (const Variant &v : p_variants) {
- multiplayer->encode_and_compress_variant(v, p_buffer ? p_buffer + r_len : nullptr, size);
- r_len += size;
- }
- return OK;
-}
-
-Error MultiplayerReplicator::_decode_state(const List<StringName> &p_properties, Object *p_obj, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw) {
- r_len = 0;
- int argc = p_properties.size();
- if (argc == 0 && p_raw) {
- ERR_FAIL_COND_V_MSG(p_len != 0, ERR_INVALID_DATA, "Buffer has trailing bytes.");
- return OK;
- }
- ERR_FAIL_COND_V(p_raw && argc != 1, ERR_INVALID_DATA);
- if (p_raw) {
- r_len = p_len;
- PackedByteArray pba;
- pba.resize(p_len);
- memcpy(pba.ptrw(), p_buffer, p_len);
- p_obj->set(p_properties[0], pba);
- return OK;
- }
-
- Vector<Variant> args;
- Vector<const Variant *> argp;
- args.resize(argc);
-
- for (int i = 0; i < argc; i++) {
- ERR_FAIL_COND_V_MSG(r_len >= p_len, ERR_INVALID_DATA, "Invalid packet received. Size too small.");
-
- int vlen;
- Error err = multiplayer->decode_and_decompress_variant(args.write[i], &p_buffer[r_len], p_len - r_len, &vlen);
- ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid packet received. Unable to decode state variable.");
- r_len += vlen;
- }
- ERR_FAIL_COND_V_MSG(p_len - r_len != 0, ERR_INVALID_DATA, "Buffer has trailing bytes.");
-
- int i = 0;
- for (const StringName &prop : p_properties) {
- p_obj->set(prop, args[i]);
- i += 1;
- }
- return OK;
-}
-
-Error MultiplayerReplicator::spawn_config(const ResourceUID::ID &p_id, ReplicationMode p_mode, const TypedArray<StringName> &p_props, const Callable &p_on_send, const Callable &p_on_recv) {
- ERR_FAIL_COND_V(p_mode < REPLICATION_MODE_NONE || p_mode > REPLICATION_MODE_CUSTOM, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(!ResourceUID::get_singleton()->has_id(p_id), ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V_MSG(p_on_send.is_valid() != p_on_recv.is_valid(), ERR_INVALID_PARAMETER, "Send and receive custom callables must be both valid or both empty");
-#ifdef TOOLS_ENABLED
- if (!p_on_send.is_valid()) {
- // We allow non scene spawning with custom callables.
- String path = ResourceUID::get_singleton()->get_id_path(p_id);
- RES res = ResourceLoader::load(path);
- ERR_FAIL_COND_V(!res->is_class("PackedScene"), ERR_INVALID_PARAMETER);
- }
-#endif
- if (p_mode == REPLICATION_MODE_NONE) {
- if (replications.has(p_id)) {
- replications.erase(p_id);
- }
- } else {
- SceneConfig cfg;
- cfg.mode = p_mode;
- for (int i = 0; i < p_props.size(); i++) {
- cfg.properties.push_back(p_props[i]);
- }
- cfg.on_spawn_despawn_send = p_on_send;
- cfg.on_spawn_despawn_receive = p_on_recv;
- replications[p_id] = cfg;
- }
- return OK;
-}
-
-Error MultiplayerReplicator::sync_config(const ResourceUID::ID &p_id, uint64_t p_interval, const TypedArray<StringName> &p_props, const Callable &p_on_send, const Callable &p_on_recv) {
- ERR_FAIL_COND_V(!ResourceUID::get_singleton()->has_id(p_id), ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V_MSG(p_on_send.is_valid() != p_on_recv.is_valid(), ERR_INVALID_PARAMETER, "Send and receive custom callables must be both valid or both empty");
- ERR_FAIL_COND_V(!replications.has(p_id), ERR_UNCONFIGURED);
- SceneConfig &cfg = replications[p_id];
- ERR_FAIL_COND_V_MSG(p_interval && cfg.mode != REPLICATION_MODE_SERVER && !p_on_send.is_valid(), ERR_INVALID_PARAMETER, "Timed updates in custom mode are only allowed if custom callbacks are also specified");
- for (int i = 0; i < p_props.size(); i++) {
- cfg.sync_properties.push_back(p_props[i]);
- }
- cfg.on_sync_send = p_on_send;
- cfg.on_sync_receive = p_on_recv;
- cfg.sync_interval = p_interval * 1000;
- return OK;
-}
-
-Error MultiplayerReplicator::_send_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, bool p_spawn) {
- int data_size = 0;
- int is_raw = false;
- if (p_data.get_type() == Variant::PACKED_BYTE_ARRAY) {
- const PackedByteArray pba = p_data;
- is_raw = true;
- data_size = p_data.operator PackedByteArray().size();
- } else if (p_data.get_type() == Variant::NIL) {
- is_raw = true;
- } else {
- Error err = encode_variant(p_data, nullptr, data_size);
- ERR_FAIL_COND_V(err, err);
- }
- MAKE_ROOM(SPAWN_CMD_OFFSET + data_size);
- uint8_t *ptr = packet_cache.ptrw();
- ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) + ((is_raw ? 1 : 0) << BYTE_OR_ZERO_SHIFT);
- encode_uint64(p_scene_id, &ptr[1]);
- if (p_data.get_type() == Variant::PACKED_BYTE_ARRAY) {
- const PackedByteArray pba = p_data;
- memcpy(&ptr[SPAWN_CMD_OFFSET], pba.ptr(), pba.size());
- } else if (data_size) {
- encode_variant(p_data, &ptr[SPAWN_CMD_OFFSET], data_size);
- }
- Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
- peer->set_target_peer(p_peer_id);
- peer->set_transfer_channel(0);
- peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
- return peer->put_packet(ptr, SPAWN_CMD_OFFSET + data_size);
-}
-
-Error MultiplayerReplicator::send_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, const NodePath &p_path) {
- ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);
- ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id));
- const SceneConfig &cfg = replications[p_scene_id];
- if (cfg.on_spawn_despawn_send.is_valid()) {
- return _send_spawn_despawn(p_peer_id, p_scene_id, p_data, true);
- } else {
- ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server(), ERR_UNAVAILABLE, "Manual despawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests.");
- NodePath path = p_path;
- Object *obj = p_data.get_type() == Variant::OBJECT ? p_data.get_validated_object() : nullptr;
- if (path.is_empty() && obj) {
- Node *node = Object::cast_to<Node>(obj);
- if (node && node->is_inside_tree()) {
- path = node->get_path();
- }
- }
- ERR_FAIL_COND_V_MSG(path.is_empty(), ERR_INVALID_PARAMETER, "Despawn default implementation requires a despawn path, or the data to be a node inside the SceneTree");
- return _send_default_spawn_despawn(p_peer_id, p_scene_id, obj, path, false);
- }
-}
-
-Error MultiplayerReplicator::send_spawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, const NodePath &p_path) {
- ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);
- ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id));
- const SceneConfig &cfg = replications[p_scene_id];
- if (cfg.on_spawn_despawn_send.is_valid()) {
- return _send_spawn_despawn(p_peer_id, p_scene_id, p_data, false);
- } else {
- ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server(), ERR_UNAVAILABLE, "Manual spawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests.");
- NodePath path = p_path;
- Object *obj = p_data.get_type() == Variant::OBJECT ? p_data.get_validated_object() : nullptr;
- ERR_FAIL_COND_V_MSG(!obj, ERR_INVALID_PARAMETER, "Spawn default implementation requires the data to be an object.");
- if (path.is_empty()) {
- Node *node = Object::cast_to<Node>(obj);
- if (node && node->is_inside_tree()) {
- path = node->get_path();
- }
- }
- ERR_FAIL_COND_V_MSG(path.is_empty(), ERR_INVALID_PARAMETER, "Spawn default implementation requires a spawn path, or the data to be a node inside the SceneTree");
- return _send_default_spawn_despawn(p_peer_id, p_scene_id, obj, path, true);
- }
-}
-
-Error MultiplayerReplicator::_spawn_despawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer, bool p_spawn) {
- ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id));
-
- const SceneConfig &cfg = replications[p_scene_id];
- if (cfg.on_spawn_despawn_send.is_valid()) {
- Variant args[4];
- args[0] = p_peer;
- args[1] = p_scene_id;
- args[2] = p_obj;
- args[3] = p_spawn;
- const Variant *argp[] = { &args[0], &args[1], &args[2], &args[3] };
- Callable::CallError ce;
- Variant ret;
- cfg.on_spawn_despawn_send.call(argp, 4, ret, ce);
- ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, FAILED, "Custom send function failed");
- return OK;
- } else {
- Node *node = Object::cast_to<Node>(p_obj);
- ERR_FAIL_COND_V_MSG(!p_obj, ERR_INVALID_PARAMETER, "Only nodes can be replicated by the default implementation");
- return _send_default_spawn_despawn(p_peer, p_scene_id, node, node->get_path(), p_spawn);
- }
-}
-
-Error MultiplayerReplicator::spawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer) {
- return _spawn_despawn(p_scene_id, p_obj, p_peer, true);
-}
-
-Error MultiplayerReplicator::despawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer) {
- return _spawn_despawn(p_scene_id, p_obj, p_peer, false);
-}
-
-PackedByteArray MultiplayerReplicator::encode_state(const ResourceUID::ID &p_scene_id, const Object *p_obj, bool p_initial) {
- PackedByteArray state;
- ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), state, vformat("Spawnable not found: %d", p_scene_id));
- const SceneConfig &cfg = replications[p_scene_id];
- int len = 0;
- List<Variant> state_vars;
- const List<StringName> props = p_initial ? cfg.properties : cfg.sync_properties;
- Error err = _get_state(props, p_obj, state_vars);
- ERR_FAIL_COND_V_MSG(err != OK, state, "Unable to retrieve object state.");
- err = _encode_state(state_vars, nullptr, len);
- ERR_FAIL_COND_V_MSG(err != OK, state, "Unable to encode object state.");
- state.resize(len);
- _encode_state(state_vars, state.ptrw(), len);
- return state;
-}
-
-Error MultiplayerReplicator::decode_state(const ResourceUID::ID &p_scene_id, Object *p_obj, const PackedByteArray p_data, bool p_initial) {
- ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id));
- const SceneConfig &cfg = replications[p_scene_id];
- const List<StringName> props = p_initial ? cfg.properties : cfg.sync_properties;
- int size;
- return _decode_state(props, p_obj, p_data.ptr(), p_data.size(), size);
-}
-
-void MultiplayerReplicator::scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter) {
- if (!multiplayer->has_multiplayer_peer()) {
- return;
- }
- Node *root_node = multiplayer->get_root_node();
- ERR_FAIL_COND(!p_node || !p_node->get_parent() || !root_node);
- NodePath path = (root_node->get_path()).rel_path_to(p_node->get_parent()->get_path());
- if (path.is_empty()) {
- return;
- }
- ResourceUID::ID id = ResourceLoader::get_resource_uid(p_scene);
- if (!replications.has(id)) {
- return;
- }
- const SceneConfig &cfg = replications[id];
- if (p_enter) {
- if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server()) {
- replicated_nodes[p_node->get_instance_id()] = id;
- _track(id, p_node);
- spawn(id, p_node, 0);
- }
- emit_signal(SNAME("replicated_instance_added"), id, p_node);
- } else {
- if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server() && replicated_nodes.has(p_node->get_instance_id())) {
- replicated_nodes.erase(p_node->get_instance_id());
- _untrack(id, p_node);
- despawn(id, p_node, 0);
- }
- emit_signal(SNAME("replicated_instance_removed"), id, p_node);
- }
-}
-
-void MultiplayerReplicator::spawn_all(int p_peer) {
- for (const KeyValue<ObjectID, ResourceUID::ID> &E : replicated_nodes) {
- // Only server mode adds to replicated_nodes, no need to check it.
- Object *obj = ObjectDB::get_instance(E.key);
- ERR_CONTINUE(!obj);
- Node *node = Object::cast_to<Node>(obj);
- ERR_CONTINUE(!node);
- spawn(E.value, node, p_peer);
- }
-}
-
-void MultiplayerReplicator::poll() {
- for (KeyValue<ResourceUID::ID, SceneConfig> &E : replications) {
- if (!E.value.sync_interval) {
- continue;
- }
- if (E.value.mode == REPLICATION_MODE_SERVER && !multiplayer->is_server()) {
- continue;
- }
- uint64_t time = OS::get_singleton()->get_ticks_usec();
- if (E.value.sync_last + E.value.sync_interval <= time) {
- sync_all(E.key, 0);
- E.value.sync_last = time;
- }
- // Handle wrapping.
- if (E.value.sync_last > time) {
- E.value.sync_last = time;
- }
- }
-}
-
-void MultiplayerReplicator::track(const ResourceUID::ID &p_scene_id, Object *p_obj) {
- ERR_FAIL_COND(!replications.has(p_scene_id));
- const SceneConfig &cfg = replications[p_scene_id];
- ERR_FAIL_COND_MSG(cfg.mode == REPLICATION_MODE_SERVER, "Manual object tracking is not allowed in server mode.");
- _track(p_scene_id, p_obj);
-}
-
-void MultiplayerReplicator::_track(const ResourceUID::ID &p_scene_id, Object *p_obj) {
- ERR_FAIL_COND(!p_obj);
- ERR_FAIL_COND(!replications.has(p_scene_id));
- if (!tracked_objects.has(p_scene_id)) {
- tracked_objects[p_scene_id] = List<ObjectID>();
- }
- tracked_objects[p_scene_id].push_back(p_obj->get_instance_id());
-}
-
-void MultiplayerReplicator::untrack(const ResourceUID::ID &p_scene_id, Object *p_obj) {
- ERR_FAIL_COND(!replications.has(p_scene_id));
- const SceneConfig &cfg = replications[p_scene_id];
- ERR_FAIL_COND_MSG(cfg.mode == REPLICATION_MODE_SERVER, "Manual object tracking is not allowed in server mode.");
- _untrack(p_scene_id, p_obj);
-}
-
-void MultiplayerReplicator::_untrack(const ResourceUID::ID &p_scene_id, Object *p_obj) {
- ERR_FAIL_COND(!p_obj);
- ERR_FAIL_COND(!replications.has(p_scene_id));
- if (tracked_objects.has(p_scene_id)) {
- tracked_objects[p_scene_id].erase(p_obj->get_instance_id());
- }
-}
-
-Error MultiplayerReplicator::sync_all(const ResourceUID::ID &p_scene_id, int p_peer) {
- ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER);
- if (!tracked_objects.has(p_scene_id)) {
- return OK;
- }
- const SceneConfig &cfg = replications[p_scene_id];
- if (cfg.on_sync_send.is_valid()) {
- Array objs;
- if (tracked_objects.has(p_scene_id)) {
- objs.resize(tracked_objects[p_scene_id].size());
- int idx = 0;
- for (const ObjectID &obj_id : tracked_objects[p_scene_id]) {
- objs[idx++] = ObjectDB::get_instance(obj_id);
- }
- }
- Variant args[3] = { p_scene_id, objs, p_peer };
- Variant *argp[3] = { args, &args[1], &args[2] };
- Callable::CallError ce;
- Variant ret;
- cfg.on_sync_send.call((const Variant **)argp, 3, ret, ce);
- ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, FAILED, "Custom sync function failed");
- return OK;
- } else if (cfg.sync_properties.size()) {
- return _sync_all_default(p_scene_id, p_peer);
- }
- return OK;
-}
-
-Error MultiplayerReplicator::send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, Multiplayer::TransferMode p_transfer_mode, int p_channel) {
- ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);
- ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER);
- const SceneConfig &cfg = replications[p_scene_id];
- ERR_FAIL_COND_V_MSG(!cfg.on_sync_send.is_valid(), ERR_UNCONFIGURED, "Sending raw sync messages is only available with custom functions");
- MAKE_ROOM(SYNC_CMD_OFFSET + p_data.size());
- uint8_t *ptr = packet_cache.ptrw();
- ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC;
- encode_uint64(p_scene_id, &ptr[1]);
- if (p_data.size()) {
- memcpy(&ptr[SYNC_CMD_OFFSET], p_data.ptr(), p_data.size());
- }
- Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
- peer->set_target_peer(p_peer_id);
- peer->set_transfer_channel(p_channel);
- peer->set_transfer_mode(p_transfer_mode);
- return peer->put_packet(ptr, SYNC_CMD_OFFSET + p_data.size());
-}
-
-void MultiplayerReplicator::clear() {
- tracked_objects.clear();
- replicated_nodes.clear();
-}
-
-void MultiplayerReplicator::_bind_methods() {
- ClassDB::bind_method(D_METHOD("spawn_config", "scene_id", "spawn_mode", "properties", "custom_send", "custom_receive"), &MultiplayerReplicator::spawn_config, DEFVAL(TypedArray<StringName>()), DEFVAL(Callable()), DEFVAL(Callable()));
- ClassDB::bind_method(D_METHOD("sync_config", "scene_id", "interval", "properties", "custom_send", "custom_receive"), &MultiplayerReplicator::sync_config, DEFVAL(TypedArray<StringName>()), DEFVAL(Callable()), DEFVAL(Callable()));
- ClassDB::bind_method(D_METHOD("despawn", "scene_id", "object", "peer_id"), &MultiplayerReplicator::despawn, DEFVAL(0));
- ClassDB::bind_method(D_METHOD("spawn", "scene_id", "object", "peer_id"), &MultiplayerReplicator::spawn, DEFVAL(0));
- ClassDB::bind_method(D_METHOD("send_despawn", "peer_id", "scene_id", "data", "path"), &MultiplayerReplicator::send_despawn, DEFVAL(Variant()), DEFVAL(NodePath()));
- ClassDB::bind_method(D_METHOD("send_spawn", "peer_id", "scene_id", "data", "path"), &MultiplayerReplicator::send_spawn, DEFVAL(Variant()), DEFVAL(NodePath()));
- ClassDB::bind_method(D_METHOD("send_sync", "peer_id", "scene_id", "data", "transfer_mode", "channel"), &MultiplayerReplicator::send_sync, DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
- ClassDB::bind_method(D_METHOD("sync_all", "scene_id", "peer_id"), &MultiplayerReplicator::sync_all, DEFVAL(0));
- ClassDB::bind_method(D_METHOD("track", "scene_id", "object"), &MultiplayerReplicator::track);
- ClassDB::bind_method(D_METHOD("untrack", "scene_id", "object"), &MultiplayerReplicator::untrack);
- ClassDB::bind_method(D_METHOD("encode_state", "scene_id", "object", "initial"), &MultiplayerReplicator::encode_state, DEFVAL(true));
- ClassDB::bind_method(D_METHOD("decode_state", "scene_id", "object", "data", "initial"), &MultiplayerReplicator::decode_state, DEFVAL(true));
-
- ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
- ADD_SIGNAL(MethodInfo("spawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
- ADD_SIGNAL(MethodInfo("despawn_requested", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "parent", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data")));
- ADD_SIGNAL(MethodInfo("spawn_requested", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "parent", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data")));
- ADD_SIGNAL(MethodInfo("replicated_instance_added", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
- ADD_SIGNAL(MethodInfo("replicated_instance_removed", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
-
- BIND_ENUM_CONSTANT(REPLICATION_MODE_NONE);
- BIND_ENUM_CONSTANT(REPLICATION_MODE_SERVER);
- BIND_ENUM_CONSTANT(REPLICATION_MODE_CUSTOM);
-}
diff --git a/core/multiplayer/multiplayer_replicator.h b/core/multiplayer/multiplayer_replicator.h
deleted file mode 100644
index a9cd6e211e..0000000000
--- a/core/multiplayer/multiplayer_replicator.h
+++ /dev/null
@@ -1,138 +0,0 @@
-/*************************************************************************/
-/* multiplayer_replicator.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef MULTIPLAYER_REPLICATOR_H
-#define MULTIPLAYER_REPLICATOR_H
-
-#include "core/multiplayer/multiplayer_api.h"
-
-#include "core/io/resource_uid.h"
-#include "core/templates/hash_map.h"
-#include "core/variant/typed_array.h"
-
-class MultiplayerReplicator : public Object {
- GDCLASS(MultiplayerReplicator, Object);
-
-public:
- enum {
- SPAWN_CMD_OFFSET = 9,
- SYNC_CMD_OFFSET = 9,
- };
-
- enum ReplicationMode {
- REPLICATION_MODE_NONE,
- REPLICATION_MODE_SERVER,
- REPLICATION_MODE_CUSTOM,
- };
-
- struct SceneConfig {
- ReplicationMode mode;
- uint64_t sync_interval = 0;
- uint64_t sync_last = 0;
- uint8_t sync_recv = 0;
- List<StringName> properties;
- List<StringName> sync_properties;
- Callable on_spawn_despawn_send;
- Callable on_spawn_despawn_receive;
- Callable on_sync_send;
- Callable on_sync_receive;
- };
-
-protected:
- static void _bind_methods();
-
-private:
- enum {
- BYTE_OR_ZERO_SHIFT = MultiplayerAPI::CMD_FLAG_0_SHIFT,
- };
-
- enum {
- BYTE_OR_ZERO_FLAG = 1 << BYTE_OR_ZERO_SHIFT,
- };
-
- MultiplayerAPI *multiplayer = nullptr;
- Vector<uint8_t> packet_cache;
- Map<ResourceUID::ID, SceneConfig> replications;
- Map<ObjectID, ResourceUID::ID> replicated_nodes;
- HashMap<ResourceUID::ID, List<ObjectID>> tracked_objects;
-
- // Encoding
- Error _get_state(const List<StringName> &p_properties, const Object *p_obj, List<Variant> &r_variant);
- Error _encode_state(const List<Variant> &p_variants, uint8_t *p_buffer, int &r_len, bool *r_raw = nullptr);
- Error _decode_state(const List<StringName> &p_cfg, Object *p_obj, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw = false);
-
- // Spawn
- Error _spawn_despawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer, bool p_spawn);
- Error _send_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, bool p_spawn);
- void _process_default_spawn_despawn(int p_from, const ResourceUID::ID &p_scene_id, const uint8_t *p_packet, int p_packet_len, bool p_spawn);
- Error _send_default_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, Object *p_obj, const NodePath &p_path, bool p_spawn);
-
- // Sync
- void _process_default_sync(const ResourceUID::ID &p_id, const uint8_t *p_packet, int p_packet_len);
- Error _sync_all_default(const ResourceUID::ID &p_scene_id, int p_peer);
- void _track(const ResourceUID::ID &p_scene_id, Object *p_object);
- void _untrack(const ResourceUID::ID &p_scene_id, Object *p_object);
-
-public:
- void clear();
-
- // Encoding
- PackedByteArray encode_state(const ResourceUID::ID &p_scene_id, const Object *p_node, bool p_initial);
- Error decode_state(const ResourceUID::ID &p_scene_id, Object *p_node, PackedByteArray p_data, bool p_initial);
-
- // Spawn
- Error spawn_config(const ResourceUID::ID &p_id, ReplicationMode p_mode, const TypedArray<StringName> &p_props = TypedArray<StringName>(), const Callable &p_on_send = Callable(), const Callable &p_on_recv = Callable());
- Error spawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer = 0);
- Error despawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer = 0);
- Error send_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data = Variant(), const NodePath &p_path = NodePath());
- Error send_spawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data = Variant(), const NodePath &p_path = NodePath());
-
- // Sync
- Error sync_config(const ResourceUID::ID &p_id, uint64_t p_interval, const TypedArray<StringName> &p_props = TypedArray<StringName>(), const Callable &p_on_send = Callable(), const Callable &p_on_recv = Callable());
- Error sync_all(const ResourceUID::ID &p_scene_id, int p_peer);
- Error send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, Multiplayer::TransferMode p_mode, int p_channel);
- void track(const ResourceUID::ID &p_scene_id, Object *p_object);
- void untrack(const ResourceUID::ID &p_scene_id, Object *p_object);
-
- // Used by MultiplayerAPI
- void spawn_all(int p_peer);
- void process_spawn_despawn(int p_from, const uint8_t *p_packet, int p_packet_len, bool p_spawn);
- void process_sync(int p_from, const uint8_t *p_packet, int p_packet_len);
- void scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter);
- void poll();
-
- MultiplayerReplicator(MultiplayerAPI *p_multiplayer) {
- multiplayer = p_multiplayer;
- }
-};
-
-VARIANT_ENUM_CAST(MultiplayerReplicator::ReplicationMode);
-
-#endif // MULTIPLAYER_REPLICATOR_H
diff --git a/core/multiplayer/rpc_manager.cpp b/core/multiplayer/rpc_manager.cpp
index 7736637349..1e6d2108be 100644
--- a/core/multiplayer/rpc_manager.cpp
+++ b/core/multiplayer/rpc_manager.cpp
@@ -235,16 +235,12 @@ void RPCManager::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int
ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", authority is " + itos(p_node->get_multiplayer_authority()) + ".");
int argc = 0;
- bool byte_only = false;
const bool byte_only_or_no_args = p_packet[0] & BYTE_ONLY_OR_NO_ARGS_FLAG;
if (byte_only_or_no_args) {
if (p_offset < p_packet_len) {
// This packet contains only bytes.
argc = 1;
- byte_only = true;
- } else {
- // This rpc calls a method without parameters.
}
} else {
// Normal variant, takes the argument count from the packet.
@@ -262,25 +258,10 @@ void RPCManager::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int
_profile_node_data("in_rpc", p_node->get_instance_id());
#endif
- if (byte_only) {
- Vector<uint8_t> pure_data;
- const int len = p_packet_len - p_offset;
- pure_data.resize(len);
- memcpy(pure_data.ptrw(), &p_packet[p_offset], len);
- args.write[0] = pure_data;
- argp.write[0] = &args[0];
- p_offset += len;
- } else {
- for (int i = 0; i < argc; i++) {
- ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
-
- int vlen;
- Error err = multiplayer->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];
- p_offset += vlen;
- }
+ int out;
+ MultiplayerAPI::decode_and_decompress_variants(args, &p_packet[p_offset], p_packet_len - p_offset, out, byte_only_or_no_args, multiplayer->is_object_decoding_allowed());
+ for (int i = 0; i < argc; i++) {
+ argp.write[i] = &args[i];
}
Callable::CallError ce;
@@ -380,28 +361,19 @@ void RPCManager::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Mult
ofs += 2;
}
- if (p_argcount == 0) {
- byte_only_or_no_args = true;
- } else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) {
- byte_only_or_no_args = true;
- // Special optimization when only the byte vector is sent.
- const Vector<uint8_t> data = *p_arg[0];
- MAKE_ROOM(ofs + data.size());
- memcpy(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size());
- ofs += data.size();
+ int len;
+ Error err = MultiplayerAPI::encode_and_compress_variants(p_arg, p_argcount, nullptr, len, &byte_only_or_no_args, multiplayer->is_object_decoding_allowed());
+ ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC arguments. THIS IS LIKELY A BUG IN THE ENGINE!");
+ if (byte_only_or_no_args) {
+ MAKE_ROOM(ofs + len);
} else {
- // Arguments
- MAKE_ROOM(ofs + 1);
+ MAKE_ROOM(ofs + 1 + len);
packet_cache.write[ofs] = p_argcount;
ofs += 1;
- for (int i = 0; i < p_argcount; i++) {
- int len(0);
- Error err = multiplayer->encode_and_compress_variant(*p_arg[i], nullptr, len);
- ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!");
- MAKE_ROOM(ofs + len);
- multiplayer->encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len);
- ofs += len;
- }
+ }
+ if (len) {
+ MultiplayerAPI::encode_and_compress_variants(p_arg, p_argcount, &packet_cache.write[ofs], len, &byte_only_or_no_args, multiplayer->is_object_decoding_allowed());
+ ofs += len;
}
ERR_FAIL_COND(command_type > 7);
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index a18ec4d6ad..388368d181 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -69,7 +69,6 @@
#include "core/math/triangle_mesh.h"
#include "core/multiplayer/multiplayer_api.h"
#include "core/multiplayer/multiplayer_peer.h"
-#include "core/multiplayer/multiplayer_replicator.h"
#include "core/object/class_db.h"
#include "core/object/undo_redo.h"
#include "core/os/main_loop.h"
@@ -200,7 +199,6 @@ void register_core_types() {
GDREGISTER_VIRTUAL_CLASS(MultiplayerPeer);
GDREGISTER_CLASS(MultiplayerPeerExtension);
- GDREGISTER_VIRTUAL_CLASS(MultiplayerReplicator);
GDREGISTER_CLASS(MultiplayerAPI);
GDREGISTER_CLASS(MainLoop);
GDREGISTER_CLASS(Translation);
diff --git a/core/string/char_utils.h b/core/string/char_utils.h
new file mode 100644
index 0000000000..0afd058f01
--- /dev/null
+++ b/core/string/char_utils.h
@@ -0,0 +1,92 @@
+/*************************************************************************/
+/* char_utils.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 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 CHAR_UTILS_H
+#define CHAR_UTILS_H
+
+#include "core/typedefs.h"
+
+static _FORCE_INLINE_ bool is_ascii_upper_case(char32_t c) {
+ return (c >= 'A' && c <= 'Z');
+}
+
+static _FORCE_INLINE_ bool is_ascii_lower_case(char32_t c) {
+ return (c >= 'a' && c <= 'z');
+}
+
+static _FORCE_INLINE_ bool is_digit(char32_t c) {
+ return (c >= '0' && c <= '9');
+}
+
+static _FORCE_INLINE_ bool is_hex_digit(char32_t c) {
+ return (is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
+}
+
+static _FORCE_INLINE_ bool is_binary_digit(char32_t c) {
+ return (c == '0' || c == '1');
+}
+
+static _FORCE_INLINE_ bool is_ascii_char(char32_t c) {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+}
+
+static _FORCE_INLINE_ bool is_ascii_alphanumeric_char(char32_t c) {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
+}
+
+static _FORCE_INLINE_ bool is_ascii_identifier_char(char32_t c) {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
+}
+
+static _FORCE_INLINE_ bool is_symbol(char32_t c) {
+ return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' ');
+}
+
+static _FORCE_INLINE_ bool is_control(char32_t p_char) {
+ return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009f);
+}
+
+static _FORCE_INLINE_ bool is_whitespace(char32_t p_char) {
+ return (p_char == ' ') || (p_char == 0x00a0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085);
+}
+
+static _FORCE_INLINE_ bool is_linebreak(char32_t p_char) {
+ return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029);
+}
+
+static _FORCE_INLINE_ bool is_punct(char32_t p_char) {
+ return (p_char >= ' ' && p_char <= '/') || (p_char >= ':' && p_char <= '@') || (p_char >= '[' && p_char <= '^') || (p_char == '`') || (p_char >= '{' && p_char <= '~') || (p_char >= 0x2000 && p_char <= 0x206f) || (p_char >= 0x3000 && p_char <= 0x303f);
+}
+
+static _FORCE_INLINE_ bool is_underscore(char32_t p_char) {
+ return (p_char == '_');
+}
+
+#endif // CHAR_UTILS_H
diff --git a/core/string/translation.cpp b/core/string/translation.cpp
index 355ee238e8..7cc41df9ef 100644
--- a/core/string/translation.cpp
+++ b/core/string/translation.cpp
@@ -213,14 +213,6 @@ static _character_accent_pair _character_to_accented[] = {
{ 'z', U"ΕΊ" },
};
-static _FORCE_INLINE_ bool is_upper_case(char32_t c) {
- return (c >= 'A' && c <= 'Z');
-}
-
-static _FORCE_INLINE_ bool is_lower_case(char32_t c) {
- return (c >= 'a' && c <= 'z');
-}
-
Vector<TranslationServer::LocaleScriptInfo> TranslationServer::locale_script_info;
Map<String, String> TranslationServer::language_map;
@@ -309,15 +301,15 @@ String TranslationServer::standardize_locale(const String &p_locale) const {
Vector<String> locale_elements = univ_locale.get_slice("@", 0).split("_");
lang = locale_elements[0];
if (locale_elements.size() >= 2) {
- if (locale_elements[1].length() == 4 && is_upper_case(locale_elements[1][0]) && is_lower_case(locale_elements[1][1]) && is_lower_case(locale_elements[1][2]) && is_lower_case(locale_elements[1][3])) {
+ if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) {
script = locale_elements[1];
}
- if (locale_elements[1].length() == 2 && is_upper_case(locale_elements[1][0]) && is_upper_case(locale_elements[1][1])) {
+ if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) {
country = locale_elements[1];
}
}
if (locale_elements.size() >= 3) {
- if (locale_elements[2].length() == 2 && is_upper_case(locale_elements[2][0]) && is_upper_case(locale_elements[2][1])) {
+ if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) {
country = locale_elements[2];
} else if (variant_map.has(locale_elements[2].to_lower()) && variant_map[locale_elements[2].to_lower()] == lang) {
variant = locale_elements[2].to_lower();
@@ -434,15 +426,15 @@ String TranslationServer::get_locale_name(const String &p_locale) const {
Vector<String> locale_elements = locale.split("_");
lang = locale_elements[0];
if (locale_elements.size() >= 2) {
- if (locale_elements[1].length() == 4 && is_upper_case(locale_elements[1][0]) && is_lower_case(locale_elements[1][1]) && is_lower_case(locale_elements[1][2]) && is_lower_case(locale_elements[1][3])) {
+ if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) {
script = locale_elements[1];
}
- if (locale_elements[1].length() == 2 && is_upper_case(locale_elements[1][0]) && is_upper_case(locale_elements[1][1])) {
+ if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) {
country = locale_elements[1];
}
}
if (locale_elements.size() >= 3) {
- if (locale_elements[2].length() == 2 && is_upper_case(locale_elements[2][0]) && is_upper_case(locale_elements[2][1])) {
+ if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) {
country = locale_elements[2];
}
}
@@ -911,7 +903,7 @@ String TranslationServer::add_padding(String &p_message, int p_length) const {
}
const char32_t *TranslationServer::get_accented_version(char32_t p_character) const {
- if (!((p_character >= 'a' && p_character <= 'z') || (p_character >= 'A' && p_character <= 'Z'))) {
+ if (!is_ascii_char(p_character)) {
return nullptr;
}
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 6e0a7c7022..c4edc8c086 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -54,34 +54,14 @@
static const int MAX_DECIMALS = 32;
-static _FORCE_INLINE_ bool is_digit(char32_t c) {
- return (c >= '0' && c <= '9');
-}
-
-static _FORCE_INLINE_ bool is_hex_digit(char32_t c) {
- return (is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
-}
-
-static _FORCE_INLINE_ bool is_upper_case(char32_t c) {
- return (c >= 'A' && c <= 'Z');
-}
-
-static _FORCE_INLINE_ bool is_lower_case(char32_t c) {
- return (c >= 'a' && c <= 'z');
-}
-
static _FORCE_INLINE_ char32_t lower_case(char32_t c) {
- return (is_upper_case(c) ? (c + ('a' - 'A')) : c);
+ return (is_ascii_upper_case(c) ? (c + ('a' - 'A')) : c);
}
const char CharString::_null = 0;
const char16_t Char16String::_null = 0;
const char32_t String::_null = 0;
-bool is_symbol(char32_t c) {
- return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' ');
-}
-
bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end) {
const String &s = p_s;
int beg = CLAMP(p_col, 0, s.length());
@@ -974,21 +954,21 @@ String String::camelcase_to_underscore(bool lowercase) const {
int start_index = 0;
for (int i = 1; i < this->size(); i++) {
- bool is_upper = is_upper_case(cstr[i]);
+ bool is_upper = is_ascii_upper_case(cstr[i]);
bool is_number = is_digit(cstr[i]);
bool are_next_2_lower = false;
bool is_next_lower = false;
bool is_next_number = false;
- bool was_precedent_upper = is_upper_case(cstr[i - 1]);
+ bool was_precedent_upper = is_ascii_upper_case(cstr[i - 1]);
bool was_precedent_number = is_digit(cstr[i - 1]);
if (i + 2 < this->size()) {
- are_next_2_lower = is_lower_case(cstr[i + 1]) && is_lower_case(cstr[i + 2]);
+ are_next_2_lower = is_ascii_lower_case(cstr[i + 1]) && is_ascii_lower_case(cstr[i + 2]);
}
if (i + 1 < this->size()) {
- is_next_lower = is_lower_case(cstr[i + 1]);
+ is_next_lower = is_ascii_lower_case(cstr[i + 1]);
is_next_number = is_digit(cstr[i + 1]);
}
@@ -2212,7 +2192,7 @@ bool String::is_numeric() const {
return false;
}
dot = true;
- } else if (c < '0' || c > '9') {
+ } else if (!is_digit(c)) {
return false;
}
}
@@ -3691,7 +3671,7 @@ bool String::is_valid_identifier() const {
}
}
- bool valid_char = is_digit(str[i]) || is_lower_case(str[i]) || is_upper_case(str[i]) || str[i] == '_';
+ bool valid_char = is_ascii_identifier_char(str[i]);
if (!valid_char) {
return false;
@@ -3716,7 +3696,7 @@ String String::uri_encode() const {
String res;
for (int i = 0; i < temp.length(); ++i) {
char ord = temp[i];
- if (ord == '.' || ord == '-' || ord == '_' || ord == '~' || is_lower_case(ord) || is_upper_case(ord) || is_digit(ord)) {
+ if (ord == '.' || ord == '-' || ord == '~' || is_ascii_identifier_char(ord)) {
res += ord;
} else {
char h_Val[3];
@@ -3738,9 +3718,9 @@ String String::uri_decode() const {
for (int i = 0; i < src.length(); ++i) {
if (src[i] == '%' && i + 2 < src.length()) {
char ord1 = src[i + 1];
- if (is_digit(ord1) || is_upper_case(ord1)) {
+ if (is_digit(ord1) || is_ascii_upper_case(ord1)) {
char ord2 = src[i + 2];
- if (is_digit(ord2) || is_upper_case(ord2)) {
+ if (is_digit(ord2) || is_ascii_upper_case(ord2)) {
char bytes[3] = { (char)ord1, (char)ord2, 0 };
res += (char)strtol(bytes, nullptr, 16);
i += 2;
@@ -3867,7 +3847,7 @@ static _FORCE_INLINE_ int _xml_unescape(const char32_t *p_src, int p_src_len, ch
for (int i = 2; i < p_src_len; i++) {
eat = i + 1;
char32_t ct = p_src[i];
- if (ct == ';' || ct < '0' || ct > '9') {
+ if (ct == ';' || !is_digit(ct)) {
break;
}
}
@@ -3997,7 +3977,7 @@ String String::pad_zeros(int p_digits) const {
int begin = 0;
- while (begin < end && (s[begin] < '0' || s[begin] > '9')) {
+ while (begin < end && !is_digit(s[begin])) {
begin++;
}
@@ -4042,7 +4022,7 @@ bool String::is_valid_int() const {
}
for (int i = from; i < len; i++) {
- if (operator[](i) < '0' || operator[](i) > '9') {
+ if (!is_digit(operator[](i))) {
return false; // no start with number plz
}
}
diff --git a/core/string/ustring.h b/core/string/ustring.h
index b685e3929f..1d302b65a7 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -32,6 +32,7 @@
#define USTRING_GODOT_H
// Note: Renamed to avoid conflict with ICU header with the same name.
+#include "core/string/char_utils.h"
#include "core/templates/cowdata.h"
#include "core/templates/vector.h"
#include "core/typedefs.h"
@@ -533,7 +534,6 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St
String RTR(const String &p_text, const String &p_context = "");
String RTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = "");
-bool is_symbol(char32_t c);
bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end);
_FORCE_INLINE_ void sarray_add_str(Vector<String> &arr) {
diff --git a/core/variant/method_ptrcall.h b/core/variant/method_ptrcall.h
index 75a93ac4c8..d0acf60c22 100644
--- a/core/variant/method_ptrcall.h
+++ b/core/variant/method_ptrcall.h
@@ -31,7 +31,6 @@
#ifndef METHOD_PTRCALL_H
#define METHOD_PTRCALL_H
-#include "core/math/transform_2d.h"
#include "core/object/object_id.h"
#include "core/typedefs.h"
#include "core/variant/variant.h"
diff --git a/core/variant/variant.h b/core/variant/variant.h
index 36fa755647..b75882a87c 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -39,8 +39,12 @@
#include "core/math/face3.h"
#include "core/math/plane.h"
#include "core/math/quaternion.h"
+#include "core/math/rect2.h"
+#include "core/math/rect2i.h"
#include "core/math/transform_2d.h"
#include "core/math/transform_3d.h"
+#include "core/math/vector2.h"
+#include "core/math/vector2i.h"
#include "core/math/vector3.h"
#include "core/math/vector3i.h"
#include "core/object/object_id.h"
diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp
index 55fc9212b7..e889a1bb40 100644
--- a/core/variant/variant_parser.cpp
+++ b/core/variant/variant_parser.cpp
@@ -188,7 +188,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
if (p_stream->is_eof()) {
r_token.type = TK_EOF;
return OK;
- } else if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) {
+ } else if (is_hex_digit(ch)) {
color_str += ch;
} else {
@@ -265,13 +265,13 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
- if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
+ if (!is_hex_digit(c)) {
r_err_str = "Malformed hex constant in string";
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
char32_t v;
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
v = c - '0';
} else if (c >= 'a' && c <= 'f') {
v = c - 'a';
@@ -381,7 +381,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
while (true) {
switch (reading) {
case READING_INT: {
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
//pass
} else if (c == '.') {
reading = READING_DEC;
@@ -395,7 +395,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
} break;
case READING_DEC: {
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
} else if (c == 'e') {
reading = READING_EXP;
} else {
@@ -404,7 +404,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
} break;
case READING_EXP: {
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
exp_beg = true;
} else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) {
@@ -433,11 +433,11 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
r_token.value = num.as_int();
}
return OK;
- } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') {
+ } else if (is_ascii_char(cchar) || is_underscore(cchar)) {
StringBuffer<> id;
bool first = true;
- while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && cchar >= '0' && cchar <= '9')) {
+ while (is_ascii_char(cchar) || is_underscore(cchar) || (!first && is_digit(cchar))) {
id += cchar;
cchar = p_stream->get_char();
first = false;