diff options
Diffstat (limited to 'core')
-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 |
5 files changed, 179 insertions, 44 deletions
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(); |