diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp index 2d05e5bf645..5a6c867f13d 100644 --- a/core/io/packet_peer_udp.cpp +++ b/core/io/packet_peer_udp.cpp @@ -31,6 +31,7 @@ #include "packet_peer_udp.h" #include "core/io/ip.h" +#include "core/io/udp_server.h" void PacketPeerUDP::set_blocking_mode(bool p_enable) { @@ -38,13 +39,14 @@ void PacketPeerUDP::set_blocking_mode(bool p_enable) { } void PacketPeerUDP::set_broadcast_enabled(bool p_enabled) { + ERR_FAIL_COND(udp_server); broadcast = p_enabled; if (_sock.is_valid() && _sock->is_open()) _sock->set_broadcasting_enabled(p_enabled); } Error PacketPeerUDP::join_multicast_group(IP_Address p_multi_address, String p_if_name) { - + ERR_FAIL_COND_V(udp_server, ERR_LOCKED); ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(!p_multi_address.is_valid(), ERR_INVALID_PARAMETER); @@ -59,7 +61,7 @@ Error PacketPeerUDP::join_multicast_group(IP_Address p_multi_address, String p_i } Error PacketPeerUDP::leave_multicast_group(IP_Address p_multi_address, String p_if_name) { - + ERR_FAIL_COND_V(udp_server, ERR_LOCKED); ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(!_sock->is_open(), ERR_UNCONFIGURED); return _sock->leave_multicast_group(p_multi_address, p_if_name); @@ -133,7 +135,7 @@ Error PacketPeerUDP::put_packet(const uint8_t *p_buffer, int p_buffer_size) { } do { - if (connected) { + if (connected && !udp_server) { err = _sock->send(p_buffer, p_buffer_size, sent); } else { err = _sock->sendto(p_buffer, p_buffer_size, sent, peer_addr, peer_port); @@ -188,26 +190,25 @@ Error PacketPeerUDP::listen(int p_port, const IP_Address &p_bind_address, int p_ return OK; } -Error PacketPeerUDP::connect_socket(Ref p_sock) { - Error err; - int read = 0; - uint16_t r_port; - IP_Address r_ip; - - err = p_sock->recvfrom(recv_buffer, sizeof(recv_buffer), read, r_ip, r_port, true); - ERR_FAIL_COND_V(err != OK, err); - err = p_sock->connect_to_host(r_ip, r_port); - ERR_FAIL_COND_V(err != OK, err); +Error PacketPeerUDP::connect_shared_socket(Ref p_sock, IP_Address p_ip, uint16_t p_port, UDPServer *p_server) { + udp_server = p_server; + connected = true; _sock = p_sock; - peer_addr = r_ip; - peer_port = r_port; + peer_addr = p_ip; + peer_port = p_port; packet_ip = peer_addr; packet_port = peer_port; - connected = true; return OK; } +void PacketPeerUDP::disconnect_shared_socket() { + udp_server = nullptr; + _sock = Ref(NetSocket::create()); + close(); +} + Error PacketPeerUDP::connect_to_host(const IP_Address &p_host, int p_port) { + ERR_FAIL_COND_V(udp_server, ERR_LOCKED); ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER); @@ -245,9 +246,13 @@ bool PacketPeerUDP::is_connected_to_host() const { } void PacketPeerUDP::close() { - - if (_sock.is_valid()) + if (udp_server) { + udp_server->remove_peer(peer_addr, peer_port); + udp_server = nullptr; + _sock = Ref(NetSocket::create()); + } else if (_sock.is_valid()) { _sock->close(); + } rb.resize(16); queue_count = 0; connected = false; @@ -266,6 +271,9 @@ Error PacketPeerUDP::_poll() { if (!_sock->is_open()) { return FAILED; } + if (udp_server) { + return OK; // Handled by UDPServer. + } Error err; int read; @@ -287,23 +295,29 @@ Error PacketPeerUDP::_poll() { return FAILED; } - if (rb.space_left() < read + 24) { + err = store_packet(ip, port, recv_buffer, read); #ifdef TOOLS_ENABLED - WARN_PRINTS("Buffer full, dropping packets!"); -#endif - continue; + if (err != OK) { + WARN_PRINT("Buffer full, dropping packets!"); } - - uint32_t port32 = port; - rb.write(ip.get_ipv6(), 16); - rb.write((uint8_t *)&port32, 4); - rb.write((uint8_t *)&read, 4); - rb.write(recv_buffer, read); - ++queue_count; +#endif } return OK; } + +Error PacketPeerUDP::store_packet(IP_Address p_ip, uint32_t p_port, uint8_t *p_buf, int p_buf_size) { + if (rb.space_left() < p_buf_size + 24) { + return ERR_OUT_OF_MEMORY; + } + rb.write(p_ip.get_ipv6(), 16); + rb.write((uint8_t *)&p_port, 4); + rb.write((uint8_t *)&p_buf_size, 4); + rb.write(p_buf, p_buf_size); + ++queue_count; + return OK; +} + bool PacketPeerUDP::is_listening() const { return _sock.is_valid() && _sock->is_open(); @@ -349,6 +363,7 @@ PacketPeerUDP::PacketPeerUDP() : connected(false), blocking(true), broadcast(false), + udp_server(nullptr), _sock(Ref(NetSocket::create())) { rb.resize(16); } diff --git a/core/io/packet_peer_udp.h b/core/io/packet_peer_udp.h index b5a9fc9ec3c..249f84fca7c 100644 --- a/core/io/packet_peer_udp.h +++ b/core/io/packet_peer_udp.h @@ -35,6 +35,8 @@ #include "core/io/net_socket.h" #include "core/io/packet_peer.h" +class UDPServer; + class PacketPeerUDP : public PacketPeer { GDCLASS(PacketPeerUDP, PacketPeer); @@ -55,6 +57,7 @@ protected: bool connected; bool blocking; bool broadcast; + UDPServer *udp_server; Ref _sock; static void _bind_methods(); @@ -72,7 +75,9 @@ public: Error wait(); bool is_listening() const; - Error connect_socket(Ref p_sock); // Used by UDPServer + Error connect_shared_socket(Ref p_sock, IP_Address p_ip, uint16_t p_port, UDPServer *ref); // Used by UDPServer + void disconnect_shared_socket(); // Used by UDPServer + Error store_packet(IP_Address p_ip, uint32_t p_port, uint8_t *p_buf, int p_buf_size); // Used internally and by UDPServer Error connect_to_host(const IP_Address &p_host, int p_port); bool is_connected_to_host() const; diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index 4f7eeddc437..aebf5f204da 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -33,10 +33,8 @@ #include "core/os/file_access.h" #include "core/translation.h" -RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const String &p_path) { - +RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { enum Status { - STATUS_NONE, STATUS_READING_ID, STATUS_READING_STRING, @@ -56,6 +54,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S bool skip_this = false; bool skip_next = false; bool is_eof = false; + const String path = f->get_path(); while (!is_eof) { @@ -67,7 +66,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S if (status == STATUS_READING_ID) { memdelete(f); - ERR_FAIL_V_MSG(RES(), p_path + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: "); + ERR_FAIL_V_MSG(RES(), "Unexpected EOF while reading 'msgid' at: " + path + ":" + itos(line)); } else { break; } @@ -76,9 +75,8 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S if (l.begins_with("msgid")) { if (status == STATUS_READING_ID) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), p_path + ":" + itos(line) + " Unexpected 'msgid', was expecting 'msgstr' while parsing: "); + ERR_FAIL_V_MSG(RES(), "Unexpected 'msgid', was expecting 'msgstr' while parsing: " + path + ":" + itos(line)); } if (msg_id != "") { @@ -98,9 +96,8 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S if (l.begins_with("msgstr")) { if (status != STATUS_READING_ID) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), p_path + ":" + itos(line) + " Unexpected 'msgstr', was expecting 'msgid' while parsing: "); + ERR_FAIL_V_MSG(RES(), "Unexpected 'msgstr', was expecting 'msgid' while parsing: " + path + ":" + itos(line)); } l = l.substr(6, l.length()).strip_edges(); @@ -115,7 +112,10 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S continue; //nothing to read or comment } - ERR_FAIL_COND_V_MSG(!l.begins_with("\"") || status == STATUS_NONE, RES(), p_path + ":" + itos(line) + " Invalid line '" + l + "' while parsing: "); + if (!l.begins_with("\"") || status == STATUS_NONE) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Invalid line '" + l + "' while parsing: " + path + ":" + itos(line)); + } l = l.substr(1, l.length()); //find final quote @@ -128,7 +128,10 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S } } - ERR_FAIL_COND_V_MSG(end_pos == -1, RES(), p_path + ":" + itos(line) + " Expected '\"' at end of message while parsing file: "); + if (end_pos == -1) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Expected '\"' at end of message while parsing: " + path + ":" + itos(line)); + } l = l.substr(0, end_pos); l = l.c_unescape(); @@ -141,7 +144,6 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S line++; } - f->close(); memdelete(f); if (status == STATUS_READING_STRING) { @@ -153,7 +155,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S config = msg_str; } - ERR_FAIL_COND_V_MSG(config == "", RES(), "No config found in file: " + p_path + "."); + ERR_FAIL_COND_V_MSG(config == "", RES(), "No config found in file: " + path + "."); Vector configs = config.split("\n"); for (int i = 0; i < configs.size(); i++) { @@ -190,7 +192,6 @@ RES TranslationLoaderPO::load(const String &p_path, const String &p_original_pat void TranslationLoaderPO::get_recognized_extensions(List *p_extensions) const { p_extensions->push_back("po"); - //p_extensions->push_back("mo"); //mo in the future... } bool TranslationLoaderPO::handles_type(const String &p_type) const { diff --git a/core/io/translation_loader_po.h b/core/io/translation_loader_po.h index 47e64276cac..13e07a6cf06 100644 --- a/core/io/translation_loader_po.h +++ b/core/io/translation_loader_po.h @@ -37,7 +37,7 @@ class TranslationLoaderPO : public ResourceFormatLoader { public: - static RES load_translation(FileAccess *f, Error *r_error, const String &p_path = String()); + static RES load_translation(FileAccess *f, Error *r_error); virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual void get_recognized_extensions(List *p_extensions) const; virtual bool handles_type(const String &p_type) const; diff --git a/core/io/udp_server.cpp b/core/io/udp_server.cpp index 16b7863cddb..485bb19f1e1 100644 --- a/core/io/udp_server.cpp +++ b/core/io/udp_server.cpp @@ -33,10 +33,58 @@ void UDPServer::_bind_methods() { ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &UDPServer::listen, DEFVAL("*")); + ClassDB::bind_method(D_METHOD("poll"), &UDPServer::poll); ClassDB::bind_method(D_METHOD("is_connection_available"), &UDPServer::is_connection_available); ClassDB::bind_method(D_METHOD("is_listening"), &UDPServer::is_listening); ClassDB::bind_method(D_METHOD("take_connection"), &UDPServer::take_connection); ClassDB::bind_method(D_METHOD("stop"), &UDPServer::stop); + ClassDB::bind_method(D_METHOD("set_max_pending_connections", "max_pending_connections"), &UDPServer::set_max_pending_connections); + ClassDB::bind_method(D_METHOD("get_max_pending_connections"), &UDPServer::get_max_pending_connections); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_pending_connections", PROPERTY_HINT_RANGE, "0,256,1"), "set_max_pending_connections", "get_max_pending_connections"); +} + +Error UDPServer::poll() { + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + if (!_sock->is_open()) { + return ERR_UNCONFIGURED; + } + Error err; + int read; + IP_Address ip; + uint16_t port; + while (true) { + err = _sock->recvfrom(recv_buffer, sizeof(recv_buffer), read, ip, port); + if (err != OK) { + if (err == ERR_BUSY) { + break; + } + return FAILED; + } + Peer p; + p.ip = ip; + p.port = port; + List::Element *E = peers.find(p); + if (!E) { + E = pending.find(p); + } + if (E) { + E->get().peer->store_packet(ip, port, recv_buffer, read); + } else { + if (pending.size() >= max_pending_connections) { + // Drop connection. + continue; + } + // It's a new peer, add it to the pending list. + Peer peer; + peer.ip = ip; + peer.port = port; + peer.peer = memnew(PacketPeerUDP); + peer.peer->connect_shared_socket(_sock, ip, port, this); + peer.peer->store_packet(ip, port, recv_buffer, read); + pending.push_back(peer); + } + } + return OK; } Error UDPServer::listen(uint16_t p_port, const IP_Address &p_bind_address) { @@ -82,8 +130,24 @@ bool UDPServer::is_connection_available() const { if (!_sock->is_open()) return false; - Error err = _sock->poll(NetSocket::POLL_TYPE_IN, 0); - return (err == OK); + return pending.size() > 0; +} + +void UDPServer::set_max_pending_connections(int p_max) { + ERR_FAIL_COND_MSG(p_max < 0, "Max pending connections value must be a positive number (0 means refuse new connections)."); + max_pending_connections = p_max; + while (p_max > pending.size()) { + List::Element *E = pending.back(); + if (!E) { + break; + } + memdelete(E->get().peer); + pending.erase(E); + } +} + +int UDPServer::get_max_pending_connections() const { + return max_pending_connections; } Ref UDPServer::take_connection() { @@ -93,11 +157,20 @@ Ref UDPServer::take_connection() { return conn; } - conn = Ref(memnew(PacketPeerUDP)); - conn->connect_socket(_sock); - _sock = Ref(NetSocket::create()); - listen(bind_port, bind_address); - return conn; + Peer peer = pending[0]; + pending.pop_front(); + peers.push_back(peer); + return peer.peer; +} + +void UDPServer::remove_peer(IP_Address p_ip, int p_port) { + Peer peer; + peer.ip = p_ip; + peer.port = p_port; + List::Element *E = peers.find(peer); + if (E) { + peers.erase(E); + } } void UDPServer::stop() { @@ -107,6 +180,19 @@ void UDPServer::stop() { } bind_port = 0; bind_address = IP_Address(); + List::Element *E = peers.front(); + while (E) { + E->get().peer->disconnect_shared_socket(); + E = E->next(); + } + E = pending.front(); + while (E) { + E->get().peer->disconnect_shared_socket(); + memdelete(E->get().peer); + E = E->next(); + } + peers.clear(); + pending.clear(); } UDPServer::UDPServer() : diff --git a/core/io/udp_server.h b/core/io/udp_server.h index 90bb82b62b6..3175b09b195 100644 --- a/core/io/udp_server.h +++ b/core/io/udp_server.h @@ -38,15 +38,40 @@ class UDPServer : public Reference { GDCLASS(UDPServer, Reference); protected: - static void _bind_methods(); - int bind_port; + enum { + PACKET_BUFFER_SIZE = 65536 + }; + + struct Peer { + PacketPeerUDP *peer; + IP_Address ip; + uint16_t port = 0; + + bool operator==(const Peer &p_other) const { + return (ip == p_other.ip && port == p_other.port); + } + }; + uint8_t recv_buffer[PACKET_BUFFER_SIZE]; + + int bind_port = 0; IP_Address bind_address; + + List peers; + List pending; + int max_pending_connections = 16; + Ref _sock; + static void _bind_methods(); + public: + void remove_peer(IP_Address p_ip, int p_port); Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*")); + Error poll(); bool is_listening() const; bool is_connection_available() const; + void set_max_pending_connections(int p_max); + int get_max_pending_connections() const; Ref take_connection(); void stop(); diff --git a/doc/classes/CollisionObject2D.xml b/doc/classes/CollisionObject2D.xml index 4fdead27b97..e87566e42cb 100644 --- a/doc/classes/CollisionObject2D.xml +++ b/doc/classes/CollisionObject2D.xml @@ -216,7 +216,7 @@ - + If [code]true[/code], this object is pickable. A pickable object can detect the mouse pointer entering/leaving, and if the mouse is inside it, report input events. diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index a6f11fea55d..59d4f47ba57 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -97,6 +97,7 @@ [codeblock] call("set", "position", Vector2(42.0, 0.0)) [/codeblock] + [b]Note:[/b] In C#, the method name must be specified as snake_case if it is defined by a built-in Godot node. This doesn't apply to user-defined methods where you should use the same convention as in the C# source (typically PascalCase). @@ -109,6 +110,7 @@ [codeblock] call_deferred("set", "position", Vector2(42.0, 0.0)) [/codeblock] + [b]Note:[/b] In C#, the method name must be specified as snake_case if it is defined by a built-in Godot node. This doesn't apply to user-defined methods where you should use the same convention as in the C# source (typically PascalCase). @@ -205,6 +207,7 @@ Returns the [Variant] value of the given [code]property[/code]. If the [code]property[/code] doesn't exist, this will return [code]null[/code]. + [b]Note:[/b] In C#, the property name must be specified as snake_case if it is defined by a built-in Godot node. This doesn't apply to user-defined properties where you should use the same convention as in the C# source (typically PascalCase). @@ -405,6 +408,7 @@ Assigns a new value to the given property. If the [code]property[/code] does not exist, nothing will happen. + [b]Note:[/b] In C#, the property name must be specified as snake_case if it is defined by a built-in Godot node. This doesn't apply to user-defined properties where you should use the same convention as in the C# source (typically PascalCase). @@ -425,6 +429,7 @@ Assigns a new value to the given property, after the current frame's physics step. This is equivalent to calling [method set] via [method call_deferred], i.e. [code]call_deferred("set", property, value)[/code]. + [b]Note:[/b] In C#, the property name must be specified as snake_case if it is defined by a built-in Godot node. This doesn't apply to user-defined properties where you should use the same convention as in the C# source (typically PascalCase). diff --git a/doc/classes/UDPServer.xml b/doc/classes/UDPServer.xml index 71a9422689d..844c984ed48 100644 --- a/doc/classes/UDPServer.xml +++ b/doc/classes/UDPServer.xml @@ -5,6 +5,7 @@ A simple server that opens a UDP socket and returns connected [PacketPeerUDP] upon receiving new packets. See also [method PacketPeerUDP.connect_to_host]. + After starting the server ([method listen]), you will need to [method poll] it at regular intervals (e.g. inside [method Node._process]) for it to process new packets, delivering them to the appropriate [PacketPeerUDP], and taking new connections. Below a small example of how it can be used: [codeblock] # server.gd @@ -17,6 +18,7 @@ server.listen(4242) func _process(delta): + server.poll() # Important! if server.is_connection_available(): var peer : PacketPeerUDP = server.take_connection() var pkt = peer.get_packet() @@ -57,7 +59,7 @@ - Returns [code]true[/code] if a packet with a new address/port combination is received on the socket. + Returns [code]true[/code] if a packet with a new address/port combination was received on the socket. @@ -78,21 +80,33 @@ Starts the server by opening a UDP socket listening on the given port. You can optionally specify a [code]bind_address[/code] to only listen for packets sent to that address. See also [method PacketPeerUDP.listen]. + + + + + Call this method at regular intervals (e.g. inside [method Node._process]) to process new packets. And packet from known address/port pair will be delivered to the appropriate [PacketPeerUDP], any packet received from an unknown address/port pair will be added as a pending connection (see [method is_connection_available], [method take_connection]). The maximum number of pending connection is defined via [member max_pending_connections]. + + - Stops the server, closing the UDP socket if open. Will not disconnect any connected [PacketPeerUDP]. + Stops the server, closing the UDP socket if open. Will close all connected [PacketPeerUDP] accepted via [method take_connection] (remote peers will not be notified). - Returns a [PacketPeerUDP] connected to the address/port combination of the first packet in queue. Will return [code]null[/code] if no packet is in queue. See also [method PacketPeerUDP.connect_to_host]. + Returns the first pending connection (connected to the appropriate address/port). Will return [code]null[/code] if no new connection is available. See also [method is_connection_available], [method PacketPeerUDP.connect_to_host]. + + + Define the maximum number of pending connections, during [method poll], any new pending connection exceeding that value will be automatically dropped. Setting this value to [code]0[/code] effectively prevents any new pending connection to be accepted (e.g. when all your players have connected). + + diff --git a/doc/classes/VehicleBody.xml b/doc/classes/VehicleBody.xml index 3b99bf9ed10..672aa59855b 100644 --- a/doc/classes/VehicleBody.xml +++ b/doc/classes/VehicleBody.xml @@ -6,6 +6,7 @@ This node implements all the physics logic needed to simulate a car. It is based on the raycast vehicle system commonly found in physics engines. You will need to add a [CollisionShape] for the main body of your vehicle and add [VehicleWheel] nodes for the wheels. You should also add a [MeshInstance] to this node for the 3D model of your car but this model should not include meshes for the wheels. You should control the vehicle by using the [member brake], [member engine_force], and [member steering] properties and not change the position or orientation of this node directly. [b]Note:[/b] The origin point of your VehicleBody will determine the center of gravity of your vehicle so it is better to keep this low and move the [CollisionShape] and [MeshInstance] upwards. + [b]Note:[/b] This class has known issues and isn't designed to provide realistic 3D vehicle physics. If you want advanced vehicle physics, you will probably have to write your own physics integration using another [PhysicsBody] class. diff --git a/doc/classes/VehicleWheel.xml b/doc/classes/VehicleWheel.xml index b611d00da04..055ce5990f0 100644 --- a/doc/classes/VehicleWheel.xml +++ b/doc/classes/VehicleWheel.xml @@ -5,6 +5,7 @@ This node needs to be used as a child node of [VehicleBody] and simulates the behavior of one of its wheels. This node also acts as a collider to detect if the wheel is touching a surface. + [b]Note:[/b] This class has known issues and isn't designed to provide realistic 3D vehicle physics. If you want advanced vehicle physics, you will probably have to write your own physics integration using another [PhysicsBody] class. diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index aeb6aec44cd..b8621b27ffb 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -89,7 +89,7 @@ void EditorAudioBus::_notification(int p_what) { bypass->set_icon(get_icon("AudioBusBypass", "EditorIcons")); bypass->add_color_override("icon_color_pressed", bypass_color); - bus_options->set_icon(get_icon("GuiTabMenu", "EditorIcons")); + bus_options->set_icon(get_icon("GuiTabMenuHl", "EditorIcons")); update_bus(); set_process(true); @@ -181,7 +181,7 @@ void EditorAudioBus::_notification(int p_what) { mute->set_icon(get_icon("AudioBusMute", "EditorIcons")); bypass->set_icon(get_icon("AudioBusBypass", "EditorIcons")); - bus_options->set_icon(get_icon("GuiTabMenu", "EditorIcons")); + bus_options->set_icon(get_icon("GuiTabMenuHl", "EditorIcons")); } break; case NOTIFICATION_MOUSE_EXIT: case NOTIFICATION_DRAG_END: { diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 4649bf0d189..0a15aabe80b 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -997,7 +997,7 @@ void EditorSettings::setup_language() { FileAccessMemory *fa = memnew(FileAccessMemory); fa->open_custom(data.ptr(), data.size()); - Ref tr = TranslationLoaderPO::load_translation(fa, NULL, "translation_" + String(etl->lang)); + Ref tr = TranslationLoaderPO::load_translation(fa, NULL); if (tr.is_valid()) { tr->set_locale(etl->lang); diff --git a/editor/icons/icon_GUI_tab_menu.svg b/editor/icons/icon_GUI_tab_menu.svg index 8bf5ef2f7de..55e98143a69 100644 --- a/editor/icons/icon_GUI_tab_menu.svg +++ b/editor/icons/icon_GUI_tab_menu.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/editor/icons/icon_GUI_tab_menu_hl.svg b/editor/icons/icon_GUI_tab_menu_hl.svg index 42d58a5abf3..e4c5b7bf1e5 100644 --- a/editor/icons/icon_GUI_tab_menu_hl.svg +++ b/editor/icons/icon_GUI_tab_menu_hl.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 994bc8bb6fd..3c8a8264760 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -126,7 +126,7 @@ void AnimationPlayerEditor::_notification(int p_what) { stop->set_icon(get_icon("Stop", "EditorIcons")); onion_toggle->set_icon(get_icon("Onion", "EditorIcons")); - onion_skinning->set_icon(get_icon("GuiTabMenu", "EditorIcons")); + onion_skinning->set_icon(get_icon("GuiTabMenuHl", "EditorIcons")); pin->set_icon(get_icon("Pin", "EditorIcons")); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index bebf46f3127..20bf32abe35 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -3861,7 +3861,7 @@ void CanvasItemEditor::_notification(int p_what) { rotate_button->set_icon(get_icon("ToolRotate", "EditorIcons")); smart_snap_button->set_icon(get_icon("Snap", "EditorIcons")); grid_snap_button->set_icon(get_icon("SnapGrid", "EditorIcons")); - snap_config_menu->set_icon(get_icon("GuiTabMenu", "EditorIcons")); + snap_config_menu->set_icon(get_icon("GuiTabMenuHl", "EditorIcons")); skeleton_menu->set_icon(get_icon("Bone", "EditorIcons")); override_camera_button->set_icon(get_icon("Camera2D", "EditorIcons")); pan_button->set_icon(get_icon("ToolPan", "EditorIcons")); @@ -3878,7 +3878,7 @@ void CanvasItemEditor::_notification(int p_what) { key_scale_button->set_icon(get_icon("KeyScale", "EditorIcons")); key_insert_button->set_icon(get_icon("Key", "EditorIcons")); key_auto_insert_button->set_icon(get_icon("AutoKey", "EditorIcons")); - animation_menu->set_icon(get_icon("GuiTabMenu", "EditorIcons")); + animation_menu->set_icon(get_icon("GuiTabMenuHl", "EditorIcons")); zoom_minus->set_icon(get_icon("ZoomLess", "EditorIcons")); zoom_plus->set_icon(get_icon("ZoomMore", "EditorIcons")); @@ -6262,7 +6262,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte selector->add_child(vbc); vbc->set_h_size_flags(SIZE_EXPAND_FILL); vbc->set_v_size_flags(SIZE_EXPAND_FILL); - vbc->set_custom_minimum_size(Size2(200, 260) * EDSCALE); + vbc->set_custom_minimum_size(Size2(240, 260) * EDSCALE); btn_group = memnew(VBoxContainer); vbc->add_child(btn_group); diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 953a8b953f4..7f5ebb262d2 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -2542,7 +2542,7 @@ void SpatialEditorViewport::_notification(int p_what) { if (p_what == NOTIFICATION_THEME_CHANGED) { - view_menu->set_icon(get_icon("GuiTabMenu", "EditorIcons")); + view_menu->set_icon(get_icon("GuiTabMenuHl", "EditorIcons")); preview_camera->set_icon(get_icon("Camera", "EditorIcons")); view_menu->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); diff --git a/main/main.cpp b/main/main.cpp index 7e3806a9df8..551b5ccf5d9 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -954,21 +954,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph #endif - GLOBAL_DEF("logging/file_logging/enable_file_logging", false); - // Only file logging by default on desktop platforms as logs can't be - // accessed easily on mobile/Web platforms (if at all). - // This also prevents logs from being created for the editor instance, as feature tags - // are disabled while in the editor (even if they should logically apply). - GLOBAL_DEF("logging/file_logging/enable_file_logging.pc", true); - GLOBAL_DEF("logging/file_logging/log_path", "user://logs/godot.log"); - GLOBAL_DEF("logging/file_logging/max_log_files", 5); - ProjectSettings::get_singleton()->set_custom_property_info("logging/file_logging/max_log_files", PropertyInfo(Variant::INT, "logging/file_logging/max_log_files", PROPERTY_HINT_RANGE, "0,20,1,or_greater")); //no negative numbers - if (FileAccess::get_create_func(FileAccess::ACCESS_USERDATA) && GLOBAL_GET("logging/file_logging/enable_file_logging")) { - String base_path = GLOBAL_GET("logging/file_logging/log_path"); - int max_files = GLOBAL_GET("logging/file_logging/max_log_files"); - OS::get_singleton()->add_logger(memnew(RotatedFileLogger(base_path, max_files))); - } - #ifdef TOOLS_ENABLED if (editor) { Engine::get_singleton()->set_editor_hint(true); @@ -985,6 +970,23 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } #endif + GLOBAL_DEF("logging/file_logging/enable_file_logging", false); + // Only file logging by default on desktop platforms as logs can't be + // accessed easily on mobile/Web platforms (if at all). + // This also prevents logs from being created for the editor instance, as feature tags + // are disabled while in the editor (even if they should logically apply). + GLOBAL_DEF("logging/file_logging/enable_file_logging.pc", true); + GLOBAL_DEF("logging/file_logging/log_path", "user://logs/godot.log"); + GLOBAL_DEF("logging/file_logging/max_log_files", 5); + ProjectSettings::get_singleton()->set_custom_property_info("logging/file_logging/max_log_files", PropertyInfo(Variant::INT, "logging/file_logging/max_log_files", PROPERTY_HINT_RANGE, "0,20,1,or_greater")); //no negative numbers + if (!project_manager && !editor && FileAccess::get_create_func(FileAccess::ACCESS_USERDATA) && GLOBAL_GET("logging/file_logging/enable_file_logging")) { + // Don't create logs for the project manager as they would be written to + // the current working directory, which is inconvenient. + String base_path = GLOBAL_GET("logging/file_logging/log_path"); + int max_files = GLOBAL_GET("logging/file_logging/max_log_files"); + OS::get_singleton()->add_logger(memnew(RotatedFileLogger(base_path, max_files))); + } + if (main_args.size() == 0 && String(GLOBAL_DEF("application/run/main_scene", "")) == "") { #ifdef TOOLS_ENABLED if (!editor && !project_manager) { diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp index 4f332b7f505..d327eca5dec 100644 --- a/modules/bullet/area_bullet.cpp +++ b/modules/bullet/area_bullet.cpp @@ -181,7 +181,7 @@ void AreaBullet::reload_body() { void AreaBullet::set_space(SpaceBullet *p_space) { // Clear the old space if there is one if (space) { - overlappingObjects.clear(); + clear_overlaps(false); isScratched = false; // Remove this object form the physics world diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index 406eb467f03..59b4c0b99fb 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -668,6 +668,9 @@ int NetworkedMultiplayerENet::get_unique_id() const { void NetworkedMultiplayerENet::set_refuse_new_connections(bool p_enable) { refuse_connections = p_enable; +#ifdef GODOT_ENET + enet_host_refuse_new_connections(host, p_enable); +#endif } bool NetworkedMultiplayerENet::is_refusing_new_connections() const { diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp index 850871579b6..256c44febcd 100644 --- a/modules/gdnative/nativescript/api_generator.cpp +++ b/modules/gdnative/nativescript/api_generator.cpp @@ -219,7 +219,7 @@ List generate_c_api_classes() { { List inheriters; ClassDB::get_inheriters_from_class("Reference", &inheriters); - bool is_reference = !!inheriters.find(class_name); + bool is_reference = !!inheriters.find(class_name) || class_name == "Reference"; // @Unclear class_api.is_reference = !class_api.is_singleton && is_reference; } @@ -458,6 +458,7 @@ static List generate_c_api_json(const List &p_api) { source.push_back("\t\t\t\t\t{\n"); source.push_back("\t\t\t\t\t\t\"name\": \"" + e->get().argument_names[i] + "\",\n"); source.push_back("\t\t\t\t\t\t\"type\": \"" + e->get().argument_types[i] + "\",\n"); + source.push_back(String("\t\t\t\t\t\t\"has_default_value\": ") + (e->get().default_arguments.has(i) ? "true" : "false") + ",\n"); source.push_back("\t\t\t\t\t\t\"default_value\": \"" + (e->get().default_arguments.has(i) ? (String)e->get().default_arguments[i] : "") + "\"\n"); source.push_back(String("\t\t\t\t\t}") + ((i < e->get().argument_names.size() - 1) ? "," : "") + "\n"); } diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 267e559b7ad..7f89f2037c4 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -2020,116 +2020,114 @@ String GDScriptWarning::get_message() const { #define CHECK_SYMBOLS(m_amount) ERR_FAIL_COND_V(symbols.size() < m_amount, String()); - String msg; - switch (code) { case UNASSIGNED_VARIABLE_OP_ASSIGN: { CHECK_SYMBOLS(1); - msg = "Using assignment with operation but the variable '" + symbols[0] + "' was not previously assigned a value."; + return "Using assignment with operation but the variable '" + symbols[0] + "' was not previously assigned a value."; } break; case UNASSIGNED_VARIABLE: { CHECK_SYMBOLS(1); - msg = "The variable '" + symbols[0] + "' was used but never assigned a value."; + return "The variable '" + symbols[0] + "' was used but never assigned a value."; } break; case UNUSED_VARIABLE: { CHECK_SYMBOLS(1); - msg = "The local variable '" + symbols[0] + "' is declared but never used in the block. If this is intended, prefix it with an underscore: '_" + symbols[0] + "'"; + return "The local variable '" + symbols[0] + "' is declared but never used in the block. If this is intended, prefix it with an underscore: '_" + symbols[0] + "'"; } break; case SHADOWED_VARIABLE: { CHECK_SYMBOLS(2); - msg = "The local variable '" + symbols[0] + "' is shadowing an already-defined variable at line " + symbols[1] + "."; + return "The local variable '" + symbols[0] + "' is shadowing an already-defined variable at line " + symbols[1] + "."; } break; case UNUSED_CLASS_VARIABLE: { CHECK_SYMBOLS(1); - msg = "The class variable '" + symbols[0] + "' is declared but never used in the script."; + return "The class variable '" + symbols[0] + "' is declared but never used in the script."; } break; case UNUSED_ARGUMENT: { CHECK_SYMBOLS(2); - msg = "The argument '" + symbols[1] + "' is never used in the function '" + symbols[0] + "'. If this is intended, prefix it with an underscore: '_" + symbols[1] + "'"; + return "The argument '" + symbols[1] + "' is never used in the function '" + symbols[0] + "'. If this is intended, prefix it with an underscore: '_" + symbols[1] + "'"; } break; case UNREACHABLE_CODE: { CHECK_SYMBOLS(1); - msg = "Unreachable code (statement after return) in function '" + symbols[0] + "()'."; + return "Unreachable code (statement after return) in function '" + symbols[0] + "()'."; } break; case STANDALONE_EXPRESSION: { - msg = "Standalone expression (the line has no effect)."; + return "Standalone expression (the line has no effect)."; } break; case VOID_ASSIGNMENT: { CHECK_SYMBOLS(1); - msg = "Assignment operation, but the function '" + symbols[0] + "()' returns void."; + return "Assignment operation, but the function '" + symbols[0] + "()' returns void."; } break; case NARROWING_CONVERSION: { - msg = "Narrowing conversion (float is converted to int and loses precision)."; + return "Narrowing conversion (float is converted to int and loses precision)."; } break; case FUNCTION_MAY_YIELD: { CHECK_SYMBOLS(1); - msg = "Assigned variable is typed but the function '" + symbols[0] + "()' may yield and return a GDScriptFunctionState instead."; + return "Assigned variable is typed but the function '" + symbols[0] + "()' may yield and return a GDScriptFunctionState instead."; } break; case VARIABLE_CONFLICTS_FUNCTION: { CHECK_SYMBOLS(1); - msg = "Variable declaration of '" + symbols[0] + "' conflicts with a function of the same name."; + return "Variable declaration of '" + symbols[0] + "' conflicts with a function of the same name."; } break; case FUNCTION_CONFLICTS_VARIABLE: { CHECK_SYMBOLS(1); - msg = "Function declaration of '" + symbols[0] + "()' conflicts with a variable of the same name."; + return "Function declaration of '" + symbols[0] + "()' conflicts with a variable of the same name."; } break; case FUNCTION_CONFLICTS_CONSTANT: { CHECK_SYMBOLS(1); - msg = "Function declaration of '" + symbols[0] + "()' conflicts with a constant of the same name."; + return "Function declaration of '" + symbols[0] + "()' conflicts with a constant of the same name."; } break; case INCOMPATIBLE_TERNARY: { - msg = "Values of the ternary conditional are not mutually compatible."; + return "Values of the ternary conditional are not mutually compatible."; } break; case UNUSED_SIGNAL: { CHECK_SYMBOLS(1); - msg = "The signal '" + symbols[0] + "' is declared but never emitted."; + return "The signal '" + symbols[0] + "' is declared but never emitted."; } break; case RETURN_VALUE_DISCARDED: { CHECK_SYMBOLS(1); - msg = "The function '" + symbols[0] + "()' returns a value, but this value is never used."; + return "The function '" + symbols[0] + "()' returns a value, but this value is never used."; } break; case PROPERTY_USED_AS_FUNCTION: { CHECK_SYMBOLS(2); - msg = "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a property with the same name. Did you mean to access it?"; + return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a property with the same name. Did you mean to access it?"; } break; case CONSTANT_USED_AS_FUNCTION: { CHECK_SYMBOLS(2); - msg = "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a constant with the same name. Did you mean to access it?"; + return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a constant with the same name. Did you mean to access it?"; } break; case FUNCTION_USED_AS_PROPERTY: { CHECK_SYMBOLS(2); - msg = "The property '" + symbols[0] + "' was not found in base '" + symbols[1] + "' but there's a method with the same name. Did you mean to call it?"; + return "The property '" + symbols[0] + "' was not found in base '" + symbols[1] + "' but there's a method with the same name. Did you mean to call it?"; } break; case INTEGER_DIVISION: { - msg = "Integer division, decimal part will be discarded."; + return "Integer division, decimal part will be discarded."; } break; case UNSAFE_PROPERTY_ACCESS: { CHECK_SYMBOLS(2); - msg = "The property '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype)."; + return "The property '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype)."; } break; case UNSAFE_METHOD_ACCESS: { CHECK_SYMBOLS(2); - msg = "The method '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype)."; + return "The method '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype)."; } break; case UNSAFE_CAST: { CHECK_SYMBOLS(1); - msg = "The value is cast to '" + symbols[0] + "' but has an unknown type."; + return "The value is cast to '" + symbols[0] + "' but has an unknown type."; } break; case UNSAFE_CALL_ARGUMENT: { CHECK_SYMBOLS(4); - msg = "The argument '" + symbols[0] + "' of the function '" + symbols[1] + "' requires a the subtype '" + symbols[2] + "' but the supertype '" + symbols[3] + "' was provided"; + return "The argument '" + symbols[0] + "' of the function '" + symbols[1] + "' requires a the subtype '" + symbols[2] + "' but the supertype '" + symbols[3] + "' was provided"; } break; case DEPRECATED_KEYWORD: { CHECK_SYMBOLS(2); - msg = "The '" + symbols[0] + "' keyword is deprecated and will be removed in a future release, please replace its uses by '" + symbols[1] + "'."; + return "The '" + symbols[0] + "' keyword is deprecated and will be removed in a future release, please replace its uses by '" + symbols[1] + "'."; } break; case STANDALONE_TERNARY: { - msg = "Standalone ternary conditional operator: the return value is being discarded."; - } break; + return "Standalone ternary conditional operator: the return value is being discarded."; + } case WARNING_MAX: - ERR_FAIL_V_MSG(String(), "Invalid GDScript warning code: " + get_name_from_code(code) + "."); + break; // Can't happen, but silences warning } - return msg + " [" + get_name() + "]"; + ERR_FAIL_V_MSG(String(), "Invalid GDScript warning code: " + get_name_from_code(code) + "."); #undef CHECK_SYMBOLS } diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 121ccbd1022..dc449b6ab23 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -63,7 +63,7 @@ void ExtendGDScriptParser::update_diagnostics() { const GDScriptWarning &warning = E->get(); lsp::Diagnostic diagnostic; diagnostic.severity = lsp::DiagnosticSeverity::Warning; - diagnostic.message = warning.get_message(); + diagnostic.message = "(" + warning.get_name() + "): " + warning.get_message(); diagnostic.source = "gdscript"; diagnostic.code = warning.code; lsp::Range range; diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index 1e9e6672262..46932b80e92 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -1432,13 +1432,14 @@ void KinematicBody::_bind_methods() { KinematicBody::KinematicBody() : PhysicsBody(PhysicsServer::BODY_MODE_KINEMATIC) { - - margin = 0.001; locked_axis = 0; on_floor = false; on_ceiling = false; on_wall = false; + + set_safe_margin(0.001); } + KinematicBody::~KinematicBody() { if (motion_cache.is_valid()) { diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index 13d5c9699f3..0323be62cab 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -228,7 +228,7 @@ struct SpatialIndexer2D { List added; List removed; - int visible_cells = (end.x - begin.x) * (end.y - begin.y); + uint64_t visible_cells = (uint64_t)(end.x - begin.x) * (uint64_t)(end.y - begin.y); if (visible_cells > 10000) { diff --git a/servers/physics/body_sw.cpp b/servers/physics/body_sw.cpp index 64e07e1155a..c8b3ddf27f0 100644 --- a/servers/physics/body_sw.cpp +++ b/servers/physics/body_sw.cpp @@ -773,7 +773,7 @@ BodySW::BodySW() : active = true; mass = 1; - kinematic_safe_margin = 0.01; + kinematic_safe_margin = 0.001; //_inv_inertia=Transform(); _inv_mass = 1; bounce = 0; diff --git a/thirdparty/enet/enet/enet.h b/thirdparty/enet/enet/enet.h index 3900353c345..24d36647d92 100644 --- a/thirdparty/enet/enet/enet.h +++ b/thirdparty/enet/enet/enet.h @@ -13,7 +13,16 @@ extern "C" #include #include +// -- Godot start -- +#if 0 +#ifdef _WIN32 +#include "enet/win32.h" +#else +#include "enet/unix.h" +#endif +#endif #include "enet/godot.h" +// -- Godot end -- #include "enet/types.h" #include "enet/protocol.h" @@ -69,6 +78,7 @@ typedef enum _ENetSocketShutdown ENET_SOCKET_SHUTDOWN_READ_WRITE = 2 } ENetSocketShutdown; +#define ENET_HOST_ANY 0 #define ENET_HOST_BROADCAST 0xFFFFFFFFU #define ENET_PORT_ANY 0 @@ -82,13 +92,15 @@ typedef enum _ENetSocketShutdown * but not for enet_host_create. Once a server responds to a broadcast, the * address is updated from ENET_HOST_BROADCAST to the server's actual IP address. */ +// -- Godot start -- +#if 0 typedef struct _ENetAddress { - uint8_t host[16]; + enet_uint32 host; enet_uint16 port; - uint8_t wildcard; } ENetAddress; -#define enet_host_equal(host_a, host_b) (memcmp(&host_a, &host_b,16) == 0) +#endif +// -- Godot end -- /** * Packet flag bit constants. @@ -535,16 +547,6 @@ ENET_API int enet_address_set_host_ip (ENetAddress * address, const char * hostN */ ENET_API int enet_address_set_host (ENetAddress * address, const char * hostName); -/** Sets the host field in the address parameter from ip struct. - @param address destination to store resolved address - @param ip the ip struct to read from - @param size the size of the ip struct. - @retval 0 on success - @retval != 0 on failure - @returns the address of the given ip in address on success. -*/ -ENET_API void enet_address_set_ip(ENetAddress * address, const uint8_t * ip, size_t size); - /** Gives the printable form of the IP address specified in the address parameter. @param address address printed @param hostName destination for name, must not be NULL @@ -585,8 +587,6 @@ ENET_API void enet_host_channel_limit (ENetHost *, size_t); ENET_API void enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32); extern void enet_host_bandwidth_throttle (ENetHost *); extern enet_uint32 enet_host_random_seed (void); -ENET_API void enet_host_dtls_server_setup (ENetHost *, void *, void *); -ENET_API void enet_host_dtls_client_setup (ENetHost *, void *, uint8_t, const char *); ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *); ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8 * channelID); @@ -616,6 +616,10 @@ ENET_API size_t enet_range_coder_decompress (void *, const enet_uint8 *, size_t, extern size_t enet_protocol_command_size (enet_uint8); +// -- Godot start -- +#include "enet/godot_ext.h" +// -- Godot end -- + #ifdef __cplusplus } #endif diff --git a/thirdparty/enet/enet/godot.h b/thirdparty/enet/enet/godot.h index 4f25ea9c7b9..296b92763d5 100644 --- a/thirdparty/enet/enet/godot.h +++ b/thirdparty/enet/enet/godot.h @@ -69,4 +69,12 @@ typedef struct typedef void ENetSocketSet; +typedef struct _ENetAddress +{ + uint8_t host[16]; + uint16_t port; + uint8_t wildcard; +} ENetAddress; +#define enet_host_equal(host_a, host_b) (memcmp(&host_a, &host_b,16) == 0) + #endif /* __ENET_GODOT_H__ */ diff --git a/thirdparty/enet/enet/godot_ext.h b/thirdparty/enet/enet/godot_ext.h new file mode 100644 index 00000000000..84a23b1c85d --- /dev/null +++ b/thirdparty/enet/enet/godot_ext.h @@ -0,0 +1,18 @@ +#ifndef __ENET_GODOT_EXT_H__ +#define __ENET_GODOT_EXT_H__ + +/** Sets the host field in the address parameter from ip struct. + @param address destination to store resolved address + @param ip the ip struct to read from + @param size the size of the ip struct. + @retval 0 on success + @retval != 0 on failure + @returns the address of the given ip in address on success. +*/ +ENET_API void enet_address_set_ip(ENetAddress * address, const uint8_t * ip, size_t size); + +ENET_API void enet_host_dtls_server_setup (ENetHost *, void *, void *); +ENET_API void enet_host_dtls_client_setup (ENetHost *, void *, uint8_t, const char *); +ENET_API void enet_host_refuse_new_connections (ENetHost *, int); + +#endif // __ENET_GODOT_EXT_H__ diff --git a/thirdparty/enet/godot.cpp b/thirdparty/enet/godot.cpp index da3a86277b3..3975c1d86e9 100644 --- a/thirdparty/enet/godot.cpp +++ b/thirdparty/enet/godot.cpp @@ -51,6 +51,7 @@ public: virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port) = 0; virtual int set_option(ENetSocketOption p_option, int p_value) = 0; virtual void close() = 0; + virtual void set_refuse_new_connections(bool p_refuse) { /* Only used by dtls server */ } virtual ~ENetGodotSocket(){}; }; @@ -250,6 +251,10 @@ public: close(); } + void set_refuse_new_connections(bool p_refuse) { + udp_server->set_max_pending_connections(p_refuse ? 0 : 16); + } + Error bind(IP_Address p_ip, uint16_t p_port) { return udp_server->listen(p_port, p_ip); } @@ -269,6 +274,7 @@ public: } Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port) { + udp_server->poll(); // TODO limits? Maybe we can better enforce allowed connections! if (udp_server->is_connection_available()) { Ref udp = udp_server->take_connection(); @@ -409,6 +415,11 @@ void enet_host_dtls_client_setup(ENetHost *host, void *p_cert, uint8_t p_verify, memdelete(sock); } +void enet_host_refuse_new_connections(ENetHost *host, int p_refuse) { + ERR_FAIL_COND(!host->socket); + ((ENetGodotSocket *)host->socket)->set_refuse_new_connections(p_refuse); +} + int enet_socket_bind(ENetSocket socket, const ENetAddress *address) { IP_Address ip; diff --git a/thirdparty/enet/patches/dtls_support.patch b/thirdparty/enet/patches/dtls_support.patch deleted file mode 100644 index ce3480a8586..00000000000 --- a/thirdparty/enet/patches/dtls_support.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/thirdparty/enet/enet/enet.h b/thirdparty/enet/enet/enet.h -index 966e3a465d..ac7552adb2 100644 ---- a/thirdparty/enet/enet/enet.h -+++ b/thirdparty/enet/enet/enet.h -@@ -578,6 +578,8 @@ ENET_API void enet_host_channel_limit (ENetHost *, size_t); - ENET_API void enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32); - extern void enet_host_bandwidth_throttle (ENetHost *); - extern enet_uint32 enet_host_random_seed (void); -+ENET_API void enet_host_dtls_server_setup (ENetHost *, void *, void *); -+ENET_API void enet_host_dtls_client_setup (ENetHost *, void *, uint8_t, const char *); - - ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *); - ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8 * channelID); diff --git a/thirdparty/enet/patches/ipv6_support.patch b/thirdparty/enet/patches/godot.patch similarity index 61% rename from thirdparty/enet/patches/ipv6_support.patch rename to thirdparty/enet/patches/godot.patch index 1f79863645e..c8b4a5225da 100644 --- a/thirdparty/enet/patches/ipv6_support.patch +++ b/thirdparty/enet/patches/godot.patch @@ -1,61 +1,54 @@ diff --git a/thirdparty/enet/enet/enet.h b/thirdparty/enet/enet/enet.h -index 650b199ee5..246cbb0a62 100644 +index 54d91b5603..24d36647d9 100644 --- a/thirdparty/enet/enet/enet.h +++ b/thirdparty/enet/enet/enet.h -@@ -10,13 +10,10 @@ extern "C" +@@ -10,13 +10,19 @@ extern "C" { #endif +#include #include --#ifdef _WIN32 --#include "enet/win32.h" --#else --#include "enet/unix.h" --#endif ++// -- Godot start -- ++#if 0 + #ifdef _WIN32 + #include "enet/win32.h" + #else + #include "enet/unix.h" + #endif ++#endif +#include "enet/godot.h" ++// -- Godot end -- #include "enet/types.h" #include "enet/protocol.h" -@@ -72,7 +69,6 @@ typedef enum _ENetSocketShutdown - ENET_SOCKET_SHUTDOWN_READ_WRITE = 2 - } ENetSocketShutdown; - --#define ENET_HOST_ANY 0 - #define ENET_HOST_BROADCAST 0xFFFFFFFFU - #define ENET_PORT_ANY 0 - -@@ -88,9 +84,11 @@ typedef enum _ENetSocketShutdown +@@ -86,11 +92,15 @@ typedef enum _ENetSocketShutdown + * but not for enet_host_create. Once a server responds to a broadcast, the + * address is updated from ENET_HOST_BROADCAST to the server's actual IP address. */ ++// -- Godot start -- ++#if 0 typedef struct _ENetAddress { -- enet_uint32 host; -+ uint8_t host[16]; + enet_uint32 host; enet_uint16 port; -+ uint8_t wildcard; } ENetAddress; -+#define enet_host_equal(host_a, host_b) (memcmp(&host_a, &host_b,16) == 0) ++#endif ++// -- Godot end -- /** * Packet flag bit constants. -@@ -519,6 +517,16 @@ ENET_API int enet_socketset_select (ENetSocket, ENetSocketSet *, ENetSock - */ - ENET_API int enet_address_set_host (ENetAddress * address, const char * hostName); +@@ -606,6 +616,10 @@ ENET_API size_t enet_range_coder_decompress (void *, const enet_uint8 *, size_t, + + extern size_t enet_protocol_command_size (enet_uint8); -+/** Sets the host field in the address parameter from ip struct. -+ @param address destination to store resolved address -+ @param ip the ip struct to read from -+ @param size the size of the ip struct. -+ @retval 0 on success -+ @retval != 0 on failure -+ @returns the address of the given ip in address on success. -+*/ -+ENET_API void enet_address_set_ip(ENetAddress * address, const uint8_t * ip, size_t size); ++// -- Godot start -- ++#include "enet/godot_ext.h" ++// -- Godot end -- + - /** Gives the printable form of the IP address specified in the address parameter. - @param address address printed - @param hostName destination for name, must not be NULL + #ifdef __cplusplus + } + #endif diff --git a/thirdparty/enet/host.c b/thirdparty/enet/host.c index 3be6c0922c..fc4da4ca67 100644 --- a/thirdparty/enet/host.c @@ -70,10 +63,10 @@ index 3be6c0922c..fc4da4ca67 100644 host -> receivedData = NULL; host -> receivedDataLength = 0; diff --git a/thirdparty/enet/protocol.c b/thirdparty/enet/protocol.c -index 29d648732d..ab26886de4 100644 +index 0a60253173..fefc0e6f0a 100644 --- a/thirdparty/enet/protocol.c +++ b/thirdparty/enet/protocol.c -@@ -298,7 +298,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet +@@ -307,7 +307,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet } else if (currentPeer -> state != ENET_PEER_STATE_CONNECTING && @@ -82,7 +75,7 @@ index 29d648732d..ab26886de4 100644 { if (currentPeer -> address.port == host -> receivedAddress.port && currentPeer -> connectID == command -> connect.connectID) -@@ -1010,9 +1010,8 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) +@@ -1027,9 +1027,8 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE || @@ -94,7 +87,7 @@ index 29d648732d..ab26886de4 100644 (peer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID && sessionID != peer -> incomingSessionID)) return 0; -@@ -1054,7 +1053,7 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) +@@ -1071,7 +1070,7 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) if (peer != NULL) {