summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/bind/core_bind.cpp34
-rw-r--r--core/bind/core_bind.h7
-rw-r--r--core/dictionary.cpp26
-rw-r--r--core/dictionary.h2
-rw-r--r--core/engine.cpp11
-rw-r--r--core/engine.h4
-rw-r--r--core/io/http_client.cpp64
-rw-r--r--core/io/http_client.h1
-rw-r--r--core/io/marshalls.cpp4
-rw-r--r--core/io/multiplayer_api.cpp109
-rw-r--r--core/io/multiplayer_api.h43
-rw-r--r--core/io/packet_peer.cpp2
-rw-r--r--core/math/geometry.h15
-rw-r--r--core/math/math_2d.h9
-rw-r--r--core/math/matrix3.cpp13
-rw-r--r--core/math/matrix3.h2
-rw-r--r--core/math/quat.cpp16
-rw-r--r--core/math/quat.h24
-rw-r--r--core/math/vector3.h10
-rw-r--r--core/method_bind.h28
-rw-r--r--core/object.cpp4
-rw-r--r--core/object.h7
-rw-r--r--core/os/os.cpp1
-rw-r--r--core/os/os.h14
-rw-r--r--core/pool_allocator.cpp2
-rw-r--r--core/project_settings.cpp1
-rw-r--r--core/script_debugger_local.cpp207
-rw-r--r--core/script_debugger_local.h6
-rw-r--r--core/script_debugger_remote.cpp4
-rw-r--r--core/script_language.cpp2
-rw-r--r--core/script_language.h5
-rw-r--r--core/ustring.cpp44
-rw-r--r--core/ustring.h1
-rw-r--r--core/variant_call.cpp20
34 files changed, 629 insertions, 113 deletions
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index a51f2397c6..3270b33f1c 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -336,6 +336,14 @@ void _OS::set_borderless_window(bool p_borderless) {
OS::get_singleton()->set_borderless_window(p_borderless);
}
+bool _OS::get_window_per_pixel_transparency_enabled() const {
+ return OS::get_singleton()->get_window_per_pixel_transparency_enabled();
+}
+
+void _OS::set_window_per_pixel_transparency_enabled(bool p_enabled) {
+ OS::get_singleton()->set_window_per_pixel_transparency_enabled(p_enabled);
+}
+
bool _OS::get_borderless_window() const {
return OS::get_singleton()->get_borderless_window();
}
@@ -1068,6 +1076,9 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_borderless_window", "borderless"), &_OS::set_borderless_window);
ClassDB::bind_method(D_METHOD("get_borderless_window"), &_OS::get_borderless_window);
+ ClassDB::bind_method(D_METHOD("get_window_per_pixel_transparency_enabled"), &_OS::get_window_per_pixel_transparency_enabled);
+ ClassDB::bind_method(D_METHOD("set_window_per_pixel_transparency_enabled", "enabled"), &_OS::set_window_per_pixel_transparency_enabled);
+
ClassDB::bind_method(D_METHOD("set_ime_position", "position"), &_OS::set_ime_position);
ClassDB::bind_method(D_METHOD("set_screen_orientation", "orientation"), &_OS::set_screen_orientation);
@@ -1185,6 +1196,7 @@ void _OS::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait,Reverse Landscape,Reverse Portrait,Sensor Landscape,Sensor Portrait,Sensor"), "set_screen_orientation", "get_screen_orientation");
ADD_GROUP("Window", "window_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_borderless"), "set_borderless_window", "get_borderless_window");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_per_pixel_transparency_enabled"), "set_window_per_pixel_transparency_enabled", "get_window_per_pixel_transparency_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_fullscreen"), "set_window_fullscreen", "is_window_fullscreen");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_maximized"), "set_window_maximized", "is_window_maximized");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_minimized"), "set_window_minimized", "is_window_minimized");
@@ -1281,6 +1293,16 @@ Variant _Geometry::segment_intersects_segment_2d(const Vector2 &p_from_a, const
};
};
+Variant _Geometry::line_intersects_line_2d(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b) {
+
+ Vector2 result;
+ if (Geometry::line_intersects_line_2d(p_from_a, p_dir_a, p_from_b, p_dir_b, result)) {
+ return result;
+ } else {
+ return Variant();
+ }
+}
+
PoolVector<Vector2> _Geometry::get_closest_points_between_segments_2d(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2) {
Vector2 r1, r2;
@@ -1436,6 +1458,7 @@ void _Geometry::_bind_methods() {
ClassDB::bind_method(D_METHOD("build_capsule_planes", "radius", "height", "sides", "lats", "axis"), &_Geometry::build_capsule_planes, DEFVAL(Vector3::AXIS_Z));
ClassDB::bind_method(D_METHOD("segment_intersects_circle", "segment_from", "segment_to", "circle_position", "circle_radius"), &_Geometry::segment_intersects_circle);
ClassDB::bind_method(D_METHOD("segment_intersects_segment_2d", "from_a", "to_a", "from_b", "to_b"), &_Geometry::segment_intersects_segment_2d);
+ ClassDB::bind_method(D_METHOD("line_intersects_line_2d", "from_a", "dir_a", "from_b", "dir_b"), &_Geometry::line_intersects_line_2d);
ClassDB::bind_method(D_METHOD("get_closest_points_between_segments_2d", "p1", "q1", "p2", "q2"), &_Geometry::get_closest_points_between_segments_2d);
ClassDB::bind_method(D_METHOD("get_closest_points_between_segments", "p1", "p2", "q1", "q2"), &_Geometry::get_closest_points_between_segments);
@@ -2641,6 +2664,14 @@ int _Engine::get_iterations_per_second() const {
return Engine::get_singleton()->get_iterations_per_second();
}
+void _Engine::set_physics_jitter_fix(float p_threshold) {
+ Engine::get_singleton()->set_physics_jitter_fix(p_threshold);
+}
+
+float _Engine::get_physics_jitter_fix() const {
+ return Engine::get_singleton()->get_physics_jitter_fix();
+}
+
void _Engine::set_target_fps(int p_fps) {
Engine::get_singleton()->set_target_fps(p_fps);
}
@@ -2707,6 +2738,8 @@ void _Engine::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_iterations_per_second", "iterations_per_second"), &_Engine::set_iterations_per_second);
ClassDB::bind_method(D_METHOD("get_iterations_per_second"), &_Engine::get_iterations_per_second);
+ ClassDB::bind_method(D_METHOD("set_physics_jitter_fix", "physics_jitter_fix"), &_Engine::set_physics_jitter_fix);
+ ClassDB::bind_method(D_METHOD("get_physics_jitter_fix"), &_Engine::get_physics_jitter_fix);
ClassDB::bind_method(D_METHOD("set_target_fps", "target_fps"), &_Engine::set_target_fps);
ClassDB::bind_method(D_METHOD("get_target_fps"), &_Engine::get_target_fps);
@@ -2732,6 +2765,7 @@ void _Engine::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "iterations_per_second"), "set_iterations_per_second", "get_iterations_per_second");
ADD_PROPERTY(PropertyInfo(Variant::INT, "target_fps"), "set_target_fps", "get_target_fps");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_scale"), "set_time_scale", "get_time_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "physics_jitter_fix"), "set_physics_jitter_fix", "get_physics_jitter_fix");
}
_Engine *_Engine::singleton = NULL;
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index 1790c68757..a363f5970f 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -180,6 +180,9 @@ public:
virtual void set_borderless_window(bool p_borderless);
virtual bool get_borderless_window() const;
+ virtual bool get_window_per_pixel_transparency_enabled() const;
+ virtual void set_window_per_pixel_transparency_enabled(bool p_enabled);
+
virtual void set_ime_position(const Point2 &p_pos);
Error native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track);
@@ -358,6 +361,7 @@ public:
PoolVector<Plane> build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis = Vector3::AXIS_Z);
PoolVector<Plane> build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z);
Variant segment_intersects_segment_2d(const Vector2 &p_from_a, const Vector2 &p_to_a, const Vector2 &p_from_b, const Vector2 &p_to_b);
+ Variant line_intersects_line_2d(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b);
PoolVector<Vector2> get_closest_points_between_segments_2d(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2);
PoolVector<Vector3> get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2);
Vector2 get_closest_point_to_segment_2d(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b);
@@ -669,6 +673,9 @@ public:
void set_iterations_per_second(int p_ips);
int get_iterations_per_second() const;
+ void set_physics_jitter_fix(float p_threshold);
+ float get_physics_jitter_fix() const;
+
void set_target_fps(int p_fps);
int get_target_fps() const;
diff --git a/core/dictionary.cpp b/core/dictionary.cpp
index ba0de95861..d68411a572 100644
--- a/core/dictionary.cpp
+++ b/core/dictionary.cpp
@@ -50,6 +50,32 @@ void Dictionary::get_key_list(List<Variant> *p_keys) const {
}
}
+Variant Dictionary::get_key_at_index(int p_index) const {
+
+ int index = 0;
+ for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) {
+ if (index == p_index) {
+ return E.key();
+ }
+ index++;
+ }
+
+ return Variant();
+}
+
+Variant Dictionary::get_value_at_index(int p_index) const {
+
+ int index = 0;
+ for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) {
+ if (index == p_index) {
+ return E.value();
+ }
+ index++;
+ }
+
+ return Variant();
+}
+
Variant &Dictionary::operator[](const Variant &p_key) {
return _p->variant_map[p_key];
diff --git a/core/dictionary.h b/core/dictionary.h
index 9eef265d5b..84a5cafe1d 100644
--- a/core/dictionary.h
+++ b/core/dictionary.h
@@ -47,6 +47,8 @@ class Dictionary {
public:
void get_key_list(List<Variant> *p_keys) const;
+ Variant get_key_at_index(int p_index) const;
+ Variant get_value_at_index(int p_index) const;
Variant &operator[](const Variant &p_key);
const Variant &operator[](const Variant &p_key) const;
diff --git a/core/engine.cpp b/core/engine.cpp
index af9052110f..b2c34a853c 100644
--- a/core/engine.cpp
+++ b/core/engine.cpp
@@ -42,6 +42,16 @@ int Engine::get_iterations_per_second() const {
return ips;
}
+void Engine::set_physics_jitter_fix(float p_threshold) {
+ if (p_threshold < 0)
+ p_threshold = 0;
+ physics_jitter_fix = p_threshold;
+}
+
+float Engine::get_physics_jitter_fix() const {
+ return physics_jitter_fix;
+}
+
void Engine::set_target_fps(int p_fps) {
_target_fps = p_fps > 0 ? p_fps : 0;
}
@@ -137,6 +147,7 @@ Engine::Engine() {
singleton = this;
frames_drawn = 0;
ips = 60;
+ physics_jitter_fix = 0.5;
_frame_delay = 0;
_fps = 1;
_target_fps = 0;
diff --git a/core/engine.h b/core/engine.h
index 54b30ab81f..665992699a 100644
--- a/core/engine.h
+++ b/core/engine.h
@@ -57,6 +57,7 @@ private:
float _frame_step;
int ips;
+ float physics_jitter_fix;
float _fps;
int _target_fps;
float _time_scale;
@@ -79,6 +80,9 @@ public:
virtual void set_iterations_per_second(int p_ips);
virtual int get_iterations_per_second() const;
+ void set_physics_jitter_fix(float p_threshold);
+ float get_physics_jitter_fix() const;
+
virtual void set_target_fps(int p_fps);
virtual float get_target_fps() const;
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 9e301ccac5..8d85e78226 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -248,6 +248,7 @@ void HTTPClient::close() {
body_size = 0;
body_left = 0;
chunk_left = 0;
+ read_until_eof = false;
response_num = 0;
}
@@ -352,10 +353,17 @@ Error HTTPClient::poll() {
chunked = false;
body_left = 0;
chunk_left = 0;
+ read_until_eof = false;
response_str.clear();
response_headers.clear();
response_num = RESPONSE_OK;
+ // Per the HTTP 1.1 spec, keep-alive is the default, but in practice
+ // it's safe to assume it only if the explicit header is found, allowing
+ // to handle body-up-to-EOF responses on naive servers; that's what Curl
+ // and browsers do
+ bool keep_alive = false;
+
for (int i = 0; i < responses.size(); i++) {
String header = responses[i].strip_edges();
@@ -365,13 +373,14 @@ Error HTTPClient::poll() {
if (s.begins_with("content-length:")) {
body_size = s.substr(s.find(":") + 1, s.length()).strip_edges().to_int();
body_left = body_size;
- }
- if (s.begins_with("transfer-encoding:")) {
+ } else if (s.begins_with("transfer-encoding:")) {
String encoding = header.substr(header.find(":") + 1, header.length()).strip_edges();
if (encoding == "chunked") {
chunked = true;
}
+ } else if (s.begins_with("connection: keep-alive")) {
+ keep_alive = true;
}
if (i == 0 && responses[i].begins_with("HTTP")) {
@@ -384,11 +393,16 @@ Error HTTPClient::poll() {
}
}
- if (body_size == 0 && !chunked) {
+ if (body_size || chunked) {
- status = STATUS_CONNECTED; // Ready for new requests
- } else {
status = STATUS_BODY;
+ } else if (!keep_alive) {
+
+ read_until_eof = true;
+ status = STATUS_BODY;
+ } else {
+
+ status = STATUS_CONNECTED;
}
return OK;
}
@@ -515,34 +529,53 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
} else {
- int to_read = MIN(body_left, read_chunk_size);
+ int to_read = !read_until_eof ? MIN(body_left, read_chunk_size) : read_chunk_size;
PoolByteArray ret;
ret.resize(to_read);
int _offset = 0;
- while (to_read > 0) {
+ while (read_until_eof || to_read > 0) {
int rec = 0;
{
PoolByteArray::Write w = ret.write();
err = _get_http_data(w.ptr() + _offset, to_read, rec);
}
- if (rec > 0) {
- body_left -= rec;
- to_read -= rec;
- _offset += rec;
- } else {
+ if (rec < 0) {
if (to_read > 0) // Ended up reading less
ret.resize(_offset);
break;
+ } else {
+ _offset += rec;
+ if (!read_until_eof) {
+ body_left -= rec;
+ to_read -= rec;
+ } else {
+ if (rec < to_read) {
+ ret.resize(_offset);
+ err = ERR_FILE_EOF;
+ break;
+ }
+ ret.resize(_offset + to_read);
+ }
}
}
- if (body_left == 0) {
- status = STATUS_CONNECTED;
+ if (!read_until_eof) {
+ if (body_left == 0) {
+ status = STATUS_CONNECTED;
+ }
+ return ret;
+ } else {
+ if (err == ERR_FILE_EOF) {
+ err = OK; // EOF is expected here
+ close();
+ return ret;
+ }
}
- return ret;
}
if (err != OK) {
+
close();
+
if (err == ERR_FILE_EOF) {
status = STATUS_DISCONNECTED; // Server disconnected
@@ -602,6 +635,7 @@ HTTPClient::HTTPClient() {
body_size = 0;
chunked = false;
body_left = 0;
+ read_until_eof = false;
chunk_left = 0;
response_num = 0;
ssl = false;
diff --git a/core/io/http_client.h b/core/io/http_client.h
index 839012e701..38ec82ce8c 100644
--- a/core/io/http_client.h
+++ b/core/io/http_client.h
@@ -173,6 +173,7 @@ private:
int chunk_left;
int body_size;
int body_left;
+ bool read_until_eof;
Ref<StreamPeerTCP> tcp_connection;
Ref<StreamPeer> connection;
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index 2ebe8d6df7..0a3a6c1ba1 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -1211,7 +1211,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
r_len += len;
if (buf)
buf += len;
- encode_variant(d[E->get()], buf, len, p_object_as_id);
+ Variant *v = d.getptr(E->get());
+ ERR_FAIL_COND_V(!v, ERR_BUG);
+ encode_variant(*v, buf, len, p_object_as_id);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
r_len += len;
if (buf)
diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp
index cbe7f87d92..b0f2ca754d 100644
--- a/core/io/multiplayer_api.cpp
+++ b/core/io/multiplayer_api.cpp
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* multiplayer_api.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 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/io/multiplayer_api.h"
#include "core/io/marshalls.h"
#include "scene/main/node.h"
@@ -47,11 +77,11 @@ void MultiplayerAPI::set_root_node(Node *p_node) {
void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer) {
if (network_peer.is_valid()) {
- network_peer->disconnect("peer_connected", this, "add_peer");
- network_peer->disconnect("peer_disconnected", this, "del_peer");
- network_peer->disconnect("connection_succeeded", this, "connected_to_server");
- network_peer->disconnect("connection_failed", this, "connection_failed");
- network_peer->disconnect("server_disconnected", this, "server_disconnected");
+ network_peer->disconnect("peer_connected", this, "_add_peer");
+ network_peer->disconnect("peer_disconnected", this, "_del_peer");
+ network_peer->disconnect("connection_succeeded", this, "_connected_to_server");
+ network_peer->disconnect("connection_failed", this, "_connection_failed");
+ network_peer->disconnect("server_disconnected", this, "_server_disconnected");
clear();
}
@@ -61,11 +91,11 @@ void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_pee
ERR_FAIL_COND(p_peer.is_valid() && p_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED);
if (network_peer.is_valid()) {
- network_peer->connect("peer_connected", this, "add_peer");
- network_peer->connect("peer_disconnected", this, "del_peer");
- network_peer->connect("connection_succeeded", this, "connected_to_server");
- network_peer->connect("connection_failed", this, "connection_failed");
- network_peer->connect("server_disconnected", this, "server_disconnected");
+ network_peer->connect("peer_connected", this, "_add_peer");
+ network_peer->connect("peer_disconnected", this, "_del_peer");
+ network_peer->connect("connection_succeeded", this, "_connected_to_server");
+ network_peer->connect("connection_failed", this, "_connection_failed");
+ network_peer->connect("server_disconnected", this, "_server_disconnected");
}
}
@@ -76,7 +106,7 @@ Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const {
void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) {
ERR_FAIL_COND(root_node == NULL);
- ERR_FAIL_COND(p_packet_len < 5);
+ ERR_FAIL_COND(p_packet_len < 1);
uint8_t packet_type = p_packet[0];
@@ -123,6 +153,11 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
}
} break;
+
+ case NETWORK_COMMAND_RAW: {
+
+ _process_raw(p_from, p_packet, p_packet_len);
+ } break;
}
}
@@ -259,6 +294,8 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet,
void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
+ ERR_FAIL_COND(p_packet_len < 2);
+
String paths;
paths.parse_utf8((const char *)&p_packet[1], p_packet_len - 1);
@@ -458,29 +495,29 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
}
}
-void MultiplayerAPI::add_peer(int p_id) {
+void MultiplayerAPI::_add_peer(int p_id) {
connected_peers.insert(p_id);
path_get_cache.insert(p_id, PathGetCache());
emit_signal("network_peer_connected", p_id);
}
-void MultiplayerAPI::del_peer(int p_id) {
+void MultiplayerAPI::_del_peer(int p_id) {
connected_peers.erase(p_id);
path_get_cache.erase(p_id); //I no longer need your cache, sorry
emit_signal("network_peer_disconnected", p_id);
}
-void MultiplayerAPI::connected_to_server() {
+void MultiplayerAPI::_connected_to_server() {
emit_signal("connected_to_server");
}
-void MultiplayerAPI::connection_failed() {
+void MultiplayerAPI::_connection_failed() {
emit_signal("connection_failed");
}
-void MultiplayerAPI::server_disconnected() {
+void MultiplayerAPI::_server_disconnected() {
emit_signal("server_disconnected");
}
@@ -648,6 +685,34 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
_send_rpc(p_node, p_peer_id, p_unreliable, true, p_property, &vptr, 1);
}
+Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to) {
+
+ ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(!network_peer.is_valid(), ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED);
+
+ MAKE_ROOM(p_data.size() + 1);
+ PoolVector<uint8_t>::Read r = p_data.read();
+ packet_cache[0] = NETWORK_COMMAND_RAW;
+ memcpy(&packet_cache[1], &r[0], p_data.size());
+ network_peer->set_target_peer(p_to);
+ return network_peer->put_packet(packet_cache.ptr(), p_data.size() + 1);
+}
+
+void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) {
+
+ ERR_FAIL_COND(p_packet_len < 2);
+
+ PoolVector<uint8_t> out;
+ int len = p_packet_len - 1;
+ out.resize(len);
+ {
+ PoolVector<uint8_t>::Write w = out.write();
+ memcpy(&w[0], &p_packet[1], len);
+ }
+ emit_signal("network_peer_packet", p_from, out);
+}
+
int MultiplayerAPI::get_network_unique_id() const {
ERR_FAIL_COND_V(!network_peer.is_valid(), 0);
@@ -686,20 +751,21 @@ Vector<int> MultiplayerAPI::get_network_connected_peers() const {
void MultiplayerAPI::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node);
+ ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST));
ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer);
ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer);
ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id);
ClassDB::bind_method(D_METHOD("is_network_server"), &MultiplayerAPI::is_network_server);
ClassDB::bind_method(D_METHOD("get_rpc_sender_id"), &MultiplayerAPI::get_rpc_sender_id);
- ClassDB::bind_method(D_METHOD("add_peer", "id"), &MultiplayerAPI::add_peer);
- ClassDB::bind_method(D_METHOD("del_peer", "id"), &MultiplayerAPI::del_peer);
+ ClassDB::bind_method(D_METHOD("_add_peer", "id"), &MultiplayerAPI::_add_peer);
+ ClassDB::bind_method(D_METHOD("_del_peer", "id"), &MultiplayerAPI::_del_peer);
ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &MultiplayerAPI::set_network_peer);
ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll);
ClassDB::bind_method(D_METHOD("clear"), &MultiplayerAPI::clear);
- ClassDB::bind_method(D_METHOD("connected_to_server"), &MultiplayerAPI::connected_to_server);
- ClassDB::bind_method(D_METHOD("connection_failed"), &MultiplayerAPI::connection_failed);
- ClassDB::bind_method(D_METHOD("server_disconnected"), &MultiplayerAPI::server_disconnected);
+ ClassDB::bind_method(D_METHOD("_connected_to_server"), &MultiplayerAPI::_connected_to_server);
+ ClassDB::bind_method(D_METHOD("_connection_failed"), &MultiplayerAPI::_connection_failed);
+ ClassDB::bind_method(D_METHOD("_server_disconnected"), &MultiplayerAPI::_server_disconnected);
ClassDB::bind_method(D_METHOD("get_network_connected_peers"), &MultiplayerAPI::get_network_connected_peers);
ClassDB::bind_method(D_METHOD("set_refuse_new_network_connections", "refuse"), &MultiplayerAPI::set_refuse_new_network_connections);
ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &MultiplayerAPI::is_refusing_new_network_connections);
@@ -708,6 +774,7 @@ void MultiplayerAPI::_bind_methods() {
ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("network_peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::POOL_BYTE_ARRAY, "packet")));
ADD_SIGNAL(MethodInfo("connected_to_server"));
ADD_SIGNAL(MethodInfo("connection_failed"));
ADD_SIGNAL(MethodInfo("server_disconnected"));
diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h
index e7c6ffbea6..64f59d32d8 100644
--- a/core/io/multiplayer_api.h
+++ b/core/io/multiplayer_api.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* multiplayer_api.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 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_PROTOCOL_H
#define MULTIPLAYER_PROTOCOL_H
@@ -43,6 +73,7 @@ protected:
Node *_process_get_node(int p_from, const uint8_t *p_packet, int p_packet_len);
void _process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
void _process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
+ void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len);
void _send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount);
bool _send_confirm_path(NodePath p_path, PathSentCache *psc, int p_from);
@@ -53,6 +84,7 @@ public:
NETWORK_COMMAND_REMOTE_SET,
NETWORK_COMMAND_SIMPLIFY_PATH,
NETWORK_COMMAND_CONFIRM_PATH,
+ NETWORK_COMMAND_RAW,
};
void poll();
@@ -60,17 +92,18 @@ public:
void set_root_node(Node *p_node);
void set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer);
Ref<NetworkedMultiplayerPeer> get_network_peer() const;
+ Error send_bytes(PoolVector<uint8_t> p_data, int p_to = NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST);
// Called by Node.rpc
void rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount);
// Called by Node.rset
void rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value);
- void add_peer(int p_id);
- void del_peer(int p_id);
- void connected_to_server();
- void connection_failed();
- void server_disconnected();
+ void _add_peer(int p_id);
+ void _del_peer(int p_id);
+ void _connected_to_server();
+ void _connection_failed();
+ void _server_disconnected();
bool has_network_peer() const { return network_peer.is_valid(); }
Vector<int> get_network_connected_peers() const;
diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp
index bd851ebb6d..b777a9f960 100644
--- a/core/io/packet_peer.cpp
+++ b/core/io/packet_peer.cpp
@@ -172,6 +172,7 @@ Error PacketPeerStream::_poll_buffer() const {
ERR_FAIL_COND_V(peer.is_null(), ERR_UNCONFIGURED);
int read = 0;
+ ERR_FAIL_COND_V(input_buffer.size() < ring_buffer.space_left(), ERR_UNAVAILABLE);
Error err = peer->get_partial_data(&input_buffer[0], ring_buffer.space_left(), read);
if (err)
return err;
@@ -223,6 +224,7 @@ Error PacketPeerStream::get_packet(const uint8_t **r_buffer, int &r_buffer_size)
uint32_t len = decode_uint32(lbuf);
ERR_FAIL_COND_V(remaining < (int)len, ERR_UNAVAILABLE);
+ ERR_FAIL_COND_V(input_buffer.size() < len, ERR_UNAVAILABLE);
ring_buffer.read(lbuf, 4); //get rid of first 4 bytes
ring_buffer.read(&input_buffer[0], len); // read packet
diff --git a/core/math/geometry.h b/core/math/geometry.h
index 73a53c53b6..be998aef0b 100644
--- a/core/math/geometry.h
+++ b/core/math/geometry.h
@@ -529,6 +529,21 @@ public:
return p_segment[0] + n * d; // inside
}
+ static bool line_intersects_line_2d(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b, Vector2 &r_result) {
+
+ // see http://paulbourke.net/geometry/pointlineplane/
+
+ const real_t denom = p_dir_b.y * p_dir_a.x - p_dir_b.x * p_dir_a.y;
+ if (Math::abs(denom) < CMP_EPSILON) { // parallel?
+ return false;
+ }
+
+ const Vector2 v = p_from_a - p_from_b;
+ const real_t t = (p_dir_b.x * v.y - p_dir_b.y * v.x) / denom;
+ r_result = p_from_a + t * p_dir_a;
+ return true;
+ }
+
static bool segment_intersects_segment_2d(const Vector2 &p_from_a, const Vector2 &p_to_a, const Vector2 &p_from_b, const Vector2 &p_to_b, Vector2 *r_result) {
Vector2 B = p_to_a - p_from_a;
diff --git a/core/math/math_2d.h b/core/math/math_2d.h
index 611d47e3ff..25c39e5d7a 100644
--- a/core/math/math_2d.h
+++ b/core/math/math_2d.h
@@ -112,6 +112,7 @@ struct Vector2 {
_FORCE_INLINE_ static Vector2 linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_t);
_FORCE_INLINE_ Vector2 linear_interpolate(const Vector2 &p_b, real_t p_t) const;
+ _FORCE_INLINE_ Vector2 slerp(const Vector2 &p_b, real_t p_t) const;
Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_t) const;
Vector2 slide(const Vector2 &p_normal) const;
@@ -263,6 +264,14 @@ Vector2 Vector2::linear_interpolate(const Vector2 &p_b, real_t p_t) const {
return res;
}
+Vector2 Vector2::slerp(const Vector2 &p_b, real_t p_t) const {
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND_V(is_normalized() == false, Vector2());
+#endif
+ real_t theta = angle_to(p_b);
+ return rotated(theta * p_t);
+}
+
Vector2 Vector2::linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_t) {
Vector2 res = p_a;
diff --git a/core/math/matrix3.cpp b/core/math/matrix3.cpp
index b0b05d1ec8..8ee8ccb457 100644
--- a/core/math/matrix3.cpp
+++ b/core/math/matrix3.cpp
@@ -826,3 +826,16 @@ void Basis::set_diagonal(const Vector3 p_diag) {
elements[2][1] = 0;
elements[2][2] = p_diag.z;
}
+
+Basis Basis::slerp(const Basis &target, const real_t &t) const {
+ // TODO: implement this directly without using quaternions to make it more efficient
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND_V(is_rotation() == false, Basis());
+ ERR_FAIL_COND_V(target.is_rotation() == false, Basis());
+#endif
+
+ Quat from(*this);
+ Quat to(target);
+
+ return Basis(from.slerp(to, t));
+}
diff --git a/core/math/matrix3.h b/core/math/matrix3.h
index fd383fc673..63d4f5d79d 100644
--- a/core/math/matrix3.h
+++ b/core/math/matrix3.h
@@ -155,6 +155,8 @@ public:
bool is_diagonal() const;
bool is_rotation() const;
+ Basis slerp(const Basis &target, const real_t &t) const;
+
operator String() const;
/* create / set */
diff --git a/core/math/quat.cpp b/core/math/quat.cpp
index 4f61401ac7..b938fc3cfd 100644
--- a/core/math/quat.cpp
+++ b/core/math/quat.cpp
@@ -98,6 +98,9 @@ void Quat::set_euler_yxz(const Vector3 &p_euler) {
// and similar for other axes.
// This implementation uses YXZ convention (Z is the first rotation).
Vector3 Quat::get_euler_yxz() const {
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND_V(is_normalized() == false, Vector3(0, 0, 0));
+#endif
Basis m(*this);
return m.get_euler_yxz();
}
@@ -135,11 +138,17 @@ bool Quat::is_normalized() const {
}
Quat Quat::inverse() const {
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND_V(is_normalized() == false, Quat(0, 0, 0, 0));
+#endif
return Quat(-x, -y, -z, w);
}
Quat Quat::slerp(const Quat &q, const real_t &t) const {
-
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND_V(is_normalized() == false, Quat(0, 0, 0, 0));
+ ERR_FAIL_COND_V(q.is_normalized() == false, Quat(0, 0, 0, 0));
+#endif
Quat to1;
real_t omega, cosom, sinom, scale0, scale1;
@@ -215,7 +224,10 @@ Quat::operator String() const {
return String::num(x) + ", " + String::num(y) + ", " + String::num(z) + ", " + String::num(w);
}
-Quat::Quat(const Vector3 &axis, const real_t &angle) {
+void Quat::set_axis_angle(const Vector3 &axis, const real_t &angle) {
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND(axis.is_normalized() == false);
+#endif
real_t d = axis.length();
if (d == 0)
set(0, 0, 0, 0);
diff --git a/core/math/quat.h b/core/math/quat.h
index ebc924504b..3e1344a913 100644
--- a/core/math/quat.h
+++ b/core/math/quat.h
@@ -64,11 +64,13 @@ public:
Quat slerpni(const Quat &q, const real_t &t) const;
Quat cubic_slerp(const Quat &q, const Quat &prep, const Quat &postq, const real_t &t) const;
+ void set_axis_angle(const Vector3 &axis, const real_t &angle);
_FORCE_INLINE_ void get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
r_angle = 2 * Math::acos(w);
- r_axis.x = x / Math::sqrt(1 - w * w);
- r_axis.y = y / Math::sqrt(1 - w * w);
- r_axis.z = z / Math::sqrt(1 - w * w);
+ real_t r = ((real_t)1) / Math::sqrt(1 - w * w);
+ r_axis.x = x * r;
+ r_axis.y = y * r;
+ r_axis.z = z * r;
}
void operator*=(const Quat &q);
@@ -83,9 +85,9 @@ public:
_FORCE_INLINE_ Vector3 xform(const Vector3 &v) const {
- Quat q = *this * v;
- q *= this->inverse();
- return Vector3(q.x, q.y, q.z);
+ Vector3 u(x, y, z);
+ Vector3 uv = u.cross(v);
+ return v + ((uv * w) + u.cross(uv)) * ((real_t)2);
}
_FORCE_INLINE_ void operator+=(const Quat &q);
@@ -115,7 +117,15 @@ public:
z = p_z;
w = p_w;
}
- Quat(const Vector3 &axis, const real_t &angle);
+ Quat(const Vector3 &axis, const real_t &angle) { set_axis_angle(axis, angle); }
+
+ Quat(const Vector3 &euler) { set_euler(euler); }
+ Quat(const Quat &q) {
+ x = q.x;
+ y = q.y;
+ z = q.z;
+ w = q.w;
+ }
Quat(const Vector3 &v0, const Vector3 &v1) // shortest arc
{
diff --git a/core/math/vector3.h b/core/math/vector3.h
index 3bbfd7627c..433adf09ee 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -91,6 +91,7 @@ struct Vector3 {
/* Static Methods between 2 vector3s */
_FORCE_INLINE_ Vector3 linear_interpolate(const Vector3 &p_b, real_t p_t) const;
+ _FORCE_INLINE_ Vector3 slerp(const Vector3 &p_b, real_t p_t) const;
Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_t) const;
Vector3 cubic_interpolaten(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_t) const;
@@ -218,6 +219,15 @@ Vector3 Vector3::linear_interpolate(const Vector3 &p_b, real_t p_t) const {
z + (p_t * (p_b.z - z)));
}
+Vector3 Vector3::slerp(const Vector3 &p_b, real_t p_t) const {
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND_V(is_normalized() == false, Vector3());
+#endif
+
+ real_t theta = angle_to(p_b);
+ return rotated(cross(p_b), theta * p_t);
+}
+
real_t Vector3::distance_to(const Vector3 &p_b) const {
return (p_b - *this).length();
diff --git a/core/method_bind.h b/core/method_bind.h
index e02d64c935..41b500c401 100644
--- a/core/method_bind.h
+++ b/core/method_bind.h
@@ -128,10 +128,36 @@ struct VariantCaster<const T &> {
// Object enum casts must go here
VARIANT_ENUM_CAST(Object::ConnectFlags);
+template <typename T>
+struct VariantObjectClassChecker {
+ static _FORCE_INLINE_ bool check(const Variant &p_variant) {
+ return true;
+ }
+};
+
+template <>
+struct VariantObjectClassChecker<Node *> {
+ static _FORCE_INLINE_ bool check(const Variant &p_variant) {
+ Object *obj = p_variant;
+ Node *node = p_variant;
+ return node || !obj;
+ }
+};
+
+template <>
+struct VariantObjectClassChecker<Control *> {
+ static _FORCE_INLINE_ bool check(const Variant &p_variant) {
+ Object *obj = p_variant;
+ Control *control = p_variant;
+ return control || !obj;
+ }
+};
+
#define CHECK_ARG(m_arg) \
if ((m_arg - 1) < p_arg_count) { \
Variant::Type argtype = get_argument_type(m_arg - 1); \
- if (!Variant::can_convert_strict(p_args[m_arg - 1]->get_type(), argtype)) { \
+ if (!Variant::can_convert_strict(p_args[m_arg - 1]->get_type(), argtype) || \
+ !VariantObjectClassChecker<P##m_arg>::check(*p_args[m_arg - 1])) { \
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; \
r_error.argument = m_arg - 1; \
r_error.expected = argtype; \
diff --git a/core/object.cpp b/core/object.cpp
index aaa37e6cf2..239700a4ab 100644
--- a/core/object.cpp
+++ b/core/object.cpp
@@ -1919,9 +1919,7 @@ ObjectID ObjectDB::add_instance(Object *p_object) {
rw_lock->write_lock();
instances[++instance_counter] = p_object;
-#ifdef DEBUG_ENABLED
instance_checks[p_object] = instance_counter;
-#endif
rw_lock->write_unlock();
return instance_counter;
@@ -1932,9 +1930,7 @@ void ObjectDB::remove_instance(Object *p_object) {
rw_lock->write_lock();
instances.erase(p_object->get_instance_id());
-#ifdef DEBUG_ENABLED
instance_checks.erase(p_object);
-#endif
rw_lock->write_unlock();
}
diff --git a/core/object.h b/core/object.h
index 8306b5a356..7963a43fd6 100644
--- a/core/object.h
+++ b/core/object.h
@@ -55,7 +55,7 @@ enum PropertyHint {
PROPERTY_HINT_RANGE, ///< hint_text = "min,max,step,slider; //slider is optional"
PROPERTY_HINT_EXP_RANGE, ///< hint_text = "min,max,step", exponential edit
PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc"
- PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease)
+ PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "full" to also include in/out. (ie: "attenuation,inout")
PROPERTY_HINT_LENGTH, ///< hint_text= "length" (as integer)
PROPERTY_HINT_SPRITE_FRAME,
PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer)
@@ -762,15 +762,10 @@ public:
static void debug_objects(DebugFunc p_func);
static int get_object_count();
-#ifdef DEBUG_ENABLED
_FORCE_INLINE_ static bool instance_validate(Object *p_ptr) {
return instance_checks.has(p_ptr);
}
-#else
- _FORCE_INLINE_ static bool instance_validate(Object *p_ptr) { return true; }
-
-#endif
};
//needed by macros
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 854d554b10..5eed10e30c 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -672,6 +672,7 @@ OS::OS() {
_render_thread_mode = RENDER_THREAD_SAFE;
_allow_hidpi = false;
+ _allow_layered = false;
_stack_bottom = (void *)(&stack_bottom);
_logger = NULL;
diff --git a/core/os/os.h b/core/os/os.h
index 943c0498f1..b36f94060c 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -65,6 +65,7 @@ class OS {
int _exit_code;
int _orientation;
bool _allow_hidpi;
+ bool _allow_layered;
bool _use_vsync;
char *last_error;
@@ -102,6 +103,8 @@ public:
bool maximized;
bool always_on_top;
bool use_vsync;
+ bool layered_splash;
+ bool layered;
float get_aspect() const { return (float)width / (float)height; }
VideoMode(int p_width = 1024, int p_height = 600, bool p_fullscreen = false, bool p_resizable = true, bool p_borderless_window = false, bool p_maximized = false, bool p_always_on_top = false, bool p_use_vsync = false) {
width = p_width;
@@ -112,6 +115,8 @@ public:
maximized = p_maximized;
always_on_top = p_always_on_top;
use_vsync = p_use_vsync;
+ layered = false;
+ layered_splash = false;
}
};
@@ -220,6 +225,13 @@ public:
virtual void set_borderless_window(bool p_borderless) {}
virtual bool get_borderless_window() { return 0; }
+ virtual bool get_window_per_pixel_transparency_enabled() const { return false; }
+ virtual void set_window_per_pixel_transparency_enabled(bool p_enabled) {}
+
+ virtual uint8_t *get_layered_buffer_data() { return NULL; }
+ virtual Size2 get_layered_buffer_size() { return Size2(0, 0); }
+ virtual void swap_layered_buffer() {}
+
virtual void set_ime_position(const Point2 &p_pos) {}
virtual void set_ime_intermediate_text_callback(ImeCallback p_callback, void *p_inp) {}
@@ -320,6 +332,7 @@ public:
virtual void disable_crash_handler() {}
virtual bool is_disable_crash_handler() const { return false; }
+ virtual void initialize_debugging() {}
enum CursorShape {
CURSOR_ARROW,
@@ -480,6 +493,7 @@ public:
virtual void force_process_input(){};
bool has_feature(const String &p_feature);
+ bool is_layered_allowed() const { return _allow_layered; }
bool is_hidpi_allowed() const { return _allow_hidpi; }
OS();
virtual ~OS();
diff --git a/core/pool_allocator.cpp b/core/pool_allocator.cpp
index 017586b92a..8952314212 100644
--- a/core/pool_allocator.cpp
+++ b/core/pool_allocator.cpp
@@ -359,7 +359,7 @@ Error PoolAllocator::resize(ID p_mem, int p_new_size) {
//p_new_size = align(p_new_size)
int _free = free_mem; // - static_area_size;
- if ((_free + aligned(e->len)) - alloc_size < 0) {
+ if (uint32_t(_free + aligned(e->len)) < alloc_size) {
mt_unlock();
ERR_FAIL_V(ERR_OUT_OF_MEMORY);
};
diff --git a/core/project_settings.cpp b/core/project_settings.cpp
index ac4a4b7d15..ef485cb3b2 100644
--- a/core/project_settings.cpp
+++ b/core/project_settings.cpp
@@ -1071,6 +1071,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("rendering/quality/intended_usage/framebuffer_mode", 2);
GLOBAL_DEF("debug/settings/profiler/max_functions", 16384);
+ GLOBAL_DEF("debug/settings/performance/update_frequency_msec", 250);
//assigning here, because using GLOBAL_GET on every block for compressing can be slow
Compression::zstd_long_distance_matching = GLOBAL_DEF("compression/formats/zstd/long_distance_matching", false);
diff --git a/core/script_debugger_local.cpp b/core/script_debugger_local.cpp
index c0e115e300..55d7270473 100644
--- a/core/script_debugger_local.cpp
+++ b/core/script_debugger_local.cpp
@@ -29,12 +29,23 @@
/*************************************************************************/
#include "script_debugger_local.h"
+#include "scene/main/scene_tree.h"
#include "os/os.h"
void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue) {
- print_line("Debugger Break, Reason: '" + p_script->debug_get_error() + "'");
+ if (!target_function.empty()) {
+ String current_function = p_script->debug_get_stack_level_function(0);
+ if (current_function != target_function) {
+ set_depth(0);
+ set_lines_left(1);
+ return;
+ }
+ target_function = "";
+ }
+
+ print_line("\nDebugger Break, Reason: '" + p_script->debug_get_error() + "'");
print_line("*Frame " + itos(0) + " - " + p_script->debug_get_stack_level_source(0) + ":" + itos(p_script->debug_get_stack_level_line(0)) + " in function '" + p_script->debug_get_stack_level_function(0) + "'");
print_line("Enter \"help\" for assistance.");
int current_frame = 0;
@@ -44,8 +55,11 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue) {
OS::get_singleton()->print("debug> ");
String line = OS::get_singleton()->get_stdin_string().strip_edges();
+ // Cache options
+ String variable_prefix = options["variable_prefix"];
+
if (line == "") {
- print_line("Debugger Break, Reason: '" + p_script->debug_get_error() + "'");
+ print_line("\nDebugger Break, Reason: '" + p_script->debug_get_error() + "'");
print_line("*Frame " + itos(current_frame) + " - " + p_script->debug_get_stack_level_source(current_frame) + ":" + itos(p_script->debug_get_stack_level_line(current_frame)) + " in function '" + p_script->debug_get_stack_level_function(current_frame) + "'");
print_line("Enter \"help\" for assistance.");
} else if (line == "c" || line == "continue")
@@ -72,38 +86,56 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue) {
}
}
+ } else if (line.begins_with("set")) {
+
+ if (line.get_slice_count(" ") == 1) {
+
+ for (Map<String, String>::Element *E = options.front(); E; E = E->next()) {
+ print_line("\t" + E->key() + "=" + E->value());
+ }
+
+ } else {
+ String key_value = line.get_slicec(' ', 1);
+ int value_pos = key_value.find("=");
+
+ if (value_pos < 0) {
+ print_line("Error: Invalid set format. Use: set key=value");
+ } else {
+
+ String key = key_value.left(value_pos);
+
+ if (!options.has(key)) {
+ print_line("Error: Unknown option " + key);
+ } else {
+
+ // Allow explicit tab character
+ String value = key_value.right(value_pos + 1).replace("\\t", "\t");
+
+ options[key] = value;
+ }
+ }
+ }
+
} else if (line == "lv" || line == "locals") {
List<String> locals;
List<Variant> values;
p_script->debug_get_stack_level_locals(current_frame, &locals, &values);
- List<Variant>::Element *V = values.front();
- for (List<String>::Element *E = locals.front(); E; E = E->next()) {
- print_line(E->get() + ": " + String(V->get()));
- V = V->next();
- }
+ print_variables(locals, values, variable_prefix);
} else if (line == "gv" || line == "globals") {
- List<String> locals;
+ List<String> globals;
List<Variant> values;
- p_script->debug_get_globals(&locals, &values);
- List<Variant>::Element *V = values.front();
- for (List<String>::Element *E = locals.front(); E; E = E->next()) {
- print_line(E->get() + ": " + String(V->get()));
- V = V->next();
- }
+ p_script->debug_get_globals(&globals, &values);
+ print_variables(globals, values, variable_prefix);
} else if (line == "mv" || line == "members") {
- List<String> locals;
+ List<String> members;
List<Variant> values;
- p_script->debug_get_stack_level_members(current_frame, &locals, &values);
- List<Variant>::Element *V = values.front();
- for (List<String>::Element *E = locals.front(); E; E = E->next()) {
- print_line(E->get() + ": " + String(V->get()));
- V = V->next();
- }
+ p_script->debug_get_stack_level_members(current_frame, &members, &values);
+ print_variables(members, values, variable_prefix);
} else if (line.begins_with("p") || line.begins_with("print")) {
@@ -121,65 +153,149 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue) {
set_depth(-1);
set_lines_left(1);
break;
- } else if (line.begins_with("n") || line.begins_with("next")) {
+ } else if (line == "n" || line == "next") {
set_depth(0);
set_lines_left(1);
break;
+ } else if (line == "fin" || line == "finish") {
+
+ String current_function = p_script->debug_get_stack_level_function(0);
+
+ for (int i = 0; i < total_frames; i++) {
+ target_function = p_script->debug_get_stack_level_function(i);
+ if (target_function != current_function) {
+ set_depth(0);
+ set_lines_left(1);
+ return;
+ }
+ }
+
+ print_line("Error: Reached last frame.");
+ target_function = "";
+
} else if (line.begins_with("br") || line.begins_with("break")) {
if (line.get_slice_count(" ") <= 1) {
- //show breakpoints
+
+ const Map<int, Set<StringName> > &breakpoints = get_breakpoints();
+ if (breakpoints.size() == 0) {
+ print_line("No Breakpoints.");
+ continue;
+ }
+
+ print_line("Breakpoint(s): " + itos(breakpoints.size()));
+ for (Map<int, Set<StringName> >::Element *E = breakpoints.front(); E; E = E->next()) {
+ print_line("\t" + String(E->value().front()->get()) + ":" + itos(E->key()));
+ }
+
} else {
- String bppos = line.get_slicec(' ', 1);
- String source = bppos.get_slicec(':', 0).strip_edges();
- int line = bppos.get_slicec(':', 1).strip_edges().to_int();
+ Pair<String, int> breakpoint = to_breakpoint(line);
+
+ String source = breakpoint.first;
+ int linenr = breakpoint.second;
- source = breakpoint_find_source(source);
+ if (source.empty())
+ continue;
- insert_breakpoint(line, source);
+ insert_breakpoint(linenr, source);
- print_line("BreakPoint at " + source + ":" + itos(line));
+ print_line("Added breakpoint at " + source + ":" + itos(linenr));
}
+ } else if (line == "q" || line == "quit") {
+
+ // Do not stop again on quit
+ clear_breakpoints();
+ ScriptDebugger::get_singleton()->set_depth(-1);
+ ScriptDebugger::get_singleton()->set_lines_left(-1);
+
+ SceneTree::get_singleton()->quit();
+ break;
} else if (line.begins_with("delete")) {
if (line.get_slice_count(" ") <= 1) {
clear_breakpoints();
} else {
- String bppos = line.get_slicec(' ', 1);
- String source = bppos.get_slicec(':', 0).strip_edges();
- int line = bppos.get_slicec(':', 1).strip_edges().to_int();
+ Pair<String, int> breakpoint = to_breakpoint(line);
+
+ String source = breakpoint.first;
+ int linenr = breakpoint.second;
- source = breakpoint_find_source(source);
+ if (source.empty())
+ continue;
- remove_breakpoint(line, source);
+ remove_breakpoint(linenr, source);
- print_line("Removed BreakPoint at " + source + ":" + itos(line));
+ print_line("Removed breakpoint at " + source + ":" + itos(linenr));
}
} else if (line == "h" || line == "help") {
print_line("Built-In Debugger command list:\n");
- print_line("\tc,continue :\t\t Continue execution.");
- print_line("\tbt,backtrace :\t\t Show stack trace (frames).");
+ print_line("\tc,continue\t\t Continue execution.");
+ print_line("\tbt,backtrace\t\t Show stack trace (frames).");
print_line("\tfr,frame <frame>:\t Change current frame.");
- print_line("\tlv,locals :\t\t Show local variables for current frame.");
- print_line("\tmv,members :\t\t Show member variables for \"this\" in frame.");
- print_line("\tgv,globals :\t\t Show global variables.");
- print_line("\tp,print <expr> :\t Execute and print variable in expression.");
- print_line("\ts,step :\t\t Step to next line.");
- print_line("\tn,next :\t\t Next line.");
- print_line("\tbr,break source:line :\t Place a breakpoint.");
- print_line("\tdelete [source:line]:\t\t Delete one/all breakpoints.");
+ print_line("\tlv,locals\t\t Show local variables for current frame.");
+ print_line("\tmv,members\t\t Show member variables for \"this\" in frame.");
+ print_line("\tgv,globals\t\t Show global variables.");
+ print_line("\tp,print <expr>\t\t Execute and print variable in expression.");
+ print_line("\ts,step\t\t\t Step to next line.");
+ print_line("\tn,next\t\t\t Next line.");
+ print_line("\tfin,finish\t\t Step out of current frame.");
+ print_line("\tbr,break [source:line]\t List all breakpoints or place a breakpoint.");
+ print_line("\tdelete [source:line]:\t Delete one/all breakpoints.");
+ print_line("\tset [key=value]:\t List all options, or set one.");
+ print_line("\tq,quit\t\t\t Quit application.");
} else {
print_line("Error: Invalid command, enter \"help\" for assistance.");
}
}
}
+void ScriptDebuggerLocal::print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix) {
+
+ String value;
+ Vector<String> value_lines;
+ const List<Variant>::Element *V = values.front();
+ for (const List<String>::Element *E = names.front(); E; E = E->next()) {
+
+ value = String(V->get());
+
+ if (variable_prefix.empty()) {
+ print_line(E->get() + ": " + String(V->get()));
+ } else {
+
+ print_line(E->get() + ":");
+ value_lines = value.split("\n");
+ for (int i = 0; i < value_lines.size(); ++i) {
+ print_line(variable_prefix + value_lines[i]);
+ }
+ }
+
+ V = V->next();
+ }
+}
+
+Pair<String, int> ScriptDebuggerLocal::to_breakpoint(const String &p_line) {
+
+ String breakpoint_part = p_line.get_slicec(' ', 1);
+ Pair<String, int> breakpoint;
+
+ int last_colon = breakpoint_part.rfind(":");
+ if (last_colon < 0) {
+ print_line("Error: Invalid breakpoint format. Expected [source:line]");
+ return breakpoint;
+ }
+
+ breakpoint.first = breakpoint_find_source(breakpoint_part.left(last_colon).strip_edges());
+ breakpoint.second = breakpoint_part.right(last_colon).strip_edges().to_int();
+
+ return breakpoint;
+}
+
struct _ScriptDebuggerLocalProfileInfoSort {
bool operator()(const ScriptLanguage::ProfilingInfo &A, const ScriptLanguage::ProfilingInfo &B) const {
@@ -304,4 +420,5 @@ ScriptDebuggerLocal::ScriptDebuggerLocal() {
profiling = false;
idle_accum = OS::get_singleton()->get_ticks_usec();
+ options["variable_prefix"] = "";
}
diff --git a/core/script_debugger_local.h b/core/script_debugger_local.h
index c87bc90bb4..7eea6ef215 100644
--- a/core/script_debugger_local.h
+++ b/core/script_debugger_local.h
@@ -31,6 +31,7 @@
#ifndef SCRIPT_DEBUGGER_LOCAL_H
#define SCRIPT_DEBUGGER_LOCAL_H
+#include "list.h"
#include "script_language.h"
class ScriptDebuggerLocal : public ScriptDebugger {
@@ -38,9 +39,14 @@ class ScriptDebuggerLocal : public ScriptDebugger {
bool profiling;
float frame_time, idle_time, physics_time, physics_frame_time;
uint64_t idle_accum;
+ String target_function;
+ Map<String, String> options;
Vector<ScriptLanguage::ProfilingInfo> pinfo;
+ Pair<String, int> to_breakpoint(const String &p_line);
+ void print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix);
+
public:
void debug(ScriptLanguage *p_script, bool p_can_continue);
virtual void send_message(const String &p_message, const Array &p_args);
diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp
index 75bcedbbc8..7a30a33c67 100644
--- a/core/script_debugger_remote.cpp
+++ b/core/script_debugger_remote.cpp
@@ -854,7 +854,7 @@ void ScriptDebuggerRemote::idle_poll() {
if (performance) {
uint64_t pt = OS::get_singleton()->get_ticks_msec();
- if (pt - last_perf_time > 1000) {
+ if (pt - last_perf_time > update_frequency) {
last_perf_time = pt;
int max = performance->get("MONITOR_MAX");
@@ -1081,7 +1081,7 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() :
eh.userdata = this;
add_error_handler(&eh);
- profile_info.resize(CLAMP(int(ProjectSettings::get_singleton()->get("debug/settings/profiler/max_functions")), 128, 65535));
+ profile_info.resize(CLAMP(int(GLOBAL_GET("debug/settings/profiler/max_functions")), 128, 65535));
profile_info_ptrs.resize(profile_info.size());
}
diff --git a/core/script_language.cpp b/core/script_language.cpp
index 1dab58e29e..ce9b138bb2 100644
--- a/core/script_language.cpp
+++ b/core/script_language.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "script_language.h"
+#include "project_settings.h"
ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES];
int ScriptServer::_language_count = 0;
@@ -283,6 +284,7 @@ ScriptDebugger::ScriptDebugger() {
lines_left = -1;
depth = -1;
break_lang = NULL;
+ update_frequency = GLOBAL_GET("debug/settings/performance/update_frequency_msec");
}
bool PlaceHolderScriptInstance::set(const StringName &p_name, const Variant &p_value) {
diff --git a/core/script_language.h b/core/script_language.h
index 0c1f99cea6..64c6f2eb81 100644
--- a/core/script_language.h
+++ b/core/script_language.h
@@ -236,6 +236,8 @@ public:
virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const = 0;
virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) = 0;
+ virtual void add_named_global_constant(const StringName &p_name, const Variant &p_value) {}
+ virtual void remove_named_global_constant(const StringName &p_name) {}
/* MULTITHREAD FUNCTIONS */
@@ -350,6 +352,8 @@ class ScriptDebugger {
public:
typedef void (*RequestSceneTreeMessageFunc)(void *);
+ int update_frequency;
+
struct LiveEditFuncs {
void *udata;
@@ -386,6 +390,7 @@ public:
bool is_breakpoint(int p_line, const StringName &p_source) const;
bool is_breakpoint_line(int p_line) const;
void clear_breakpoints();
+ const Map<int, Set<StringName> > &get_breakpoints() const { return breakpoints; }
virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true) = 0;
virtual void idle_poll();
diff --git a/core/ustring.cpp b/core/ustring.cpp
index 921d20a6fd..51f05468e2 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -753,6 +753,46 @@ Vector<String> String::split(const String &p_splitter, bool p_allow_empty, int p
return ret;
}
+Vector<String> String::rsplit(const String &p_splitter, bool p_allow_empty, int p_maxsplit) const {
+
+ Vector<String> ret;
+ const int len = length();
+ int from = len;
+
+ while (true) {
+
+ int end = rfind(p_splitter, from);
+ if (end < 0)
+ end = 0;
+
+ if (p_allow_empty || (end < from)) {
+ const String str = substr(end > 0 ? end + p_splitter.length() : end, end > 0 ? from - end : from + 2);
+
+ if (p_maxsplit <= 0) {
+ ret.push_back(str);
+ } else if (p_maxsplit > 0) {
+
+ // Put rest of the string and leave cycle.
+ if (p_maxsplit == ret.size()) {
+ ret.push_back(substr(0, from + 2));
+ break;
+ }
+
+ // Otherwise, push items until positive limit is reached.
+ ret.push_back(str);
+ }
+ }
+
+ if (end == 0)
+ break;
+
+ from = end - p_splitter.length();
+ }
+
+ ret.invert();
+ return ret;
+}
+
Vector<float> String::split_floats(const String &p_splitter, bool p_allow_empty) const {
Vector<float> ret;
@@ -945,8 +985,8 @@ String String::num(double p_num, int p_decimals) {
#ifndef NO_USE_STDLIB
- if (p_decimals > 12)
- p_decimals = 12;
+ if (p_decimals > 16)
+ p_decimals = 16;
char fmt[7];
fmt[0] = '%';
diff --git a/core/ustring.h b/core/ustring.h
index 1ed694bb80..b57e9629d9 100644
--- a/core/ustring.h
+++ b/core/ustring.h
@@ -172,6 +172,7 @@ public:
String get_slicec(CharType p_splitter, int p_slice) const;
Vector<String> split(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const;
+ Vector<String> rsplit(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const;
Vector<String> split_spaces() const;
Vector<float> split_floats(const String &p_splitter, bool p_allow_empty = true) const;
Vector<float> split_floats_mk(const Vector<String> &p_splitters, bool p_allow_empty = true) const;
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index bd1cde5a82..4158c2a60e 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -257,6 +257,7 @@ struct _VariantCall {
VCALL_LOCALMEM2R(String, insert);
VCALL_LOCALMEM0R(String, capitalize);
VCALL_LOCALMEM3R(String, split);
+ VCALL_LOCALMEM3R(String, rsplit);
VCALL_LOCALMEM2R(String, split_floats);
VCALL_LOCALMEM0R(String, to_upper);
VCALL_LOCALMEM0R(String, to_lower);
@@ -340,6 +341,7 @@ struct _VariantCall {
VCALL_LOCALMEM1R(Vector2, angle_to);
VCALL_LOCALMEM1R(Vector2, angle_to_point);
VCALL_LOCALMEM2R(Vector2, linear_interpolate);
+ VCALL_LOCALMEM2R(Vector2, slerp);
VCALL_LOCALMEM4R(Vector2, cubic_interpolate);
VCALL_LOCALMEM1R(Vector2, rotated);
VCALL_LOCALMEM0R(Vector2, tangent);
@@ -380,6 +382,7 @@ struct _VariantCall {
VCALL_LOCALMEM1R(Vector3, snapped);
VCALL_LOCALMEM2R(Vector3, rotated);
VCALL_LOCALMEM2R(Vector3, linear_interpolate);
+ VCALL_LOCALMEM2R(Vector3, slerp);
VCALL_LOCALMEM4R(Vector3, cubic_interpolate);
VCALL_LOCALMEM1R(Vector3, dot);
VCALL_LOCALMEM1R(Vector3, cross);
@@ -439,6 +442,9 @@ struct _VariantCall {
VCALL_LOCALMEM2R(Quat, slerp);
VCALL_LOCALMEM2R(Quat, slerpni);
VCALL_LOCALMEM4R(Quat, cubic_slerp);
+ VCALL_LOCALMEM0R(Quat, get_euler);
+ VCALL_LOCALMEM1(Quat, set_euler);
+ VCALL_LOCALMEM2(Quat, set_axis_angle);
VCALL_LOCALMEM0R(Color, to_rgba32);
VCALL_LOCALMEM0R(Color, to_argb32);
@@ -876,6 +882,11 @@ struct _VariantCall {
r_ret = Quat(((Vector3)(*p_args[0])), ((float)(*p_args[1])));
}
+ static void Quat_init3(Variant &r_ret, const Variant **p_args) {
+
+ r_ret = Quat(((Vector3)(*p_args[0])));
+ }
+
static void Color_init1(Variant &r_ret, const Variant **p_args) {
r_ret = Color(*p_args[0], *p_args[1], *p_args[2], *p_args[3]);
@@ -1150,7 +1161,7 @@ Variant Variant::construct(const Variant::Type p_type, const Variant **p_args, i
case RECT2: return (Rect2(*p_args[0]));
case VECTOR3: return (Vector3(*p_args[0]));
case PLANE: return (Plane(*p_args[0]));
- case QUAT: return (Quat(*p_args[0]));
+ case QUAT: return (p_args[0]->operator Quat());
case AABB:
return (::AABB(*p_args[0])); // 10
case BASIS: return (Basis(p_args[0]->operator Basis()));
@@ -1459,6 +1470,7 @@ void register_variant_methods() {
ADDFUNC2R(STRING, STRING, String, insert, INT, "position", STRING, "what", varray());
ADDFUNC0R(STRING, STRING, String, capitalize, varray());
ADDFUNC3R(STRING, POOL_STRING_ARRAY, String, split, STRING, "divisor", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0));
+ ADDFUNC3R(STRING, POOL_STRING_ARRAY, String, rsplit, STRING, "divisor", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0));
ADDFUNC2R(STRING, POOL_REAL_ARRAY, String, split_floats, STRING, "divisor", BOOL, "allow_empty", varray(true));
ADDFUNC0R(STRING, STRING, String, to_upper, varray());
@@ -1518,6 +1530,7 @@ void register_variant_methods() {
ADDFUNC1R(VECTOR2, REAL, Vector2, angle_to, VECTOR2, "to", varray());
ADDFUNC1R(VECTOR2, REAL, Vector2, angle_to_point, VECTOR2, "to", varray());
ADDFUNC2R(VECTOR2, VECTOR2, Vector2, linear_interpolate, VECTOR2, "b", REAL, "t", varray());
+ ADDFUNC2R(VECTOR2, VECTOR2, Vector2, slerp, VECTOR2, "b", REAL, "t", varray());
ADDFUNC4R(VECTOR2, VECTOR2, Vector2, cubic_interpolate, VECTOR2, "b", VECTOR2, "pre_a", VECTOR2, "post_b", REAL, "t", varray());
ADDFUNC1R(VECTOR2, VECTOR2, Vector2, rotated, REAL, "phi", varray());
ADDFUNC0R(VECTOR2, VECTOR2, Vector2, tangent, varray());
@@ -1557,6 +1570,7 @@ void register_variant_methods() {
ADDFUNC1R(VECTOR3, VECTOR3, Vector3, snapped, VECTOR3, "by", varray());
ADDFUNC2R(VECTOR3, VECTOR3, Vector3, rotated, VECTOR3, "axis", REAL, "phi", varray());
ADDFUNC2R(VECTOR3, VECTOR3, Vector3, linear_interpolate, VECTOR3, "b", REAL, "t", varray());
+ ADDFUNC2R(VECTOR3, VECTOR3, Vector3, slerp, VECTOR3, "b", REAL, "t", varray());
ADDFUNC4R(VECTOR3, VECTOR3, Vector3, cubic_interpolate, VECTOR3, "b", VECTOR3, "pre_a", VECTOR3, "post_b", REAL, "t", varray());
ADDFUNC1R(VECTOR3, REAL, Vector3, dot, VECTOR3, "b", varray());
ADDFUNC1R(VECTOR3, VECTOR3, Vector3, cross, VECTOR3, "b", varray());
@@ -1594,6 +1608,9 @@ void register_variant_methods() {
ADDFUNC2R(QUAT, QUAT, Quat, slerp, QUAT, "b", REAL, "t", varray());
ADDFUNC2R(QUAT, QUAT, Quat, slerpni, QUAT, "b", REAL, "t", varray());
ADDFUNC4R(QUAT, QUAT, Quat, cubic_slerp, QUAT, "b", QUAT, "pre_a", QUAT, "post_b", REAL, "t", varray());
+ ADDFUNC0R(QUAT, VECTOR3, Quat, get_euler, varray());
+ ADDFUNC1(QUAT, NIL, Quat, set_euler, VECTOR3, "euler", varray());
+ ADDFUNC2(QUAT, NIL, Quat, set_axis_angle, VECTOR3, "axis", REAL, "angle", varray());
ADDFUNC0R(COLOR, INT, Color, to_rgba32, varray());
ADDFUNC0R(COLOR, INT, Color, to_argb32, varray());
@@ -1816,6 +1833,7 @@ void register_variant_methods() {
_VariantCall::add_constructor(_VariantCall::Quat_init1, Variant::QUAT, "x", Variant::REAL, "y", Variant::REAL, "z", Variant::REAL, "w", Variant::REAL);
_VariantCall::add_constructor(_VariantCall::Quat_init2, Variant::QUAT, "axis", Variant::VECTOR3, "angle", Variant::REAL);
+ _VariantCall::add_constructor(_VariantCall::Quat_init3, Variant::QUAT, "euler", Variant::VECTOR3);
_VariantCall::add_constructor(_VariantCall::Color_init1, Variant::COLOR, "r", Variant::REAL, "g", Variant::REAL, "b", Variant::REAL, "a", Variant::REAL);
_VariantCall::add_constructor(_VariantCall::Color_init2, Variant::COLOR, "r", Variant::REAL, "g", Variant::REAL, "b", Variant::REAL);