summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/core_bind.cpp14
-rw-r--r--core/crypto/crypto.cpp39
-rw-r--r--core/crypto/crypto.h34
-rw-r--r--core/doc_data.cpp12
-rw-r--r--core/doc_data.h2
-rw-r--r--core/extension/gdextension_interface.cpp15
-rw-r--r--core/extension/gdextension_interface.h2
-rw-r--r--core/io/dtls_server.cpp2
-rw-r--r--core/io/dtls_server.h2
-rw-r--r--core/io/http_client.cpp2
-rw-r--r--core/io/http_client.h3
-rw-r--r--core/io/http_client_tcp.cpp33
-rw-r--r--core/io/http_client_tcp.h7
-rw-r--r--core/io/image.cpp46
-rw-r--r--core/io/image.h18
-rw-r--r--core/io/packet_peer_dtls.cpp2
-rw-r--r--core/io/packet_peer_dtls.h2
-rw-r--r--core/io/resource.cpp36
-rw-r--r--core/io/stream_peer_tls.cpp20
-rw-r--r--core/io/stream_peer_tls.h11
-rw-r--r--core/math/transform_2d.cpp39
-rw-r--r--core/os/os.cpp10
-rw-r--r--core/os/os.h8
-rw-r--r--core/register_core_types.cpp1
-rw-r--r--core/variant/array.cpp137
-rw-r--r--core/variant/array.h9
-rw-r--r--core/variant/container_type_validate.h11
-rw-r--r--core/variant/typed_array.h42
-rw-r--r--core/variant/variant_call.cpp12
-rw-r--r--core/variant/variant_internal.h19
-rw-r--r--core/variant/variant_parser.cpp148
31 files changed, 488 insertions, 250 deletions
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 9de901754a..c752bdd057 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -717,7 +717,7 @@ TypedArray<PackedVector2Array> Geometry2D::decompose_polygon_in_convex(const Vec
TypedArray<PackedVector2Array> Geometry2D::merge_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
Vector<Vector<Point2>> polys = ::Geometry2D::merge_polygons(p_polygon_a, p_polygon_b);
- Array ret;
+ TypedArray<PackedVector2Array> ret;
for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
@@ -739,7 +739,7 @@ TypedArray<PackedVector2Array> Geometry2D::clip_polygons(const Vector<Vector2> &
TypedArray<PackedVector2Array> Geometry2D::intersect_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
Vector<Vector<Point2>> polys = ::Geometry2D::intersect_polygons(p_polygon_a, p_polygon_b);
- Array ret;
+ TypedArray<PackedVector2Array> ret;
for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
@@ -750,7 +750,7 @@ TypedArray<PackedVector2Array> Geometry2D::intersect_polygons(const Vector<Vecto
TypedArray<PackedVector2Array> Geometry2D::exclude_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
Vector<Vector<Point2>> polys = ::Geometry2D::exclude_polygons(p_polygon_a, p_polygon_b);
- Array ret;
+ TypedArray<PackedVector2Array> ret;
for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
@@ -761,7 +761,7 @@ TypedArray<PackedVector2Array> Geometry2D::exclude_polygons(const Vector<Vector2
TypedArray<PackedVector2Array> Geometry2D::clip_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) {
Vector<Vector<Point2>> polys = ::Geometry2D::clip_polyline_with_polygon(p_polyline, p_polygon);
- Array ret;
+ TypedArray<PackedVector2Array> ret;
for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
@@ -772,7 +772,7 @@ TypedArray<PackedVector2Array> Geometry2D::clip_polyline_with_polygon(const Vect
TypedArray<PackedVector2Array> Geometry2D::intersect_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) {
Vector<Vector<Point2>> polys = ::Geometry2D::intersect_polyline_with_polygon(p_polyline, p_polygon);
- Array ret;
+ TypedArray<PackedVector2Array> ret;
for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
@@ -783,7 +783,7 @@ TypedArray<PackedVector2Array> Geometry2D::intersect_polyline_with_polygon(const
TypedArray<PackedVector2Array> Geometry2D::offset_polygon(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) {
Vector<Vector<Point2>> polys = ::Geometry2D::offset_polygon(p_polygon, p_delta, ::Geometry2D::PolyJoinType(p_join_type));
- Array ret;
+ TypedArray<PackedVector2Array> ret;
for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
@@ -794,7 +794,7 @@ TypedArray<PackedVector2Array> Geometry2D::offset_polygon(const Vector<Vector2>
TypedArray<PackedVector2Array> Geometry2D::offset_polyline(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) {
Vector<Vector<Point2>> polys = ::Geometry2D::offset_polyline(p_polygon, p_delta, ::Geometry2D::PolyJoinType(p_join_type), ::Geometry2D::PolyEndType(p_end_type));
- Array ret;
+ TypedArray<PackedVector2Array> ret;
for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
diff --git a/core/crypto/crypto.cpp b/core/crypto/crypto.cpp
index cbb18a277f..939c1c298f 100644
--- a/core/crypto/crypto.cpp
+++ b/core/crypto/crypto.cpp
@@ -65,6 +65,45 @@ void X509Certificate::_bind_methods() {
ClassDB::bind_method(D_METHOD("load", "path"), &X509Certificate::load);
}
+/// TLSOptions
+
+Ref<TLSOptions> TLSOptions::client(Ref<X509Certificate> p_trusted_chain, const String &p_common_name_override) {
+ Ref<TLSOptions> opts;
+ opts.instantiate();
+ opts->trusted_ca_chain = p_trusted_chain;
+ opts->common_name = p_common_name_override;
+ opts->verify_mode = TLS_VERIFY_FULL;
+ return opts;
+}
+
+Ref<TLSOptions> TLSOptions::client_unsafe(Ref<X509Certificate> p_trusted_chain) {
+ Ref<TLSOptions> opts;
+ opts.instantiate();
+ opts->trusted_ca_chain = p_trusted_chain;
+ if (p_trusted_chain.is_null()) {
+ opts->verify_mode = TLS_VERIFY_NONE;
+ } else {
+ opts->verify_mode = TLS_VERIFY_CERT;
+ }
+ return opts;
+}
+
+Ref<TLSOptions> TLSOptions::server(Ref<CryptoKey> p_own_key, Ref<X509Certificate> p_own_certificate) {
+ Ref<TLSOptions> opts;
+ opts.instantiate();
+ opts->server_mode = true;
+ opts->own_certificate = p_own_certificate;
+ opts->private_key = p_own_key;
+ opts->verify_mode = TLS_VERIFY_NONE;
+ return opts;
+}
+
+void TLSOptions::_bind_methods() {
+ ClassDB::bind_static_method("TLSOptions", D_METHOD("client", "trusted_chain", "common_name_override"), &TLSOptions::client, DEFVAL(Ref<X509Certificate>()), DEFVAL(String()));
+ ClassDB::bind_static_method("TLSOptions", D_METHOD("client_unsafe", "trusted_chain"), &TLSOptions::client_unsafe, DEFVAL(Ref<X509Certificate>()));
+ ClassDB::bind_static_method("TLSOptions", D_METHOD("server", "key", "certificate"), &TLSOptions::server);
+}
+
/// HMACContext
void HMACContext::_bind_methods() {
diff --git a/core/crypto/crypto.h b/core/crypto/crypto.h
index 981f67883c..999fe076d6 100644
--- a/core/crypto/crypto.h
+++ b/core/crypto/crypto.h
@@ -67,6 +67,40 @@ public:
virtual Error save(String p_path) = 0;
};
+class TLSOptions : public RefCounted {
+ GDCLASS(TLSOptions, RefCounted);
+
+public:
+ enum TLSVerifyMode {
+ TLS_VERIFY_NONE = 0,
+ TLS_VERIFY_CERT = 1,
+ TLS_VERIFY_FULL = 2,
+ };
+
+private:
+ bool server_mode = false;
+ String common_name;
+ TLSVerifyMode verify_mode = TLS_VERIFY_FULL;
+ Ref<X509Certificate> trusted_ca_chain;
+ Ref<X509Certificate> own_certificate;
+ Ref<CryptoKey> private_key;
+
+protected:
+ static void _bind_methods();
+
+public:
+ static Ref<TLSOptions> client(Ref<X509Certificate> p_trusted_chain = Ref<X509Certificate>(), const String &p_common_name_override = String());
+ static Ref<TLSOptions> client_unsafe(Ref<X509Certificate> p_trusted_chain);
+ static Ref<TLSOptions> server(Ref<CryptoKey> p_own_key, Ref<X509Certificate> p_own_certificate);
+
+ TLSVerifyMode get_verify_mode() const { return verify_mode; }
+ String get_common_name() const { return common_name; }
+ Ref<X509Certificate> get_trusted_ca_chain() const { return trusted_ca_chain; }
+ Ref<X509Certificate> get_own_certificate() const { return own_certificate; }
+ Ref<CryptoKey> get_private_key() const { return private_key; }
+ bool is_server() const { return server_mode; }
+};
+
class HMACContext : public RefCounted {
GDCLASS(HMACContext, RefCounted);
diff --git a/core/doc_data.cpp b/core/doc_data.cpp
index f90dbe1423..5e09e560d5 100644
--- a/core/doc_data.cpp
+++ b/core/doc_data.cpp
@@ -30,6 +30,14 @@
#include "doc_data.h"
+String DocData::get_default_value_string(const Variant &p_value) {
+ if (p_value.get_type() == Variant::ARRAY) {
+ return Variant(Array(p_value, 0, StringName(), Variant())).get_construct_string().replace("\n", " ");
+ } else {
+ return p_value.get_construct_string().replace("\n", " ");
+ }
+}
+
void DocData::return_doc_from_retinfo(DocData::MethodDoc &p_method, const PropertyInfo &p_retinfo) {
if (p_retinfo.type == Variant::INT && p_retinfo.hint == PROPERTY_HINT_INT_IS_POINTER) {
p_method.return_type = p_retinfo.hint_string;
@@ -105,7 +113,7 @@ void DocData::property_doc_from_scriptmemberinfo(DocData::PropertyDoc &p_propert
p_property.getter = p_memberinfo.getter;
if (p_memberinfo.has_default_value && p_memberinfo.default_value.get_type() != Variant::OBJECT) {
- p_property.default_value = p_memberinfo.default_value.get_construct_string().replace("\n", "");
+ p_property.default_value = get_default_value_string(p_memberinfo.default_value);
}
p_property.overridden = false;
@@ -148,7 +156,7 @@ void DocData::method_doc_from_methodinfo(DocData::MethodDoc &p_method, const Met
int default_arg_index = i - (p_methodinfo.arguments.size() - p_methodinfo.default_arguments.size());
if (default_arg_index >= 0) {
Variant default_arg = p_methodinfo.default_arguments[default_arg_index];
- argument.default_value = default_arg.get_construct_string().replace("\n", "");
+ argument.default_value = get_default_value_string(default_arg);
}
p_method.arguments.push_back(argument);
}
diff --git a/core/doc_data.h b/core/doc_data.h
index 1cf4e4f206..c503a4e0d6 100644
--- a/core/doc_data.h
+++ b/core/doc_data.h
@@ -516,6 +516,8 @@ public:
}
};
+ static String get_default_value_string(const Variant &p_value);
+
static void return_doc_from_retinfo(DocData::MethodDoc &p_method, const PropertyInfo &p_retinfo);
static void argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const PropertyInfo &p_arginfo);
static void property_doc_from_scriptmemberinfo(DocData::PropertyDoc &p_property, const ScriptMemberInfo &p_memberinfo);
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index a9063c8b27..3bea013fab 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -856,6 +856,19 @@ static GDExtensionVariantPtr gdextension_array_operator_index_const(GDExtensionC
return (GDExtensionVariantPtr)&self->operator[](p_index);
}
+void gdextension_array_ref(GDExtensionTypePtr p_self, GDExtensionConstTypePtr p_from) {
+ Array *self = (Array *)p_self;
+ const Array *from = (const Array *)p_from;
+ self->_ref(*from);
+}
+
+void gdextension_array_set_typed(GDExtensionTypePtr p_self, uint32_t p_type, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstVariantPtr p_script) {
+ Array *self = reinterpret_cast<Array *>(p_self);
+ const StringName *class_name = reinterpret_cast<const StringName *>(p_class_name);
+ const Variant *script = reinterpret_cast<const Variant *>(p_script);
+ self->set_typed(p_type, *class_name, *script);
+}
+
/* Dictionary functions */
static GDExtensionVariantPtr gdextension_dictionary_operator_index(GDExtensionTypePtr p_self, GDExtensionConstVariantPtr p_key) {
@@ -1129,6 +1142,8 @@ void gdextension_setup_interface(GDExtensionInterface *p_interface) {
gde_interface.array_operator_index = gdextension_array_operator_index;
gde_interface.array_operator_index_const = gdextension_array_operator_index_const;
+ gde_interface.array_ref = gdextension_array_ref;
+ gde_interface.array_set_typed = gdextension_array_set_typed;
/* Dictionary functions */
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index 6e5dee8265..876a09beff 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -551,6 +551,8 @@ typedef struct {
GDExtensionVariantPtr (*array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be an Array ptr
GDExtensionVariantPtr (*array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be an Array ptr
+ void (*array_ref)(GDExtensionTypePtr p_self, GDExtensionConstTypePtr p_from); // p_self should be an Array ptr
+ void (*array_set_typed)(GDExtensionTypePtr p_self, uint32_t p_type, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstVariantPtr p_script); // p_self should be an Array ptr
/* Dictionary functions */
diff --git a/core/io/dtls_server.cpp b/core/io/dtls_server.cpp
index c542e394a1..07d62d3a8d 100644
--- a/core/io/dtls_server.cpp
+++ b/core/io/dtls_server.cpp
@@ -48,6 +48,6 @@ bool DTLSServer::is_available() {
}
void DTLSServer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("setup", "key", "certificate", "chain"), &DTLSServer::setup, DEFVAL(Ref<X509Certificate>()));
+ ClassDB::bind_method(D_METHOD("setup", "server_options"), &DTLSServer::setup);
ClassDB::bind_method(D_METHOD("take_connection", "udp_peer"), &DTLSServer::take_connection);
}
diff --git a/core/io/dtls_server.h b/core/io/dtls_server.h
index e749e6968b..f3fbde3c15 100644
--- a/core/io/dtls_server.h
+++ b/core/io/dtls_server.h
@@ -47,7 +47,7 @@ public:
static bool is_available();
static DTLSServer *create();
- virtual Error setup(Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain = Ref<X509Certificate>()) = 0;
+ virtual Error setup(Ref<TLSOptions> p_options) = 0;
virtual void stop() = 0;
virtual Ref<PacketPeerDTLS> take_connection(Ref<PacketPeerUDP> p_peer) = 0;
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 829abdc614..190edbfb82 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -138,7 +138,7 @@ PackedStringArray HTTPClient::_get_response_headers() {
}
void HTTPClient::_bind_methods() {
- ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port", "use_tls", "verify_host"), &HTTPClient::connect_to_host, DEFVAL(-1), DEFVAL(false), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port", "tls_options"), &HTTPClient::connect_to_host, DEFVAL(-1), DEFVAL(Ref<TLSOptions>()));
ClassDB::bind_method(D_METHOD("set_connection", "connection"), &HTTPClient::set_connection);
ClassDB::bind_method(D_METHOD("get_connection"), &HTTPClient::get_connection);
ClassDB::bind_method(D_METHOD("request_raw", "method", "url", "headers", "body"), &HTTPClient::_request_raw);
diff --git a/core/io/http_client.h b/core/io/http_client.h
index 853ea7f472..9e018182e3 100644
--- a/core/io/http_client.h
+++ b/core/io/http_client.h
@@ -31,6 +31,7 @@
#ifndef HTTP_CLIENT_H
#define HTTP_CLIENT_H
+#include "core/crypto/crypto.h"
#include "core/io/ip.h"
#include "core/io/stream_peer.h"
#include "core/io/stream_peer_tcp.h"
@@ -168,7 +169,7 @@ public:
Error verify_headers(const Vector<String> &p_headers);
virtual Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) = 0;
- virtual Error connect_to_host(const String &p_host, int p_port = -1, bool p_tls = false, bool p_verify_host = true) = 0;
+ virtual Error connect_to_host(const String &p_host, int p_port = -1, Ref<TLSOptions> p_tls_options = Ref<TLSOptions>()) = 0;
virtual void set_connection(const Ref<StreamPeer> &p_connection) = 0;
virtual Ref<StreamPeer> get_connection() const = 0;
diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp
index 5cdb13fa06..3788fa501e 100644
--- a/core/io/http_client_tcp.cpp
+++ b/core/io/http_client_tcp.cpp
@@ -39,29 +39,31 @@ HTTPClient *HTTPClientTCP::_create_func() {
return memnew(HTTPClientTCP);
}
-Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_tls, bool p_verify_host) {
+Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, Ref<TLSOptions> p_options) {
close();
conn_port = p_port;
conn_host = p_host;
+ tls_options = p_options;
ip_candidates.clear();
- tls = p_tls;
- tls_verify_host = p_verify_host;
-
String host_lower = conn_host.to_lower();
if (host_lower.begins_with("http://")) {
conn_host = conn_host.substr(7, conn_host.length() - 7);
+ tls_options.unref();
} else if (host_lower.begins_with("https://")) {
- tls = true;
+ if (tls_options.is_null()) {
+ tls_options = TLSOptions::client();
+ }
conn_host = conn_host.substr(8, conn_host.length() - 8);
}
+ ERR_FAIL_COND_V(tls_options.is_valid() && tls_options->is_server(), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(conn_host.length() < HOST_MIN_LEN, ERR_INVALID_PARAMETER);
if (conn_port < 0) {
- if (tls) {
+ if (tls_options.is_valid()) {
conn_port = PORT_HTTPS;
} else {
conn_port = PORT_HTTP;
@@ -70,11 +72,11 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_tl
connection = tcp_connection;
- if (tls && https_proxy_port != -1) {
+ if (tls_options.is_valid() && https_proxy_port != -1) {
proxy_client.instantiate(); // Needs proxy negotiation.
server_host = https_proxy_host;
server_port = https_proxy_port;
- } else if (!tls && http_proxy_port != -1) {
+ } else if (tls_options.is_null() && http_proxy_port != -1) {
server_host = http_proxy_host;
server_port = http_proxy_port;
} else {
@@ -107,7 +109,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_tl
void HTTPClientTCP::set_connection(const Ref<StreamPeer> &p_connection) {
ERR_FAIL_COND_MSG(p_connection.is_null(), "Connection is not a reference to a valid StreamPeer object.");
- if (tls) {
+ if (tls_options.is_valid()) {
ERR_FAIL_NULL_MSG(Object::cast_to<StreamPeerTLS>(p_connection.ptr()),
"Connection is not a reference to a valid StreamPeerTLS object.");
}
@@ -156,7 +158,7 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector<
}
String uri = p_url;
- if (!tls && http_proxy_port != -1) {
+ if (tls_options.is_null() && http_proxy_port != -1) {
uri = vformat("http://%s:%d%s", conn_host, conn_port, p_url);
}
@@ -181,7 +183,7 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector<
}
}
if (add_host) {
- if ((tls && conn_port == PORT_HTTPS) || (!tls && conn_port == PORT_HTTP)) {
+ if ((tls_options.is_valid() && conn_port == PORT_HTTPS) || (tls_options.is_null() && conn_port == PORT_HTTP)) {
// Don't append the standard ports.
request += "Host: " + conn_host + "\r\n";
} else {
@@ -316,7 +318,7 @@ Error HTTPClientTCP::poll() {
return OK;
} break;
case StreamPeerTCP::STATUS_CONNECTED: {
- if (tls && proxy_client.is_valid()) {
+ if (tls_options.is_valid() && proxy_client.is_valid()) {
Error err = proxy_client->poll();
if (err == ERR_UNCONFIGURED) {
proxy_client->set_connection(tcp_connection);
@@ -357,13 +359,12 @@ Error HTTPClientTCP::poll() {
return ERR_CANT_CONNECT;
} break;
}
- } else if (tls) {
+ } else if (tls_options.is_valid()) {
Ref<StreamPeerTLS> tls_conn;
if (!handshaking) {
// Connect the StreamPeerTLS and start handshaking.
tls_conn = Ref<StreamPeerTLS>(StreamPeerTLS::create());
- tls_conn->set_blocking_handshake_enabled(false);
- Error err = tls_conn->connect_to_stream(tcp_connection, tls_verify_host, conn_host);
+ Error err = tls_conn->connect_to_stream(tcp_connection, conn_host, tls_options);
if (err != OK) {
close();
status = STATUS_TLS_HANDSHAKE_ERROR;
@@ -421,7 +422,7 @@ Error HTTPClientTCP::poll() {
case STATUS_BODY:
case STATUS_CONNECTED: {
// Check if we are still connected.
- if (tls) {
+ if (tls_options.is_valid()) {
Ref<StreamPeerTLS> tmp = connection;
tmp->poll();
if (tmp->get_status() != StreamPeerTLS::STATUS_CONNECTED) {
diff --git a/core/io/http_client_tcp.h b/core/io/http_client_tcp.h
index 97ac2d76a7..6060c975bc 100644
--- a/core/io/http_client_tcp.h
+++ b/core/io/http_client_tcp.h
@@ -33,6 +33,8 @@
#include "http_client.h"
+#include "core/crypto/crypto.h"
+
class HTTPClientTCP : public HTTPClient {
private:
Status status = STATUS_DISCONNECTED;
@@ -46,11 +48,10 @@ private:
String http_proxy_host;
int https_proxy_port = -1; // Proxy server for https requests.
String https_proxy_host;
- bool tls = false;
- bool tls_verify_host = false;
bool blocking = false;
bool handshaking = false;
bool head_request = false;
+ Ref<TLSOptions> tls_options;
Vector<uint8_t> response_str;
@@ -79,7 +80,7 @@ public:
Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) override;
- Error connect_to_host(const String &p_host, int p_port = -1, bool p_tls = false, bool p_verify_host = true) override;
+ Error connect_to_host(const String &p_host, int p_port = -1, Ref<TLSOptions> p_tls_options = Ref<TLSOptions>()) override;
void set_connection(const Ref<StreamPeer> &p_connection) override;
Ref<StreamPeer> get_connection() const override;
void close() override;
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 9408a9c103..736a3ec82e 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -78,6 +78,10 @@ const char *Image::format_names[Image::FORMAT_MAX] = {
"ETC2_RGB8A1",
"ETC2_RA_AS_RG",
"FORMAT_DXT5_RA_AS_RG",
+ "ASTC_4x4",
+ "ASTC_4x4_HDR",
+ "ASTC_8x8",
+ "ASTC_8x8_HDR",
};
SavePNGFunc Image::save_png_func = nullptr;
@@ -2187,16 +2191,16 @@ void Image::initialize_data(int p_width, int p_height, bool p_use_mipmaps, Forma
int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
if (unlikely(p_data.size() != size)) {
- String description_mipmaps;
+ String description_mipmaps = get_format_name(p_format) + " ";
if (p_use_mipmaps) {
const int num_mipmaps = get_image_required_mipmaps(p_width, p_height, p_format);
if (num_mipmaps != 1) {
- description_mipmaps = vformat("with %d mipmaps", num_mipmaps);
+ description_mipmaps += vformat("with %d mipmaps", num_mipmaps);
} else {
- description_mipmaps = "with 1 mipmap";
+ description_mipmaps += "with 1 mipmap";
}
} else {
- description_mipmaps = "without mipmaps";
+ description_mipmaps += "without mipmaps";
}
const String description = vformat("%dx%dx%d (%s)", p_width, p_height, get_format_pixel_size(p_format), description_mipmaps);
ERR_FAIL_MSG(vformat("Expected Image data size of %s = %d bytes, got %d bytes instead.", description, size, p_data.size()));
@@ -2618,35 +2622,35 @@ Error Image::decompress() {
return OK;
}
-Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_lossy_quality, ASTCFormat p_astc_format) {
+Error Image::compress(CompressMode p_mode, CompressSource p_source, ASTCFormat p_astc_format) {
ERR_FAIL_INDEX_V_MSG(p_mode, COMPRESS_MAX, ERR_INVALID_PARAMETER, "Invalid compress mode.");
ERR_FAIL_INDEX_V_MSG(p_source, COMPRESS_SOURCE_MAX, ERR_INVALID_PARAMETER, "Invalid compress source.");
- return compress_from_channels(p_mode, detect_used_channels(p_source), p_lossy_quality, p_astc_format);
+ return compress_from_channels(p_mode, detect_used_channels(p_source), p_astc_format);
}
-Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality, ASTCFormat p_astc_format) {
+Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels, ASTCFormat p_astc_format) {
ERR_FAIL_COND_V(data.is_empty(), ERR_INVALID_DATA);
switch (p_mode) {
case COMPRESS_S3TC: {
ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE);
- _image_compress_bc_func(this, p_lossy_quality, p_channels);
+ _image_compress_bc_func(this, p_channels);
} break;
case COMPRESS_ETC: {
ERR_FAIL_COND_V(!_image_compress_etc1_func, ERR_UNAVAILABLE);
- _image_compress_etc1_func(this, p_lossy_quality);
+ _image_compress_etc1_func(this);
} break;
case COMPRESS_ETC2: {
ERR_FAIL_COND_V(!_image_compress_etc2_func, ERR_UNAVAILABLE);
- _image_compress_etc2_func(this, p_lossy_quality, p_channels);
+ _image_compress_etc2_func(this, p_channels);
} break;
case COMPRESS_BPTC: {
ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE);
- _image_compress_bptc_func(this, p_lossy_quality, p_channels);
+ _image_compress_bptc_func(this, p_channels);
} break;
case COMPRESS_ASTC: {
ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE);
- _image_compress_astc_func(this, p_lossy_quality, p_astc_format);
+ _image_compress_astc_func(this, p_astc_format);
} break;
case COMPRESS_MAX: {
ERR_FAIL_V(ERR_INVALID_PARAMETER);
@@ -3000,11 +3004,11 @@ ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr;
-void (*Image::_image_compress_bc_func)(Image *, float, Image::UsedChannels) = nullptr;
-void (*Image::_image_compress_bptc_func)(Image *, float, Image::UsedChannels) = nullptr;
-void (*Image::_image_compress_etc1_func)(Image *, float) = nullptr;
-void (*Image::_image_compress_etc2_func)(Image *, float, Image::UsedChannels) = nullptr;
-void (*Image::_image_compress_astc_func)(Image *, float, Image::ASTCFormat) = nullptr;
+void (*Image::_image_compress_bc_func)(Image *, Image::UsedChannels) = nullptr;
+void (*Image::_image_compress_bptc_func)(Image *, Image::UsedChannels) = nullptr;
+void (*Image::_image_compress_etc1_func)(Image *) = nullptr;
+void (*Image::_image_compress_etc2_func)(Image *, Image::UsedChannels) = nullptr;
+void (*Image::_image_compress_astc_func)(Image *, Image::ASTCFormat) = nullptr;
void (*Image::_image_decompress_bc)(Image *) = nullptr;
void (*Image::_image_decompress_bptc)(Image *) = nullptr;
void (*Image::_image_decompress_etc1)(Image *) = nullptr;
@@ -3426,8 +3430,8 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_invisible"), &Image::is_invisible);
ClassDB::bind_method(D_METHOD("detect_used_channels", "source"), &Image::detect_used_channels, DEFVAL(COMPRESS_SOURCE_GENERIC));
- ClassDB::bind_method(D_METHOD("compress", "mode", "source", "lossy_quality", "astc_format"), &Image::compress, DEFVAL(COMPRESS_SOURCE_GENERIC), DEFVAL(0.7), DEFVAL(ASTC_FORMAT_4x4));
- ClassDB::bind_method(D_METHOD("compress_from_channels", "mode", "channels", "lossy_quality", "astc_format"), &Image::compress_from_channels, DEFVAL(0.7), DEFVAL(ASTC_FORMAT_4x4));
+ ClassDB::bind_method(D_METHOD("compress", "mode", "source", "astc_format"), &Image::compress, DEFVAL(COMPRESS_SOURCE_GENERIC), DEFVAL(ASTC_FORMAT_4x4));
+ ClassDB::bind_method(D_METHOD("compress_from_channels", "mode", "channels", "astc_format"), &Image::compress_from_channels, DEFVAL(ASTC_FORMAT_4x4));
ClassDB::bind_method(D_METHOD("decompress"), &Image::decompress);
ClassDB::bind_method(D_METHOD("is_compressed"), &Image::is_compressed);
@@ -3547,11 +3551,11 @@ void Image::_bind_methods() {
BIND_ENUM_CONSTANT(ASTC_FORMAT_8x8);
}
-void Image::set_compress_bc_func(void (*p_compress_func)(Image *, float, UsedChannels)) {
+void Image::set_compress_bc_func(void (*p_compress_func)(Image *, UsedChannels)) {
_image_compress_bc_func = p_compress_func;
}
-void Image::set_compress_bptc_func(void (*p_compress_func)(Image *, float, UsedChannels)) {
+void Image::set_compress_bptc_func(void (*p_compress_func)(Image *, UsedChannels)) {
_image_compress_bptc_func = p_compress_func;
}
diff --git a/core/io/image.h b/core/io/image.h
index 29ceb9478f..8e353a8bb7 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -149,11 +149,11 @@ public:
static ImageMemLoadFunc _tga_mem_loader_func;
static ImageMemLoadFunc _bmp_mem_loader_func;
- static void (*_image_compress_bc_func)(Image *, float, UsedChannels p_channels);
- static void (*_image_compress_bptc_func)(Image *, float p_lossy_quality, UsedChannels p_channels);
- static void (*_image_compress_etc1_func)(Image *, float);
- static void (*_image_compress_etc2_func)(Image *, float, UsedChannels p_channels);
- static void (*_image_compress_astc_func)(Image *, float, ASTCFormat p_format);
+ static void (*_image_compress_bc_func)(Image *, UsedChannels p_channels);
+ static void (*_image_compress_bptc_func)(Image *, UsedChannels p_channels);
+ static void (*_image_compress_etc1_func)(Image *);
+ static void (*_image_compress_etc2_func)(Image *, UsedChannels p_channels);
+ static void (*_image_compress_astc_func)(Image *, ASTCFormat p_format);
static void (*_image_decompress_bc)(Image *);
static void (*_image_decompress_bptc)(Image *);
@@ -368,8 +368,8 @@ public:
COMPRESS_SOURCE_MAX,
};
- Error compress(CompressMode p_mode, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7, ASTCFormat p_astc_format = ASTC_FORMAT_4x4);
- Error compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality = 0.7, ASTCFormat p_astc_format = ASTC_FORMAT_4x4);
+ Error compress(CompressMode p_mode, CompressSource p_source = COMPRESS_SOURCE_GENERIC, ASTCFormat p_astc_format = ASTC_FORMAT_4x4);
+ Error compress_from_channels(CompressMode p_mode, UsedChannels p_channels, ASTCFormat p_astc_format = ASTC_FORMAT_4x4);
Error decompress();
bool is_compressed() const;
@@ -391,8 +391,8 @@ public:
Rect2i get_used_rect() const;
Ref<Image> get_region(const Rect2i &p_area) const;
- static void set_compress_bc_func(void (*p_compress_func)(Image *, float, UsedChannels));
- static void set_compress_bptc_func(void (*p_compress_func)(Image *, float, UsedChannels));
+ static void set_compress_bc_func(void (*p_compress_func)(Image *, UsedChannels));
+ static void set_compress_bptc_func(void (*p_compress_func)(Image *, UsedChannels));
static String get_format_name(Format p_format);
Error load_png_from_buffer(const Vector<uint8_t> &p_array);
diff --git a/core/io/packet_peer_dtls.cpp b/core/io/packet_peer_dtls.cpp
index c0998f10bc..18bef3ff3c 100644
--- a/core/io/packet_peer_dtls.cpp
+++ b/core/io/packet_peer_dtls.cpp
@@ -48,7 +48,7 @@ bool PacketPeerDTLS::is_available() {
void PacketPeerDTLS::_bind_methods() {
ClassDB::bind_method(D_METHOD("poll"), &PacketPeerDTLS::poll);
- ClassDB::bind_method(D_METHOD("connect_to_peer", "packet_peer", "validate_certs", "for_hostname", "valid_certificate"), &PacketPeerDTLS::connect_to_peer, DEFVAL(true), DEFVAL(String()), DEFVAL(Ref<X509Certificate>()));
+ ClassDB::bind_method(D_METHOD("connect_to_peer", "packet_peer", "hostname", "client_options"), &PacketPeerDTLS::connect_to_peer, DEFVAL(Ref<TLSOptions>()));
ClassDB::bind_method(D_METHOD("get_status"), &PacketPeerDTLS::get_status);
ClassDB::bind_method(D_METHOD("disconnect_from_peer"), &PacketPeerDTLS::disconnect_from_peer);
diff --git a/core/io/packet_peer_dtls.h b/core/io/packet_peer_dtls.h
index 5ba1faed7c..3990a851f7 100644
--- a/core/io/packet_peer_dtls.h
+++ b/core/io/packet_peer_dtls.h
@@ -53,7 +53,7 @@ public:
};
virtual void poll() = 0;
- virtual Error connect_to_peer(Ref<PacketPeerUDP> p_base, bool p_validate_certs = true, const String &p_for_hostname = String(), Ref<X509Certificate> p_ca_certs = Ref<X509Certificate>()) = 0;
+ virtual Error connect_to_peer(Ref<PacketPeerUDP> p_base, const String &p_hostname, Ref<TLSOptions> p_options = Ref<TLSOptions>()) = 0;
virtual void disconnect_from_peer() = 0;
virtual Status get_status() const = 0;
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index e44bbc246b..4abcbffb61 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -260,15 +260,35 @@ Ref<Resource> Resource::duplicate(bool p_subresources) const {
}
Variant p = get(E.name);
- if ((p.get_type() == Variant::DICTIONARY || p.get_type() == Variant::ARRAY)) {
- r->set(E.name, p.duplicate(p_subresources));
- } else if (p.get_type() == Variant::OBJECT && !(E.usage & PROPERTY_USAGE_NEVER_DUPLICATE) && (p_subresources || (E.usage & PROPERTY_USAGE_ALWAYS_DUPLICATE))) {
- Ref<Resource> sr = p;
- if (sr.is_valid()) {
- r->set(E.name, sr->duplicate(p_subresources));
+ switch (p.get_type()) {
+ case Variant::Type::DICTIONARY:
+ case Variant::Type::ARRAY:
+ case Variant::Type::PACKED_BYTE_ARRAY:
+ case Variant::Type::PACKED_COLOR_ARRAY:
+ case Variant::Type::PACKED_INT32_ARRAY:
+ case Variant::Type::PACKED_INT64_ARRAY:
+ case Variant::Type::PACKED_FLOAT32_ARRAY:
+ case Variant::Type::PACKED_FLOAT64_ARRAY:
+ case Variant::Type::PACKED_STRING_ARRAY:
+ case Variant::Type::PACKED_VECTOR2_ARRAY:
+ case Variant::Type::PACKED_VECTOR3_ARRAY: {
+ r->set(E.name, p.duplicate(p_subresources));
+ } break;
+
+ case Variant::Type::OBJECT: {
+ if (!(E.usage & PROPERTY_USAGE_NEVER_DUPLICATE) && (p_subresources || (E.usage & PROPERTY_USAGE_ALWAYS_DUPLICATE))) {
+ Ref<Resource> sr = p;
+ if (sr.is_valid()) {
+ r->set(E.name, sr->duplicate(p_subresources));
+ }
+ } else {
+ r->set(E.name, p);
+ }
+ } break;
+
+ default: {
+ r->set(E.name, p);
}
- } else {
- r->set(E.name, p);
}
}
diff --git a/core/io/stream_peer_tls.cpp b/core/io/stream_peer_tls.cpp
index 71fadd1d30..69877974e6 100644
--- a/core/io/stream_peer_tls.cpp
+++ b/core/io/stream_peer_tls.cpp
@@ -41,31 +41,17 @@ StreamPeerTLS *StreamPeerTLS::create() {
return nullptr;
}
-bool StreamPeerTLS::available = false;
-
bool StreamPeerTLS::is_available() {
- return available;
-}
-
-void StreamPeerTLS::set_blocking_handshake_enabled(bool p_enabled) {
- blocking_handshake = p_enabled;
-}
-
-bool StreamPeerTLS::is_blocking_handshake_enabled() const {
- return blocking_handshake;
+ return _create != nullptr;
}
void StreamPeerTLS::_bind_methods() {
ClassDB::bind_method(D_METHOD("poll"), &StreamPeerTLS::poll);
- ClassDB::bind_method(D_METHOD("accept_stream", "stream", "private_key", "certificate", "chain"), &StreamPeerTLS::accept_stream, DEFVAL(Ref<X509Certificate>()));
- ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname", "valid_certificate"), &StreamPeerTLS::connect_to_stream, DEFVAL(false), DEFVAL(String()), DEFVAL(Ref<X509Certificate>()));
+ ClassDB::bind_method(D_METHOD("accept_stream", "stream", "server_options"), &StreamPeerTLS::accept_stream);
+ ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "common_name", "client_options"), &StreamPeerTLS::connect_to_stream, DEFVAL(Ref<TLSOptions>()));
ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerTLS::get_status);
ClassDB::bind_method(D_METHOD("get_stream"), &StreamPeerTLS::get_stream);
ClassDB::bind_method(D_METHOD("disconnect_from_stream"), &StreamPeerTLS::disconnect_from_stream);
- ClassDB::bind_method(D_METHOD("set_blocking_handshake_enabled", "enabled"), &StreamPeerTLS::set_blocking_handshake_enabled);
- ClassDB::bind_method(D_METHOD("is_blocking_handshake_enabled"), &StreamPeerTLS::is_blocking_handshake_enabled);
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking_handshake"), "set_blocking_handshake_enabled", "is_blocking_handshake_enabled");
BIND_ENUM_CONSTANT(STATUS_DISCONNECTED);
BIND_ENUM_CONSTANT(STATUS_HANDSHAKING);
diff --git a/core/io/stream_peer_tls.h b/core/io/stream_peer_tls.h
index 6666107ad8..5894abb7a4 100644
--- a/core/io/stream_peer_tls.h
+++ b/core/io/stream_peer_tls.h
@@ -41,10 +41,6 @@ protected:
static StreamPeerTLS *(*_create)();
static void _bind_methods();
- static bool available;
-
- bool blocking_handshake = true;
-
public:
enum Status {
STATUS_DISCONNECTED,
@@ -54,12 +50,9 @@ public:
STATUS_ERROR_HOSTNAME_MISMATCH
};
- void set_blocking_handshake_enabled(bool p_enabled);
- bool is_blocking_handshake_enabled() const;
-
virtual void poll() = 0;
- virtual Error accept_stream(Ref<StreamPeer> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain = Ref<X509Certificate>()) = 0;
- virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String(), Ref<X509Certificate> p_valid_cert = Ref<X509Certificate>()) = 0;
+ virtual Error accept_stream(Ref<StreamPeer> p_base, Ref<TLSOptions> p_options) = 0;
+ virtual Error connect_to_stream(Ref<StreamPeer> p_base, const String &p_common_name, Ref<TLSOptions> p_options) = 0;
virtual Status get_status() const = 0;
virtual Ref<StreamPeer> get_stream() const = 0;
diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp
index 910995d717..96010b4096 100644
--- a/core/math/transform_2d.cpp
+++ b/core/math/transform_2d.cpp
@@ -263,39 +263,12 @@ real_t Transform2D::basis_determinant() const {
return columns[0].x * columns[1].y - columns[0].y * columns[1].x;
}
-Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, const real_t p_c) const {
- //extract parameters
- Vector2 p1 = get_origin();
- Vector2 p2 = p_transform.get_origin();
-
- real_t r1 = get_rotation();
- real_t r2 = p_transform.get_rotation();
-
- Size2 s1 = get_scale();
- Size2 s2 = p_transform.get_scale();
-
- //slerp rotation
- Vector2 v1(Math::cos(r1), Math::sin(r1));
- Vector2 v2(Math::cos(r2), Math::sin(r2));
-
- real_t dot = v1.dot(v2);
-
- dot = CLAMP(dot, (real_t)-1.0, (real_t)1.0);
-
- Vector2 v;
-
- if (dot > 0.9995f) {
- v = v1.lerp(v2, p_c).normalized(); //linearly interpolate to avoid numerical precision issues
- } else {
- real_t angle = p_c * Math::acos(dot);
- Vector2 v3 = (v2 - v1 * dot).normalized();
- v = v1 * Math::cos(angle) + v3 * Math::sin(angle);
- }
-
- //construct matrix
- Transform2D res(v.angle(), p1.lerp(p2, p_c));
- res.scale_basis(s1.lerp(s2, p_c));
- return res;
+Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, const real_t p_weight) const {
+ return Transform2D(
+ Math::lerp_angle(get_rotation(), p_transform.get_rotation(), p_weight),
+ get_scale().lerp(p_transform.get_scale(), p_weight),
+ Math::lerp_angle(get_skew(), p_transform.get_skew(), p_weight),
+ get_origin().lerp(p_transform.get_origin(), p_weight));
}
void Transform2D::operator*=(const real_t p_val) {
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 86469852e3..ef7d860d19 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -553,6 +553,16 @@ void OS::add_frame_delay(bool p_can_draw) {
}
}
+OS::PreferredTextureFormat OS::get_preferred_texture_format() const {
+#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)
+ return PREFERRED_TEXTURE_FORMAT_ETC2_ASTC; // By rule, ARM hardware uses ETC texture compression.
+#elif defined(__x86_64__) || defined(_M_X64) || defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86)
+ return PREFERRED_TEXTURE_FORMAT_S3TC_BPTC; // By rule, X86 hardware prefers S3TC and derivatives.
+#else
+ return PREFERRED_TEXTURE_FORMAT_S3TC_BPTC; // Override in platform if needed.
+#endif
+}
+
OS::OS() {
singleton = this;
diff --git a/core/os/os.h b/core/os/os.h
index 436a83eae3..d77890d89d 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -290,6 +290,14 @@ public:
virtual Vector<String> get_granted_permissions() const { return Vector<String>(); }
virtual void process_and_drop_events() {}
+
+ enum PreferredTextureFormat {
+ PREFERRED_TEXTURE_FORMAT_S3TC_BPTC,
+ PREFERRED_TEXTURE_FORMAT_ETC2_ASTC
+ };
+
+ virtual PreferredTextureFormat get_preferred_texture_format() const;
+
OS();
virtual ~OS();
};
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index 700174bdae..a374e7c009 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -209,6 +209,7 @@ void register_core_types() {
GDREGISTER_CLASS(AESContext);
ClassDB::register_custom_instance_class<X509Certificate>();
ClassDB::register_custom_instance_class<CryptoKey>();
+ GDREGISTER_ABSTRACT_CLASS(TLSOptions);
ClassDB::register_custom_instance_class<HMACContext>();
ClassDB::register_custom_instance_class<Crypto>();
ClassDB::register_custom_instance_class<StreamPeerTLS>();
diff --git a/core/variant/array.cpp b/core/variant/array.cpp
index 94d1596514..d3c5ca801f 100644
--- a/core/variant/array.cpp
+++ b/core/variant/array.cpp
@@ -64,7 +64,7 @@ void Array::_ref(const Array &p_from) const {
_unref();
- _p = p_from._p;
+ _p = _fp;
}
void Array::_unref() const {
@@ -191,62 +191,73 @@ uint32_t Array::recursive_hash(int recursion_count) const {
return hash_fmix32(h);
}
-bool Array::_assign(const Array &p_array) {
- bool can_convert = p_array._p->typed.type == Variant::NIL;
- can_convert |= _p->typed.type == Variant::STRING && p_array._p->typed.type == Variant::STRING_NAME;
- can_convert |= _p->typed.type == Variant::STRING_NAME && p_array._p->typed.type == Variant::STRING;
+void Array::operator=(const Array &p_array) {
+ if (this == &p_array) {
+ return;
+ }
+ _ref(p_array);
+}
+
+void Array::assign(const Array &p_array) {
+ const ContainerTypeValidate &typed = _p->typed;
+ const ContainerTypeValidate &source_typed = p_array._p->typed;
- if (_p->typed.type != Variant::OBJECT && _p->typed.type == p_array._p->typed.type) {
- //same type or untyped, just reference, should be fine
- _ref(p_array);
- } else if (_p->typed.type == Variant::NIL) { //from typed to untyped, must copy, but this is cheap anyway
+ if (typed == source_typed || typed.type == Variant::NIL || (source_typed.type == Variant::OBJECT && typed.can_reference(source_typed))) {
+ // from same to same or
+ // from anything to variants or
+ // from subclasses to base classes
_p->array = p_array._p->array;
- } else if (can_convert) { //from untyped to typed, must try to check if they are all valid
- if (_p->typed.type == Variant::OBJECT) {
- //for objects, it needs full validation, either can be converted or fail
- for (int i = 0; i < p_array._p->array.size(); i++) {
- const Variant &element = p_array._p->array[i];
- if (element.get_type() != Variant::OBJECT || !_p->typed.validate_object(element, "assign")) {
- return false;
- }
- }
- _p->array = p_array._p->array; //then just copy, which is cheap anyway
+ return;
+ }
- } else {
- //for non objects, we need to check if there is a valid conversion, which needs to happen one by one, so this is the worst case.
- Vector<Variant> new_array;
- new_array.resize(p_array._p->array.size());
- for (int i = 0; i < p_array._p->array.size(); i++) {
- Variant src_val = p_array._p->array[i];
- if (src_val.get_type() == _p->typed.type) {
- new_array.write[i] = src_val;
- } else if (Variant::can_convert_strict(src_val.get_type(), _p->typed.type)) {
- Variant *ptr = &src_val;
- Callable::CallError ce;
- Variant::construct(_p->typed.type, new_array.write[i], (const Variant **)&ptr, 1, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
- ERR_FAIL_V_MSG(false, "Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(src_val.get_type()) + "' to '" + Variant::get_type_name(_p->typed.type) + "'.");
- }
- } else {
- ERR_FAIL_V_MSG(false, "Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(src_val.get_type()) + "' to '" + Variant::get_type_name(_p->typed.type) + "'.");
- }
+ const Variant *source = p_array._p->array.ptr();
+ int size = p_array._p->array.size();
+
+ if ((source_typed.type == Variant::NIL && typed.type == Variant::OBJECT) || (source_typed.type == Variant::OBJECT && source_typed.can_reference(typed))) {
+ // from variants to objects or
+ // from base classes to subclasses
+ for (int i = 0; i < size; i++) {
+ const Variant &element = source[i];
+ if (element.get_type() != Variant::NIL && (element.get_type() != Variant::OBJECT || !typed.validate_object(element, "assign"))) {
+ ERR_FAIL_MSG(vformat(R"(Unable to convert array index %i from "%s" to "%s".)", i, Variant::get_type_name(element.get_type()), Variant::get_type_name(typed.type)));
}
+ }
+ _p->array = p_array._p->array;
+ return;
+ }
- _p->array = new_array;
+ Vector<Variant> array;
+ array.resize(size);
+ Variant *data = array.ptrw();
+
+ if (source_typed.type == Variant::NIL && typed.type != Variant::OBJECT) {
+ // from variants to primitives
+ for (int i = 0; i < size; i++) {
+ const Variant *value = source + i;
+ if (value->get_type() == typed.type) {
+ data[i] = *value;
+ continue;
+ }
+ if (!Variant::can_convert_strict(value->get_type(), typed.type)) {
+ ERR_FAIL_MSG("Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(value->get_type()) + "' to '" + Variant::get_type_name(typed.type) + "'.");
+ }
+ Callable::CallError ce;
+ Variant::construct(typed.type, data[i], &value, 1, ce);
+ ERR_FAIL_COND_MSG(ce.error, vformat(R"(Unable to convert array index %i from "%s" to "%s".)", i, Variant::get_type_name(value->get_type()), Variant::get_type_name(typed.type)));
+ }
+ } else if (Variant::can_convert_strict(source_typed.type, typed.type)) {
+ // from primitives to different convertable primitives
+ for (int i = 0; i < size; i++) {
+ const Variant *value = source + i;
+ Callable::CallError ce;
+ Variant::construct(typed.type, data[i], &value, 1, ce);
+ ERR_FAIL_COND_MSG(ce.error, vformat(R"(Unable to convert array index %i from "%s" to "%s".)", i, Variant::get_type_name(value->get_type()), Variant::get_type_name(typed.type)));
}
- } else if (_p->typed.can_reference(p_array._p->typed)) { //same type or compatible
- _ref(p_array);
} else {
- ERR_FAIL_V_MSG(false, "Assignment of arrays of incompatible types.");
+ ERR_FAIL_MSG(vformat(R"(Cannot assign contents of "Array[%s]" to "Array[%s]".)", Variant::get_type_name(source_typed.type), Variant::get_type_name(typed.type)));
}
- return true;
-}
-void Array::operator=(const Array &p_array) {
- if (this == &p_array) {
- return;
- }
- _ref(p_array);
+ _p->array = array;
}
void Array::push_back(const Variant &p_value) {
@@ -269,7 +280,15 @@ void Array::append_array(const Array &p_array) {
Error Array::resize(int p_new_size) {
ERR_FAIL_COND_V_MSG(_p->read_only, ERR_LOCKED, "Array is in read-only state.");
- return _p->array.resize(p_new_size);
+ Variant::Type &variant_type = _p->typed.type;
+ int old_size = _p->array.size();
+ Error err = _p->array.resize_zeroed(p_new_size);
+ if (!err && variant_type != Variant::NIL && variant_type != Variant::OBJECT) {
+ for (int i = old_size; i < p_new_size; i++) {
+ VariantInternal::initialize(&_p->array.write[i], variant_type);
+ }
+ }
+ return err;
}
Error Array::insert(int p_pos, const Variant &p_value) {
@@ -403,24 +422,22 @@ Array Array::duplicate(bool p_deep) const {
Array Array::recursive_duplicate(bool p_deep, int recursion_count) const {
Array new_arr;
+ new_arr._p->typed = _p->typed;
if (recursion_count > MAX_RECURSION) {
ERR_PRINT("Max recursion reached");
return new_arr;
}
- int element_count = size();
- new_arr.resize(element_count);
- new_arr._p->typed = _p->typed;
if (p_deep) {
recursion_count++;
+ int element_count = size();
+ new_arr.resize(element_count);
for (int i = 0; i < element_count; i++) {
new_arr[i] = get(i).recursive_duplicate(true, recursion_count);
}
} else {
- for (int i = 0; i < element_count; i++) {
- new_arr[i] = get(i);
- }
+ new_arr._p->array = _p->array;
}
return new_arr;
@@ -737,11 +754,7 @@ Array::Array(const Array &p_from, uint32_t p_type, const StringName &p_class_nam
_p = memnew(ArrayPrivate);
_p->refcount.init();
set_typed(p_type, p_class_name, p_script);
- _assign(p_from);
-}
-
-bool Array::typed_assign(const Array &p_other) {
- return _assign(p_other);
+ assign(p_from);
}
void Array::set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script) {
@@ -763,6 +776,10 @@ bool Array::is_typed() const {
return _p->typed.type != Variant::NIL;
}
+bool Array::is_same_typed(const Array &p_other) const {
+ return _p->typed == p_other._p->typed;
+}
+
uint32_t Array::get_typed_builtin() const {
return _p->typed.type;
}
diff --git a/core/variant/array.h b/core/variant/array.h
index d9ca3278fb..4ef8ba8ce7 100644
--- a/core/variant/array.h
+++ b/core/variant/array.h
@@ -43,13 +43,11 @@ class Callable;
class Array {
mutable ArrayPrivate *_p;
- void _ref(const Array &p_from) const;
void _unref() const;
-protected:
- bool _assign(const Array &p_array);
-
public:
+ void _ref(const Array &p_from) const;
+
Variant &operator[](int p_idx);
const Variant &operator[](int p_idx) const;
@@ -68,6 +66,7 @@ public:
uint32_t recursive_hash(int recursion_count) const;
void operator=(const Array &p_array);
+ void assign(const Array &p_array);
void push_back(const Variant &p_value);
_FORCE_INLINE_ void append(const Variant &p_value) { push_back(p_value); } //for python compatibility
void append_array(const Array &p_array);
@@ -120,9 +119,9 @@ public:
const void *id() const;
- bool typed_assign(const Array &p_other);
void set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script);
bool is_typed() const;
+ bool is_same_typed(const Array &p_other) const;
uint32_t get_typed_builtin() const;
StringName get_typed_class_name() const;
Variant get_typed_script() const;
diff --git a/core/variant/container_type_validate.h b/core/variant/container_type_validate.h
index 377be03bdf..796b66dc77 100644
--- a/core/variant/container_type_validate.h
+++ b/core/variant/container_type_validate.h
@@ -74,8 +74,15 @@ struct ContainerTypeValidate {
return true;
}
+ _FORCE_INLINE_ bool operator==(const ContainerTypeValidate &p_type) const {
+ return type == p_type.type && class_name == p_type.class_name && script == p_type.script;
+ }
+ _FORCE_INLINE_ bool operator!=(const ContainerTypeValidate &p_type) const {
+ return type != p_type.type || class_name != p_type.class_name || script != p_type.script;
+ }
+
// Coerces String and StringName into each other when needed.
- _FORCE_INLINE_ bool validate(Variant &inout_variant, const char *p_operation = "use") {
+ _FORCE_INLINE_ bool validate(Variant &inout_variant, const char *p_operation = "use") const {
if (type == Variant::NIL) {
return true;
}
@@ -102,7 +109,7 @@ struct ContainerTypeValidate {
return validate_object(inout_variant, p_operation);
}
- _FORCE_INLINE_ bool validate_object(const Variant &p_variant, const char *p_operation = "use") {
+ _FORCE_INLINE_ bool validate_object(const Variant &p_variant, const char *p_operation = "use") const {
ERR_FAIL_COND_V(p_variant.get_type() != Variant::OBJECT, false);
#ifdef DEBUG_ENABLED
diff --git a/core/variant/typed_array.h b/core/variant/typed_array.h
index 5aeabbacf8..03e557819b 100644
--- a/core/variant/typed_array.h
+++ b/core/variant/typed_array.h
@@ -40,14 +40,9 @@
template <class T>
class TypedArray : public Array {
public:
- template <class U>
- _FORCE_INLINE_ void operator=(const TypedArray<U> &p_array) {
- static_assert(__is_base_of(T, U));
- _assign(p_array);
- }
-
_FORCE_INLINE_ void operator=(const Array &p_array) {
- _assign(p_array);
+ ERR_FAIL_COND_MSG(!is_same_typed(p_array), "Cannot assign an array with a different element type.");
+ _ref(p_array);
}
_FORCE_INLINE_ TypedArray(const Variant &p_variant) :
Array(Array(p_variant), Variant::OBJECT, T::get_class_static(), Variant()) {
@@ -62,22 +57,23 @@ public:
//specialization for the rest of variant types
-#define MAKE_TYPED_ARRAY(m_type, m_variant_type) \
- template <> \
- class TypedArray<m_type> : public Array { \
- public: \
- _FORCE_INLINE_ void operator=(const Array &p_array) { \
- _assign(p_array); \
- } \
- _FORCE_INLINE_ TypedArray(const Variant &p_variant) : \
- Array(Array(p_variant), m_variant_type, StringName(), Variant()) { \
- } \
- _FORCE_INLINE_ TypedArray(const Array &p_array) : \
- Array(p_array, m_variant_type, StringName(), Variant()) { \
- } \
- _FORCE_INLINE_ TypedArray() { \
- set_typed(m_variant_type, StringName(), Variant()); \
- } \
+#define MAKE_TYPED_ARRAY(m_type, m_variant_type) \
+ template <> \
+ class TypedArray<m_type> : public Array { \
+ public: \
+ _FORCE_INLINE_ void operator=(const Array &p_array) { \
+ ERR_FAIL_COND_MSG(!is_same_typed(p_array), "Cannot assign an array with a different element type."); \
+ _ref(p_array); \
+ } \
+ _FORCE_INLINE_ TypedArray(const Variant &p_variant) : \
+ Array(Array(p_variant), m_variant_type, StringName(), Variant()) { \
+ } \
+ _FORCE_INLINE_ TypedArray(const Array &p_array) : \
+ Array(p_array, m_variant_type, StringName(), Variant()) { \
+ } \
+ _FORCE_INLINE_ TypedArray() { \
+ set_typed(m_variant_type, StringName(), Variant()); \
+ } \
};
MAKE_TYPED_ARRAY(bool, Variant::BOOL)
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 3b2d29734d..0c0c8f657a 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -2189,6 +2189,7 @@ static void _register_variant_builtin_methods() {
bind_method(Array, is_empty, sarray(), varray());
bind_method(Array, clear, sarray(), varray());
bind_method(Array, hash, sarray(), varray());
+ bind_method(Array, assign, sarray("array"), varray());
bind_method(Array, push_back, sarray("value"), varray());
bind_method(Array, push_front, sarray("value"), varray());
bind_method(Array, append, sarray("value"), varray());
@@ -2223,9 +2224,8 @@ static void _register_variant_builtin_methods() {
bind_method(Array, all, sarray("method"), varray());
bind_method(Array, max, sarray(), varray());
bind_method(Array, min, sarray(), varray());
- bind_method(Array, typed_assign, sarray("array"), varray());
- bind_method(Array, set_typed, sarray("type", "class_name", "script"), varray());
bind_method(Array, is_typed, sarray(), varray());
+ bind_method(Array, is_same_typed, sarray("array"), varray());
bind_method(Array, get_typed_builtin, sarray(), varray());
bind_method(Array, get_typed_class_name, sarray(), varray());
bind_method(Array, get_typed_script, sarray(), varray());
@@ -2403,7 +2403,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedStringArray, remove_at, sarray("index"), varray());
bind_method(PackedStringArray, insert, sarray("at_index", "value"), varray());
bind_method(PackedStringArray, fill, sarray("value"), varray());
- bind_method(PackedStringArray, resize, sarray("new_size"), varray());
+ bind_methodv(PackedStringArray, resize, &PackedStringArray::resize_zeroed, sarray("new_size"), varray());
bind_method(PackedStringArray, clear, sarray(), varray());
bind_method(PackedStringArray, has, sarray("value"), varray());
bind_method(PackedStringArray, reverse, sarray(), varray());
@@ -2427,7 +2427,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedVector2Array, remove_at, sarray("index"), varray());
bind_method(PackedVector2Array, insert, sarray("at_index", "value"), varray());
bind_method(PackedVector2Array, fill, sarray("value"), varray());
- bind_method(PackedVector2Array, resize, sarray("new_size"), varray());
+ bind_methodv(PackedVector2Array, resize, &PackedVector2Array::resize_zeroed, sarray("new_size"), varray());
bind_method(PackedVector2Array, clear, sarray(), varray());
bind_method(PackedVector2Array, has, sarray("value"), varray());
bind_method(PackedVector2Array, reverse, sarray(), varray());
@@ -2451,7 +2451,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedVector3Array, remove_at, sarray("index"), varray());
bind_method(PackedVector3Array, insert, sarray("at_index", "value"), varray());
bind_method(PackedVector3Array, fill, sarray("value"), varray());
- bind_method(PackedVector3Array, resize, sarray("new_size"), varray());
+ bind_methodv(PackedVector3Array, resize, &PackedVector3Array::resize_zeroed, sarray("new_size"), varray());
bind_method(PackedVector3Array, clear, sarray(), varray());
bind_method(PackedVector3Array, has, sarray("value"), varray());
bind_method(PackedVector3Array, reverse, sarray(), varray());
@@ -2475,7 +2475,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedColorArray, remove_at, sarray("index"), varray());
bind_method(PackedColorArray, insert, sarray("at_index", "value"), varray());
bind_method(PackedColorArray, fill, sarray("value"), varray());
- bind_method(PackedColorArray, resize, sarray("new_size"), varray());
+ bind_methodv(PackedColorArray, resize, &PackedColorArray::resize_zeroed, sarray("new_size"), varray());
bind_method(PackedColorArray, clear, sarray(), varray());
bind_method(PackedColorArray, has, sarray("value"), varray());
bind_method(PackedColorArray, reverse, sarray(), varray());
diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h
index 21e1d21342..0d55ee4ae2 100644
--- a/core/variant/variant_internal.h
+++ b/core/variant/variant_internal.h
@@ -58,7 +58,13 @@ public:
init_basis(v);
break;
case Variant::TRANSFORM3D:
- init_transform(v);
+ init_transform3d(v);
+ break;
+ case Variant::PROJECTION:
+ init_projection(v);
+ break;
+ case Variant::COLOR:
+ init_color(v);
break;
case Variant::STRING_NAME:
init_string_name(v);
@@ -209,13 +215,12 @@ public:
// Should be in the same order as Variant::Type for consistency.
// Those primitive and vector types don't need an `init_` method:
- // Nil, bool, float, Vector2/i, Rect2/i, Vector3/i, Plane, Quat, Color, RID.
+ // Nil, bool, float, Vector2/i, Rect2/i, Vector3/i, Plane, Quat, RID.
// Object is a special case, handled via `object_assign_null`.
_FORCE_INLINE_ static void init_string(Variant *v) {
memnew_placement(v->_data._mem, String);
v->type = Variant::STRING;
}
-
_FORCE_INLINE_ static void init_transform2d(Variant *v) {
v->_data._transform2d = (Transform2D *)Variant::Pools::_bucket_small.alloc();
memnew_placement(v->_data._transform2d, Transform2D);
@@ -231,7 +236,7 @@ public:
memnew_placement(v->_data._basis, Basis);
v->type = Variant::BASIS;
}
- _FORCE_INLINE_ static void init_transform(Variant *v) {
+ _FORCE_INLINE_ static void init_transform3d(Variant *v) {
v->_data._transform3d = (Transform3D *)Variant::Pools::_bucket_medium.alloc();
memnew_placement(v->_data._transform3d, Transform3D);
v->type = Variant::TRANSFORM3D;
@@ -241,6 +246,10 @@ public:
memnew_placement(v->_data._projection, Projection);
v->type = Variant::PROJECTION;
}
+ _FORCE_INLINE_ static void init_color(Variant *v) {
+ memnew_placement(v->_data._mem, Color);
+ v->type = Variant::COLOR;
+ }
_FORCE_INLINE_ static void init_string_name(Variant *v) {
memnew_placement(v->_data._mem, StringName);
v->type = Variant::STRING_NAME;
@@ -1191,7 +1200,7 @@ struct VariantInitializer<Basis> {
template <>
struct VariantInitializer<Transform3D> {
- static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_transform(v); }
+ static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_transform3d(v); }
};
template <>
struct VariantInitializer<Projection> {
diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp
index 87874deb8d..a40fcfbd47 100644
--- a/core/variant/variant_parser.cpp
+++ b/core/variant/variant_parser.cpp
@@ -910,7 +910,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
bool at_key = true;
String key;
- Token token2;
bool need_comma = false;
while (true) {
@@ -920,18 +919,18 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
}
if (at_key) {
- Error err = get_token(p_stream, token2, line, r_err_str);
+ Error err = get_token(p_stream, token, line, r_err_str);
if (err != OK) {
return err;
}
- if (token2.type == TK_PARENTHESIS_CLOSE) {
+ if (token.type == TK_PARENTHESIS_CLOSE) {
value = ref.is_valid() ? Variant(ref) : Variant(obj);
return OK;
}
if (need_comma) {
- if (token2.type != TK_COMMA) {
+ if (token.type != TK_COMMA) {
r_err_str = "Expected '}' or ','";
return ERR_PARSE_ERROR;
} else {
@@ -940,31 +939,31 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
}
}
- if (token2.type != TK_STRING) {
+ if (token.type != TK_STRING) {
r_err_str = "Expected property name as string";
return ERR_PARSE_ERROR;
}
- key = token2.value;
+ key = token.value;
- err = get_token(p_stream, token2, line, r_err_str);
+ err = get_token(p_stream, token, line, r_err_str);
if (err != OK) {
return err;
}
- if (token2.type != TK_COLON) {
+ if (token.type != TK_COLON) {
r_err_str = "Expected ':'";
return ERR_PARSE_ERROR;
}
at_key = false;
} else {
- Error err = get_token(p_stream, token2, line, r_err_str);
+ Error err = get_token(p_stream, token, line, r_err_str);
if (err != OK) {
return err;
}
Variant v;
- err = parse_value(token2, v, p_stream, line, r_err_str, p_res_parser);
+ err = parse_value(token, v, p_stream, line, r_err_str, p_res_parser);
if (err) {
return err;
}
@@ -1026,6 +1025,89 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
return ERR_PARSE_ERROR;
}
}
+ } else if (id == "Array") {
+ Error err = OK;
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_BRACKET_OPEN) {
+ r_err_str = "Expected '['";
+ return ERR_PARSE_ERROR;
+ }
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_IDENTIFIER) {
+ r_err_str = "Expected type identifier";
+ return ERR_PARSE_ERROR;
+ }
+
+ static HashMap<StringName, Variant::Type> builtin_types;
+ if (builtin_types.is_empty()) {
+ for (int i = 1; i < Variant::VARIANT_MAX; i++) {
+ builtin_types[Variant::get_type_name((Variant::Type)i)] = (Variant::Type)i;
+ }
+ }
+
+ Array array = Array();
+ bool got_bracket_token = false;
+ if (builtin_types.has(token.value)) {
+ array.set_typed(builtin_types.get(token.value), StringName(), Variant());
+ } else if (token.value == "Resource" || token.value == "SubResource" || token.value == "ExtResource") {
+ Variant resource;
+ err = parse_value(token, resource, p_stream, line, r_err_str, p_res_parser);
+ if (err) {
+ if (token.value == "Resource" && err == ERR_PARSE_ERROR && r_err_str == "Expected '('" && token.type == TK_BRACKET_CLOSE) {
+ err = OK;
+ r_err_str = String();
+ array.set_typed(Variant::OBJECT, token.value, Variant());
+ got_bracket_token = true;
+ } else {
+ return err;
+ }
+ } else {
+ Ref<Script> script = resource;
+ if (script.is_valid() && script->is_valid()) {
+ array.set_typed(Variant::OBJECT, script->get_instance_base_type(), script);
+ }
+ }
+ } else if (ClassDB::class_exists(token.value)) {
+ array.set_typed(Variant::OBJECT, token.value, Variant());
+ }
+
+ if (!got_bracket_token) {
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_BRACKET_CLOSE) {
+ r_err_str = "Expected ']'";
+ return ERR_PARSE_ERROR;
+ }
+ }
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_PARENTHESIS_OPEN) {
+ r_err_str = "Expected '('";
+ return ERR_PARSE_ERROR;
+ }
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_BRACKET_OPEN) {
+ r_err_str = "Expected '['";
+ return ERR_PARSE_ERROR;
+ }
+
+ Array values;
+ err = _parse_array(values, p_stream, line, r_err_str, p_res_parser);
+ if (err) {
+ return err;
+ }
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_PARENTHESIS_CLOSE) {
+ r_err_str = "Expected ')'";
+ return ERR_PARSE_ERROR;
+ }
+
+ array.assign(values);
+
+ value = array;
} else if (id == "PackedByteArray" || id == "PoolByteArray" || id == "ByteArray") {
Vector<uint8_t> args;
Error err = _parse_construct<uint8_t>(p_stream, args, line, r_err_str);
@@ -1843,6 +1925,38 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
} break;
case Variant::ARRAY: {
+ Array array = p_variant;
+ if (array.get_typed_builtin() != Variant::NIL) {
+ p_store_string_func(p_store_string_ud, "Array[");
+
+ Variant::Type builtin_type = (Variant::Type)array.get_typed_builtin();
+ StringName class_name = array.get_typed_class_name();
+ Ref<Script> script = array.get_typed_script();
+
+ if (script.is_valid()) {
+ String resource_text = String();
+ if (p_encode_res_func) {
+ resource_text = p_encode_res_func(p_encode_res_ud, script);
+ }
+ if (resource_text.is_empty() && script->get_path().is_resource_file()) {
+ resource_text = "Resource(\"" + script->get_path() + "\")";
+ }
+
+ if (!resource_text.is_empty()) {
+ p_store_string_func(p_store_string_ud, resource_text);
+ } else {
+ ERR_PRINT("Failed to encode a path to a custom script for an array type.");
+ p_store_string_func(p_store_string_ud, class_name);
+ }
+ } else if (class_name != StringName()) {
+ p_store_string_func(p_store_string_ud, class_name);
+ } else {
+ p_store_string_func(p_store_string_ud, Variant::get_type_name(builtin_type));
+ }
+
+ p_store_string_func(p_store_string_ud, "](");
+ }
+
if (recursion_count > MAX_RECURSION) {
ERR_PRINT("Max recursion reached");
p_store_string_func(p_store_string_ud, "[]");
@@ -1850,7 +1964,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
recursion_count++;
p_store_string_func(p_store_string_ud, "[");
- Array array = p_variant;
int len = array.size();
for (int i = 0; i < len; i++) {
if (i > 0) {
@@ -1862,11 +1975,14 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, "]");
}
+ if (array.get_typed_builtin() != Variant::NIL) {
+ p_store_string_func(p_store_string_ud, ")");
+ }
+
} break;
case Variant::PACKED_BYTE_ARRAY: {
p_store_string_func(p_store_string_ud, "PackedByteArray(");
- String s;
Vector<uint8_t> data = p_variant;
int len = data.size();
const uint8_t *ptr = data.ptr();
@@ -1954,15 +2070,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
int len = data.size();
const String *ptr = data.ptr();
- String s;
- //write_string("\n");
-
for (int i = 0; i < len; i++) {
if (i > 0) {
p_store_string_func(p_store_string_ud, ", ");
}
- String str = ptr[i];
- p_store_string_func(p_store_string_ud, "\"" + str.c_escape() + "\"");
+ p_store_string_func(p_store_string_ud, "\"" + ptr[i].c_escape() + "\"");
}
p_store_string_func(p_store_string_ud, ")");
@@ -2010,9 +2122,9 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
if (i > 0) {
p_store_string_func(p_store_string_ud, ", ");
}
-
p_store_string_func(p_store_string_ud, rtos_fix(ptr[i].r) + ", " + rtos_fix(ptr[i].g) + ", " + rtos_fix(ptr[i].b) + ", " + rtos_fix(ptr[i].a));
}
+
p_store_string_func(p_store_string_ud, ")");
} break;