diff options
-rw-r--r-- | SConstruct | 3 | ||||
-rw-r--r-- | core/io/packet_peer_udp.cpp | 65 | ||||
-rw-r--r-- | core/io/packet_peer_udp.h | 7 | ||||
-rw-r--r-- | core/io/translation_loader_po.cpp | 22 | ||||
-rw-r--r-- | core/io/udp_server.cpp | 100 | ||||
-rw-r--r-- | core/io/udp_server.h | 29 | ||||
-rw-r--r-- | doc/classes/UDPServer.xml | 20 | ||||
-rw-r--r-- | modules/enet/networked_multiplayer_enet.cpp | 3 | ||||
-rw-r--r-- | modules/gdscript/gdscript.cpp | 62 | ||||
-rw-r--r-- | modules/gdscript/language_server/gdscript_extend_parser.cpp | 2 | ||||
-rw-r--r-- | scene/resources/world_2d.cpp | 2 | ||||
-rw-r--r-- | thirdparty/enet/enet/enet.h | 34 | ||||
-rw-r--r-- | thirdparty/enet/enet/godot.h | 8 | ||||
-rw-r--r-- | thirdparty/enet/enet/godot_ext.h | 18 | ||||
-rw-r--r-- | thirdparty/enet/godot.cpp | 11 | ||||
-rw-r--r-- | thirdparty/enet/patches/dtls_support.patch | 13 | ||||
-rw-r--r-- | thirdparty/enet/patches/godot.patch (renamed from thirdparty/enet/patches/ipv6_support.patch) | 71 |
17 files changed, 320 insertions, 150 deletions
diff --git a/SConstruct b/SConstruct index ef74ce7736..e7ca8b3030 100644 --- a/SConstruct +++ b/SConstruct @@ -72,7 +72,6 @@ env_base.disabled_modules = [] env_base.use_ptrcall = False env_base.module_version_string = "" env_base.msvc = False -env_base.stable_release = version.status == "stable" env_base.__class__.disable_module = methods.disable_module @@ -129,7 +128,7 @@ opts.Add("custom_modules", "A list of comma-separated directory paths containing opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", False)) opts.Add(BoolVariable("progress", "Show a progress indicator during compilation", True)) opts.Add(EnumVariable("warnings", "Level of compilation warnings", "all", ("extra", "all", "moderate", "no"))) -opts.Add(BoolVariable("werror", "Treat compiler warnings as errors", not env_base.stable_release)) +opts.Add(BoolVariable("werror", "Treat compiler warnings as errors", False)) opts.Add(BoolVariable("dev", "If yes, alias for verbose=yes warnings=extra werror=yes", False)) opts.Add("extra_suffix", "Custom extra suffix added to the base filename of all generated binary files", "") opts.Add(BoolVariable("vsproj", "Generate a Visual Studio solution", False)) diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp index 862fca96fc..e633a56d54 100644 --- a/core/io/packet_peer_udp.cpp +++ b/core/io/packet_peer_udp.cpp @@ -31,12 +31,14 @@ #include "packet_peer_udp.h" #include "core/io/ip.h" +#include "core/io/udp_server.h" void PacketPeerUDP::set_blocking_mode(bool p_enable) { blocking = 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); @@ -44,6 +46,7 @@ void PacketPeerUDP::set_broadcast_enabled(bool 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); @@ -58,6 +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); @@ -130,7 +134,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); @@ -186,26 +190,25 @@ Error PacketPeerUDP::listen(int p_port, const IP_Address &p_bind_address, int p_ return OK; } -Error PacketPeerUDP::connect_socket(Ref<NetSocket> 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<NetSocket> 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>(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); @@ -243,7 +246,11 @@ 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>(NetSocket::create()); + } else if (_sock.is_valid()) { _sock->close(); } rb.resize(16); @@ -262,6 +269,9 @@ Error PacketPeerUDP::_poll() { if (!_sock->is_open()) { return FAILED; } + if (udp_server) { + return OK; // Handled by UDPServer. + } Error err; int read; @@ -284,24 +294,29 @@ Error PacketPeerUDP::_poll() { return FAILED; } - if (rb.space_left() < read + 24) { + err = store_packet(ip, port, recv_buffer, read); #ifdef TOOLS_ENABLED + if (err != OK) { WARN_PRINT("Buffer full, dropping packets!"); -#endif - continue; } - - 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(); } diff --git a/core/io/packet_peer_udp.h b/core/io/packet_peer_udp.h index ad0a60f60d..9a44a1ebea 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 = false; bool blocking = true; bool broadcast = false; + UDPServer *udp_server = nullptr; Ref<NetSocket> _sock; static void _bind_methods(); @@ -72,7 +75,9 @@ public: Error wait(); bool is_listening() const; - Error connect_socket(Ref<NetSocket> p_sock); // Used by UDPServer + Error connect_shared_socket(Ref<NetSocket> 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 0e0a948953..11aeddee09 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -35,7 +35,6 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { enum Status { - STATUS_NONE, STATUS_READING_ID, STATUS_READING_STRING, @@ -56,6 +55,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { bool skip_this = false; bool skip_next = false; bool is_eof = false; + const String path = f->get_path(); while (!is_eof) { String l = f->get_line().strip_edges(); @@ -65,7 +65,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { if (is_eof && l.empty()) { if (status == STATUS_READING_ID) { memdelete(f); - ERR_FAIL_V_MSG(RES(), f->get_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; } @@ -74,7 +74,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { if (l.begins_with("msgid")) { if (status == STATUS_READING_ID) { memdelete(f); - ERR_FAIL_V_MSG(RES(), f->get_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 != "") { @@ -96,7 +96,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { if (l.begins_with("msgstr")) { if (status != STATUS_READING_ID) { memdelete(f); - ERR_FAIL_V_MSG(RES(), f->get_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(); @@ -111,7 +111,10 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { continue; //nothing to read or comment } - ERR_FAIL_COND_V_MSG(!l.begins_with("\"") || status == STATUS_NONE, RES(), f->get_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, ignoring escaped ones (\"). @@ -133,7 +136,10 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { escape_next = false; } - ERR_FAIL_COND_V_MSG(end_pos == -1, RES(), f->get_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(); @@ -147,7 +153,6 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { line++; } - f->close(); memdelete(f); if (status == STATUS_READING_STRING) { @@ -160,7 +165,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { } } - ERR_FAIL_COND_V_MSG(config == "", RES(), "No config found in file: " + f->get_path() + "."); + ERR_FAIL_COND_V_MSG(config == "", RES(), "No config found in file: " + path + "."); Vector<String> configs = config.split("\n"); for (int i = 0; i < configs.size(); i++) { @@ -197,7 +202,6 @@ RES TranslationLoaderPO::load(const String &p_path, const String &p_original_pat void TranslationLoaderPO::get_recognized_extensions(List<String> *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/udp_server.cpp b/core/io/udp_server.cpp index 1d329daf8b..acd15aadc6 100644 --- a/core/io/udp_server.cpp +++ b/core/io/udp_server.cpp @@ -32,10 +32,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<Peer>::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 { 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<Peer>::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<PacketPeerUDP> UDPServer::take_connection() { @@ -92,11 +156,20 @@ Ref<PacketPeerUDP> UDPServer::take_connection() { return conn; } - conn = Ref<PacketPeerUDP>(memnew(PacketPeerUDP)); - conn->connect_socket(_sock); - _sock = Ref<NetSocket>(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<Peer>::Element *E = peers.find(peer); + if (E) { + peers.erase(E); + } } void UDPServer::stop() { @@ -105,6 +178,19 @@ void UDPServer::stop() { } bind_port = 0; bind_address = IP_Address(); + List<Peer>::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 90bb82b62b..3175b09b19 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<Peer> peers; + List<Peer> pending; + int max_pending_connections = 16; + Ref<NetSocket> _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<PacketPeerUDP> take_connection(); void stop(); diff --git a/doc/classes/UDPServer.xml b/doc/classes/UDPServer.xml index f3c865c392..aabfed85f0 100644 --- a/doc/classes/UDPServer.xml +++ b/doc/classes/UDPServer.xml @@ -5,6 +5,7 @@ </brief_description> <description> 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 @@ <return type="bool"> </return> <description> - 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. </description> </method> <method name="is_listening" qualifiers="const"> @@ -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]. </description> </method> + <method name="poll"> + <return type="int" enum="Error"> + </return> + <description> + 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]. + </description> + </method> <method name="stop"> <return type="void"> </return> <description> - 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). </description> </method> <method name="take_connection"> <return type="PacketPeerUDP"> </return> <description> - 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]. </description> </method> </methods> + <members> + <member name="max_pending_connections" type="int" setter="set_max_pending_connections" getter="get_max_pending_connections" default="16"> + 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). + </member> + </members> <constants> </constants> </class> diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index 4e7698b67c..ed3924f2d2 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -640,6 +640,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/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 01af562327..632407c61f 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -2001,116 +2001,114 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b 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 330530be80..385d5dd7cb 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -62,7 +62,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/resources/world_2d.cpp b/scene/resources/world_2d.cpp index 1c753fdb91..d2bc2bea31 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -211,7 +211,7 @@ struct SpatialIndexer2D { List<VisibilityNotifier2D *> added; List<VisibilityNotifier2D *> 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) { //well you zoomed out a lot, it's your problem. To avoid freezing in the for loops below, we'll manually check cell by cell diff --git a/thirdparty/enet/enet/enet.h b/thirdparty/enet/enet/enet.h index 3900353c34..24d36647d9 100644 --- a/thirdparty/enet/enet/enet.h +++ b/thirdparty/enet/enet/enet.h @@ -13,7 +13,16 @@ extern "C" #include <stdint.h> #include <stdlib.h> +// -- 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 4f25ea9c7b..296b92763d 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 0000000000..84a23b1c85 --- /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 9fefa53e77..36b5131f80 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<PacketPeerUDP> 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 ce3480a858..0000000000 --- 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 index 1f79863645..c8b4a5225d 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 <stdint.h> #include <stdlib.h> --#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) { |