You've already forked godot
							
							
				mirror of
				https://github.com/godotengine/godot.git
				synced 2025-11-03 11:50:27 +00:00 
			
		
		
		
	Core: Add UNIX domain socket support
> [!NOTE] > > Later versions of Windows has support for `AF_UNIX`, so it could be > added.
This commit is contained in:
		@@ -121,7 +121,10 @@ void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, bool p_ignore_error_breaks, const Vector<String> &p_breakpoints, void (*p_allow_focus_steal_fn)()) {
 | 
			
		||||
	register_uri_handler("tcp://", RemoteDebuggerPeerTCP::create); // TCP is the default protocol. Platforms/modules can add more.
 | 
			
		||||
	register_uri_handler("tcp://", RemoteDebuggerPeerTCP::create_tcp); // TCP is the default protocol. Platforms/modules can add more.
 | 
			
		||||
#ifdef UNIX_ENABLED
 | 
			
		||||
	register_uri_handler("unix://", RemoteDebuggerPeerTCP::create_unix);
 | 
			
		||||
#endif
 | 
			
		||||
	if (p_uri.is_empty()) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
@@ -132,10 +135,10 @@ void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, bo
 | 
			
		||||
		OS::get_singleton()->initialize_debugging();
 | 
			
		||||
	} else if (p_uri.contains("://")) {
 | 
			
		||||
		const String proto = p_uri.substr(0, p_uri.find("://") + 3);
 | 
			
		||||
		if (!protocols.has(proto)) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		RemoteDebuggerPeer *peer = protocols[proto](p_uri);
 | 
			
		||||
		CreatePeerFunc *create_fn = protocols.getptr(proto);
 | 
			
		||||
		ERR_FAIL_NULL_MSG(create_fn, vformat("Invalid protocol: %s.", proto));
 | 
			
		||||
 | 
			
		||||
		RemoteDebuggerPeer *peer = (*create_fn)(p_uri);
 | 
			
		||||
		if (!peer) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -76,18 +76,19 @@ void RemoteDebuggerPeerTCP::close() {
 | 
			
		||||
	in_buf.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RemoteDebuggerPeerTCP::RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_tcp) {
 | 
			
		||||
RemoteDebuggerPeerTCP::RemoteDebuggerPeerTCP() {
 | 
			
		||||
	// This means remote debugger takes 16 MiB just because it exists...
 | 
			
		||||
	in_buf.resize((8 << 20) + 4); // 8 MiB should be way more than enough (need 4 extra bytes for encoding packet size).
 | 
			
		||||
	out_buf.resize(8 << 20); // 8 MiB should be way more than enough
 | 
			
		||||
	tcp_client = p_tcp;
 | 
			
		||||
	if (tcp_client.is_valid()) { // Attaching to an already connected stream.
 | 
			
		||||
		connected = true;
 | 
			
		||||
		running = true;
 | 
			
		||||
		thread.start(_thread_func, this);
 | 
			
		||||
	} else {
 | 
			
		||||
		tcp_client.instantiate();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RemoteDebuggerPeerTCP::RemoteDebuggerPeerTCP(Ref<StreamPeerSocket> p_stream) :
 | 
			
		||||
		RemoteDebuggerPeerTCP() {
 | 
			
		||||
	DEV_ASSERT(p_stream.is_valid());
 | 
			
		||||
	tcp_client = p_stream;
 | 
			
		||||
	connected = true;
 | 
			
		||||
	running = true;
 | 
			
		||||
	thread.start(_thread_func, this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RemoteDebuggerPeerTCP::~RemoteDebuggerPeerTCP() {
 | 
			
		||||
@@ -154,22 +155,10 @@ void RemoteDebuggerPeerTCP::_read_in() {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_port) {
 | 
			
		||||
	IPAddress ip;
 | 
			
		||||
	if (p_host.is_valid_ip_address()) {
 | 
			
		||||
		ip = p_host;
 | 
			
		||||
	} else {
 | 
			
		||||
		ip = IP::get_singleton()->resolve_hostname(p_host);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int port = p_port;
 | 
			
		||||
 | 
			
		||||
Error RemoteDebuggerPeerTCP::_try_connect(Ref<StreamPeerSocket> tcp_client) {
 | 
			
		||||
	const int tries = 6;
 | 
			
		||||
	const int waits[tries] = { 1, 10, 100, 1000, 1000, 1000 };
 | 
			
		||||
 | 
			
		||||
	Error err = tcp_client->connect_to_host(ip, port);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Remote Debugger: Unable to connect to host '%s:%d'.", p_host, port));
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < tries; i++) {
 | 
			
		||||
		tcp_client->poll();
 | 
			
		||||
		if (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED) {
 | 
			
		||||
@@ -186,9 +175,6 @@ Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_po
 | 
			
		||||
		ERR_PRINT(vformat("Remote Debugger: Unable to connect. Status: %s.", String::num_int64(tcp_client->get_status())));
 | 
			
		||||
		return FAILED;
 | 
			
		||||
	}
 | 
			
		||||
	connected = true;
 | 
			
		||||
	running = true;
 | 
			
		||||
	thread.start(_thread_func, this);
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -222,7 +208,7 @@ void RemoteDebuggerPeerTCP::_poll() {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create(const String &p_uri) {
 | 
			
		||||
RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create_tcp(const String &p_uri) {
 | 
			
		||||
	ERR_FAIL_COND_V(!p_uri.begins_with("tcp://"), nullptr);
 | 
			
		||||
 | 
			
		||||
	String debug_host = p_uri.replace("tcp://", "");
 | 
			
		||||
@@ -234,13 +220,30 @@ RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create(const String &p_uri) {
 | 
			
		||||
		debug_host = debug_host.substr(0, sep_pos);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	RemoteDebuggerPeerTCP *peer = memnew(RemoteDebuggerPeerTCP);
 | 
			
		||||
	Error err = peer->connect_to_host(debug_host, debug_port);
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		memdelete(peer);
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	IPAddress ip;
 | 
			
		||||
	if (debug_host.is_valid_ip_address()) {
 | 
			
		||||
		ip = debug_host;
 | 
			
		||||
	} else {
 | 
			
		||||
		ip = IP::get_singleton()->resolve_hostname(debug_host);
 | 
			
		||||
	}
 | 
			
		||||
	return peer;
 | 
			
		||||
 | 
			
		||||
	Ref<StreamPeerTCP> stream;
 | 
			
		||||
	stream.instantiate();
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(stream->connect_to_host(ip, debug_port) != OK, nullptr, vformat("Remote Debugger: Unable to connect to host '%s:%d'.", debug_host, debug_port));
 | 
			
		||||
	ERR_FAIL_COND_V(_try_connect(stream), nullptr);
 | 
			
		||||
	return memnew(RemoteDebuggerPeerTCP(stream));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create_unix(const String &p_uri) {
 | 
			
		||||
	ERR_FAIL_COND_V(!p_uri.begins_with("unix://"), nullptr);
 | 
			
		||||
 | 
			
		||||
	String debug_path = p_uri.replace("unix://", "");
 | 
			
		||||
	Ref<StreamPeerUDS> stream;
 | 
			
		||||
	stream.instantiate();
 | 
			
		||||
	Error err = stream->connect_to_host(debug_path);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err != OK && err != ERR_BUSY, nullptr, vformat("Remote Debugger: Unable to connect to socket path '%s'.", debug_path));
 | 
			
		||||
	ERR_FAIL_COND_V(_try_connect(stream), nullptr);
 | 
			
		||||
	return memnew(RemoteDebuggerPeerTCP(stream));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RemoteDebuggerPeer::RemoteDebuggerPeer() {
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "core/io/stream_peer_tcp.h"
 | 
			
		||||
#include "core/io/stream_peer_uds.h"
 | 
			
		||||
#include "core/object/ref_counted.h"
 | 
			
		||||
#include "core/os/mutex.h"
 | 
			
		||||
#include "core/os/thread.h"
 | 
			
		||||
@@ -59,7 +60,7 @@ class RemoteDebuggerPeerTCP : public RemoteDebuggerPeer {
 | 
			
		||||
	GDSOFTCLASS(RemoteDebuggerPeerTCP, RemoteDebuggerPeer);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Ref<StreamPeerTCP> tcp_client;
 | 
			
		||||
	Ref<StreamPeerSocket> tcp_client;
 | 
			
		||||
	Mutex mutex;
 | 
			
		||||
	Thread thread;
 | 
			
		||||
	List<Array> in_queue;
 | 
			
		||||
@@ -78,11 +79,11 @@ private:
 | 
			
		||||
	void _poll();
 | 
			
		||||
	void _write_out();
 | 
			
		||||
	void _read_in();
 | 
			
		||||
	static Error _try_connect(Ref<StreamPeerSocket> p_stream);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static RemoteDebuggerPeer *create(const String &p_uri);
 | 
			
		||||
 | 
			
		||||
	Error connect_to_host(const String &p_host, uint16_t p_port);
 | 
			
		||||
	static RemoteDebuggerPeer *create_tcp(const String &p_uri);
 | 
			
		||||
	static RemoteDebuggerPeer *create_unix(const String &p_uri);
 | 
			
		||||
 | 
			
		||||
	bool is_peer_connected() override;
 | 
			
		||||
	int get_max_message_size() const override;
 | 
			
		||||
@@ -92,6 +93,7 @@ public:
 | 
			
		||||
	void poll() override;
 | 
			
		||||
	void close() override;
 | 
			
		||||
 | 
			
		||||
	RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_stream = Ref<StreamPeerTCP>());
 | 
			
		||||
	RemoteDebuggerPeerTCP(Ref<StreamPeerSocket> p_stream);
 | 
			
		||||
	RemoteDebuggerPeerTCP();
 | 
			
		||||
	~RemoteDebuggerPeerTCP();
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -54,21 +54,61 @@ public:
 | 
			
		||||
		TYPE_UDP,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	virtual Error open(Type p_type, IP::Type &ip_type) = 0;
 | 
			
		||||
	enum class Family {
 | 
			
		||||
		NONE,
 | 
			
		||||
		INET,
 | 
			
		||||
		UNIX,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class Address {
 | 
			
		||||
		Family _family = Family::NONE;
 | 
			
		||||
		CharString _path;
 | 
			
		||||
		IPAddress _ip;
 | 
			
		||||
		uint16_t _port = 0;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		_FORCE_INLINE_ Family get_family() const { return _family; }
 | 
			
		||||
		_FORCE_INLINE_ bool is_inet() const { return _family == Family::INET; }
 | 
			
		||||
		_FORCE_INLINE_ bool is_unix() const { return _family == Family::UNIX; }
 | 
			
		||||
		_FORCE_INLINE_ bool is_valid() const { return is_inet() || is_unix(); }
 | 
			
		||||
 | 
			
		||||
		_FORCE_INLINE_ const IPAddress &ip() const { return _ip; }
 | 
			
		||||
		_FORCE_INLINE_ const uint16_t &port() const { return _port; }
 | 
			
		||||
 | 
			
		||||
		_FORCE_INLINE_ const CharString &get_path() const { return _path; }
 | 
			
		||||
 | 
			
		||||
		Address() {}
 | 
			
		||||
 | 
			
		||||
		Address(const IPAddress &p_addr, uint16_t p_port) :
 | 
			
		||||
				_family(Family::INET) {
 | 
			
		||||
			_ip = p_addr;
 | 
			
		||||
			_port = p_port;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Address(const String &p_path) :
 | 
			
		||||
				_family(Family::UNIX), _path(p_path.utf8()) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Address(const CharString &p_path) :
 | 
			
		||||
				_family(Family::UNIX), _path(p_path) {
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	virtual Error open(Family p_family, Type p_type, IP::Type &r_ip_type) = 0;
 | 
			
		||||
	virtual void close() = 0;
 | 
			
		||||
	virtual Error bind(IPAddress p_addr, uint16_t p_port) = 0;
 | 
			
		||||
	virtual Error bind(Address p_addr) = 0;
 | 
			
		||||
	virtual Error listen(int p_max_pending) = 0;
 | 
			
		||||
	virtual Error connect_to_host(IPAddress p_addr, uint16_t p_port) = 0;
 | 
			
		||||
	virtual Error connect_to_host(Address p_addr) = 0;
 | 
			
		||||
	virtual Error poll(PollType p_type, int timeout) const = 0;
 | 
			
		||||
	virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) = 0;
 | 
			
		||||
	virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false) = 0;
 | 
			
		||||
	virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) = 0;
 | 
			
		||||
	virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) = 0;
 | 
			
		||||
	virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port) = 0;
 | 
			
		||||
	virtual Ref<NetSocket> accept(Address &r_addr) = 0;
 | 
			
		||||
 | 
			
		||||
	virtual bool is_open() const = 0;
 | 
			
		||||
	virtual int get_available_bytes() const = 0;
 | 
			
		||||
	virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const = 0;
 | 
			
		||||
	virtual Error get_socket_address(Address *r_addr) const = 0;
 | 
			
		||||
 | 
			
		||||
	virtual Error set_broadcasting_enabled(bool p_enabled) = 0; // Returns OK if the socket option has been set successfully.
 | 
			
		||||
	virtual void set_blocking_enabled(bool p_enabled) = 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,7 @@ Error PacketPeerUDP::join_multicast_group(IPAddress p_multi_address, const Strin
 | 
			
		||||
 | 
			
		||||
	if (!_sock->is_open()) {
 | 
			
		||||
		IP::Type ip_type = p_multi_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
 | 
			
		||||
		Error err = _sock->open(NetSocket::TYPE_UDP, ip_type);
 | 
			
		||||
		Error err = _sock->open(NetSocket::Family::INET, NetSocket::TYPE_UDP, ip_type);
 | 
			
		||||
		ERR_FAIL_COND_V(err != OK, err);
 | 
			
		||||
		_sock->set_blocking_enabled(false);
 | 
			
		||||
		_sock->set_broadcasting_enabled(broadcast);
 | 
			
		||||
@@ -141,7 +141,7 @@ Error PacketPeerUDP::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
 | 
			
		||||
 | 
			
		||||
	if (!_sock->is_open()) {
 | 
			
		||||
		IP::Type ip_type = peer_addr.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
 | 
			
		||||
		err = _sock->open(NetSocket::TYPE_UDP, ip_type);
 | 
			
		||||
		err = _sock->open(NetSocket::Family::INET, NetSocket::TYPE_UDP, ip_type);
 | 
			
		||||
		ERR_FAIL_COND_V(err != OK, err);
 | 
			
		||||
		_sock->set_blocking_enabled(false);
 | 
			
		||||
		_sock->set_broadcasting_enabled(broadcast);
 | 
			
		||||
@@ -186,7 +186,7 @@ Error PacketPeerUDP::bind(int p_port, const IPAddress &p_bind_address, int p_rec
 | 
			
		||||
		ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = _sock->open(NetSocket::TYPE_UDP, ip_type);
 | 
			
		||||
	err = _sock->open(NetSocket::Family::INET, NetSocket::TYPE_UDP, ip_type);
 | 
			
		||||
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		return ERR_CANT_CREATE;
 | 
			
		||||
@@ -194,7 +194,8 @@ Error PacketPeerUDP::bind(int p_port, const IPAddress &p_bind_address, int p_rec
 | 
			
		||||
 | 
			
		||||
	_sock->set_blocking_enabled(false);
 | 
			
		||||
	_sock->set_broadcasting_enabled(broadcast);
 | 
			
		||||
	err = _sock->bind(p_bind_address, p_port);
 | 
			
		||||
	NetSocket::Address addr(p_bind_address, p_port);
 | 
			
		||||
	err = _sock->bind(addr);
 | 
			
		||||
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		_sock->close();
 | 
			
		||||
@@ -231,12 +232,13 @@ Error PacketPeerUDP::connect_to_host(const IPAddress &p_host, int p_port) {
 | 
			
		||||
 | 
			
		||||
	if (!_sock->is_open()) {
 | 
			
		||||
		IP::Type ip_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
 | 
			
		||||
		err = _sock->open(NetSocket::TYPE_UDP, ip_type);
 | 
			
		||||
		err = _sock->open(NetSocket::Family::INET, NetSocket::TYPE_UDP, ip_type);
 | 
			
		||||
		ERR_FAIL_COND_V(err != OK, ERR_CANT_OPEN);
 | 
			
		||||
		_sock->set_blocking_enabled(false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = _sock->connect_to_host(p_host, p_port);
 | 
			
		||||
	NetSocket::Address addr(p_host, p_port);
 | 
			
		||||
	err = _sock->connect_to_host(addr);
 | 
			
		||||
 | 
			
		||||
	// I see no reason why we should get ERR_BUSY (wouldblock/eagain) here.
 | 
			
		||||
	// This is UDP, so connect is only used to tell the OS to which socket
 | 
			
		||||
@@ -345,9 +347,9 @@ int PacketPeerUDP::get_packet_port() const {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int PacketPeerUDP::get_local_port() const {
 | 
			
		||||
	uint16_t local_port;
 | 
			
		||||
	_sock->get_socket_address(nullptr, &local_port);
 | 
			
		||||
	return local_port;
 | 
			
		||||
	NetSocket::Address addr;
 | 
			
		||||
	_sock->get_socket_address(&addr);
 | 
			
		||||
	return addr.port();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PacketPeerUDP::set_dest_address(const IPAddress &p_address, int p_port) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										90
									
								
								core/io/socket_server.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								core/io/socket_server.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*  socket_server.cpp                                                     */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
/*                        https://godotengine.org                         */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
 | 
			
		||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* Permission is hereby granted, free of charge, to any person obtaining  */
 | 
			
		||||
/* a copy of this software and associated documentation files (the        */
 | 
			
		||||
/* "Software"), to deal in the Software without restriction, including    */
 | 
			
		||||
/* without limitation the rights to use, copy, modify, merge, publish,    */
 | 
			
		||||
/* distribute, sublicense, and/or sell copies of the Software, and to     */
 | 
			
		||||
/* permit persons to whom the Software is furnished to do so, subject to  */
 | 
			
		||||
/* the following conditions:                                              */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* The above copyright notice and this permission notice shall be         */
 | 
			
		||||
/* included in all copies or substantial portions of the Software.        */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
 | 
			
		||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
 | 
			
		||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
 | 
			
		||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
 | 
			
		||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
 | 
			
		||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
 | 
			
		||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "socket_server.h"
 | 
			
		||||
 | 
			
		||||
void SocketServer::_bind_methods() {
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("is_connection_available"), &SocketServer::is_connection_available);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("is_listening"), &SocketServer::is_listening);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("stop"), &SocketServer::stop);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("take_socket_connection"), &SocketServer::take_socket_connection);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error SocketServer::_listen(const NetSocket::Address &p_addr) {
 | 
			
		||||
	DEV_ASSERT(_sock.is_valid());
 | 
			
		||||
	DEV_ASSERT(_sock->is_open());
 | 
			
		||||
 | 
			
		||||
	_sock->set_blocking_enabled(false);
 | 
			
		||||
	Error err = _sock->bind(p_addr);
 | 
			
		||||
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		_sock->close();
 | 
			
		||||
		return ERR_ALREADY_IN_USE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = _sock->listen(MAX_PENDING_CONNECTIONS);
 | 
			
		||||
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		_sock->close();
 | 
			
		||||
		return FAILED;
 | 
			
		||||
	}
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool SocketServer::is_listening() const {
 | 
			
		||||
	ERR_FAIL_COND_V(_sock.is_null(), false);
 | 
			
		||||
 | 
			
		||||
	return _sock->is_open();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool SocketServer::is_connection_available() const {
 | 
			
		||||
	ERR_FAIL_COND_V(_sock.is_null(), false);
 | 
			
		||||
 | 
			
		||||
	if (!_sock->is_open()) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Error err = _sock->poll(NetSocket::POLL_TYPE_IN, 0);
 | 
			
		||||
	return (err == OK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SocketServer::stop() {
 | 
			
		||||
	if (_sock.is_valid()) {
 | 
			
		||||
		_sock->close();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SocketServer::SocketServer() :
 | 
			
		||||
		_sock(NetSocket::create()) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SocketServer::~SocketServer() {
 | 
			
		||||
	stop();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										77
									
								
								core/io/socket_server.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								core/io/socket_server.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,77 @@
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*  socket_server.h                                                       */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
/*                        https://godotengine.org                         */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
 | 
			
		||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* Permission is hereby granted, free of charge, to any person obtaining  */
 | 
			
		||||
/* a copy of this software and associated documentation files (the        */
 | 
			
		||||
/* "Software"), to deal in the Software without restriction, including    */
 | 
			
		||||
/* without limitation the rights to use, copy, modify, merge, publish,    */
 | 
			
		||||
/* distribute, sublicense, and/or sell copies of the Software, and to     */
 | 
			
		||||
/* permit persons to whom the Software is furnished to do so, subject to  */
 | 
			
		||||
/* the following conditions:                                              */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* The above copyright notice and this permission notice shall be         */
 | 
			
		||||
/* included in all copies or substantial portions of the Software.        */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
 | 
			
		||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
 | 
			
		||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
 | 
			
		||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
 | 
			
		||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
 | 
			
		||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
 | 
			
		||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "core/io/net_socket.h"
 | 
			
		||||
#include "core/io/stream_peer_socket.h"
 | 
			
		||||
 | 
			
		||||
class SocketServer : public RefCounted {
 | 
			
		||||
	GDCLASS(SocketServer, RefCounted);
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	enum {
 | 
			
		||||
		MAX_PENDING_CONNECTIONS = 8,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Ref<NetSocket> _sock;
 | 
			
		||||
	static void _bind_methods();
 | 
			
		||||
 | 
			
		||||
	Error _listen(const NetSocket::Address &p_addr);
 | 
			
		||||
 | 
			
		||||
	template <typename T>
 | 
			
		||||
	Ref<T> _take_connection() {
 | 
			
		||||
		Ref<T> conn;
 | 
			
		||||
		if (!is_connection_available()) {
 | 
			
		||||
			return conn;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Ref<NetSocket> ns;
 | 
			
		||||
		NetSocket::Address addr;
 | 
			
		||||
		ns = _sock->accept(addr);
 | 
			
		||||
		if (ns.is_null()) {
 | 
			
		||||
			return conn;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		conn.instantiate();
 | 
			
		||||
		conn->accept_socket(ns, addr);
 | 
			
		||||
		return conn;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	bool is_listening() const;
 | 
			
		||||
	bool is_connection_available() const;
 | 
			
		||||
	virtual Ref<StreamPeerSocket> take_socket_connection() = 0;
 | 
			
		||||
 | 
			
		||||
	void stop(); // Stop listening
 | 
			
		||||
 | 
			
		||||
	SocketServer();
 | 
			
		||||
	~SocketServer();
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										52
									
								
								core/io/stream_peer_socket.compat.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								core/io/stream_peer_socket.compat.inc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*  stream_peer_socket.compat.inc                                         */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
/*                        https://godotengine.org                         */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
 | 
			
		||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* Permission is hereby granted, free of charge, to any person obtaining  */
 | 
			
		||||
/* a copy of this software and associated documentation files (the        */
 | 
			
		||||
/* "Software"), to deal in the Software without restriction, including    */
 | 
			
		||||
/* without limitation the rights to use, copy, modify, merge, publish,    */
 | 
			
		||||
/* distribute, sublicense, and/or sell copies of the Software, and to     */
 | 
			
		||||
/* permit persons to whom the Software is furnished to do so, subject to  */
 | 
			
		||||
/* the following conditions:                                              */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* The above copyright notice and this permission notice shall be         */
 | 
			
		||||
/* included in all copies or substantial portions of the Software.        */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
 | 
			
		||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
 | 
			
		||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
 | 
			
		||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
 | 
			
		||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
 | 
			
		||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
 | 
			
		||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
 | 
			
		||||
namespace compat::StreamPeerTCP {
 | 
			
		||||
enum class Status {
 | 
			
		||||
	STATUS_NONE = StreamPeerSocket::STATUS_NONE,
 | 
			
		||||
	STATUS_CONNECTING = StreamPeerSocket::STATUS_CONNECTING,
 | 
			
		||||
	STATUS_CONNECTED = StreamPeerSocket::STATUS_CONNECTED,
 | 
			
		||||
	STATUS_ERROR = StreamPeerSocket::STATUS_ERROR,
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VARIANT_ENUM_CAST(compat::StreamPeerTCP::Status);
 | 
			
		||||
 | 
			
		||||
compat::StreamPeerTCP::Status StreamPeerSocket::_get_status_compat_107954() const {
 | 
			
		||||
	return (compat::StreamPeerTCP::Status)get_status();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StreamPeerSocket::_bind_compatibility_methods() {
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("get_status"), &StreamPeerSocket::_get_status_compat_107954);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										236
									
								
								core/io/stream_peer_socket.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								core/io/stream_peer_socket.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,236 @@
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*  stream_peer_socket.cpp                                                */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
/*                        https://godotengine.org                         */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
 | 
			
		||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* Permission is hereby granted, free of charge, to any person obtaining  */
 | 
			
		||||
/* a copy of this software and associated documentation files (the        */
 | 
			
		||||
/* "Software"), to deal in the Software without restriction, including    */
 | 
			
		||||
/* without limitation the rights to use, copy, modify, merge, publish,    */
 | 
			
		||||
/* distribute, sublicense, and/or sell copies of the Software, and to     */
 | 
			
		||||
/* permit persons to whom the Software is furnished to do so, subject to  */
 | 
			
		||||
/* the following conditions:                                              */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* The above copyright notice and this permission notice shall be         */
 | 
			
		||||
/* included in all copies or substantial portions of the Software.        */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
 | 
			
		||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
 | 
			
		||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
 | 
			
		||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
 | 
			
		||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
 | 
			
		||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
 | 
			
		||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "stream_peer_socket.h"
 | 
			
		||||
#include "stream_peer_socket.compat.inc"
 | 
			
		||||
 | 
			
		||||
Error StreamPeerSocket::poll() {
 | 
			
		||||
	if (status == STATUS_CONNECTED) {
 | 
			
		||||
		Error err;
 | 
			
		||||
		err = _sock->poll(NetSocket::POLL_TYPE_IN, 0);
 | 
			
		||||
		if (err == OK) {
 | 
			
		||||
			// FIN received
 | 
			
		||||
			if (_sock->get_available_bytes() == 0) {
 | 
			
		||||
				disconnect_from_host();
 | 
			
		||||
				return OK;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// Also poll write
 | 
			
		||||
		err = _sock->poll(NetSocket::POLL_TYPE_IN_OUT, 0);
 | 
			
		||||
		if (err != OK && err != ERR_BUSY) {
 | 
			
		||||
			// Got an error
 | 
			
		||||
			disconnect_from_host();
 | 
			
		||||
			status = STATUS_ERROR;
 | 
			
		||||
			return err;
 | 
			
		||||
		}
 | 
			
		||||
		return OK;
 | 
			
		||||
	} else if (status != STATUS_CONNECTING) {
 | 
			
		||||
		return OK;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Error err = _sock->connect_to_host(peer_address);
 | 
			
		||||
 | 
			
		||||
	if (err == OK) {
 | 
			
		||||
		status = STATUS_CONNECTED;
 | 
			
		||||
		return OK;
 | 
			
		||||
	} else if (err == ERR_BUSY) {
 | 
			
		||||
		// Check for connect timeout
 | 
			
		||||
		if (OS::get_singleton()->get_ticks_msec() > timeout) {
 | 
			
		||||
			disconnect_from_host();
 | 
			
		||||
			status = STATUS_ERROR;
 | 
			
		||||
			return ERR_CONNECTION_ERROR;
 | 
			
		||||
		}
 | 
			
		||||
		// Still trying to connect
 | 
			
		||||
		return OK;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	disconnect_from_host();
 | 
			
		||||
	status = STATUS_ERROR;
 | 
			
		||||
	return ERR_CONNECTION_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerSocket::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block) {
 | 
			
		||||
	ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
 | 
			
		||||
 | 
			
		||||
	if (status != STATUS_CONNECTED) {
 | 
			
		||||
		return FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Error err;
 | 
			
		||||
	int data_to_send = p_bytes;
 | 
			
		||||
	const uint8_t *offset = p_data;
 | 
			
		||||
	int total_sent = 0;
 | 
			
		||||
 | 
			
		||||
	while (data_to_send) {
 | 
			
		||||
		int sent_amount = 0;
 | 
			
		||||
		err = _sock->send(offset, data_to_send, sent_amount);
 | 
			
		||||
 | 
			
		||||
		if (err != OK) {
 | 
			
		||||
			if (err != ERR_BUSY) {
 | 
			
		||||
				disconnect_from_host();
 | 
			
		||||
				return FAILED;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (!p_block) {
 | 
			
		||||
				r_sent = total_sent;
 | 
			
		||||
				return OK;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Block and wait for the socket to accept more data
 | 
			
		||||
			err = _sock->poll(NetSocket::POLL_TYPE_OUT, -1);
 | 
			
		||||
			if (err != OK) {
 | 
			
		||||
				disconnect_from_host();
 | 
			
		||||
				return FAILED;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			data_to_send -= sent_amount;
 | 
			
		||||
			offset += sent_amount;
 | 
			
		||||
			total_sent += sent_amount;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r_sent = total_sent;
 | 
			
		||||
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerSocket::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block) {
 | 
			
		||||
	if (status != STATUS_CONNECTED) {
 | 
			
		||||
		return FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Error err;
 | 
			
		||||
	int to_read = p_bytes;
 | 
			
		||||
	int total_read = 0;
 | 
			
		||||
	r_received = 0;
 | 
			
		||||
 | 
			
		||||
	while (to_read) {
 | 
			
		||||
		int read = 0;
 | 
			
		||||
		err = _sock->recv(p_buffer + total_read, to_read, read);
 | 
			
		||||
 | 
			
		||||
		if (err != OK) {
 | 
			
		||||
			if (err != ERR_BUSY) {
 | 
			
		||||
				disconnect_from_host();
 | 
			
		||||
				return FAILED;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (!p_block) {
 | 
			
		||||
				r_received = total_read;
 | 
			
		||||
				return OK;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			err = _sock->poll(NetSocket::POLL_TYPE_IN, -1);
 | 
			
		||||
 | 
			
		||||
			if (err != OK) {
 | 
			
		||||
				disconnect_from_host();
 | 
			
		||||
				return FAILED;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		} else if (read == 0) {
 | 
			
		||||
			disconnect_from_host();
 | 
			
		||||
			r_received = total_read;
 | 
			
		||||
			return ERR_FILE_EOF;
 | 
			
		||||
 | 
			
		||||
		} else {
 | 
			
		||||
			to_read -= read;
 | 
			
		||||
			total_read += read;
 | 
			
		||||
 | 
			
		||||
			if (!p_block) {
 | 
			
		||||
				r_received = total_read;
 | 
			
		||||
				return OK;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r_received = total_read;
 | 
			
		||||
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
StreamPeerSocket::Status StreamPeerSocket::get_status() const {
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StreamPeerSocket::disconnect_from_host() {
 | 
			
		||||
	if (_sock.is_valid() && _sock->is_open()) {
 | 
			
		||||
		_sock->close();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout = 0;
 | 
			
		||||
	status = STATUS_NONE;
 | 
			
		||||
	peer_address = NetSocket::Address();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerSocket::wait(NetSocket::PollType p_type, int p_timeout) {
 | 
			
		||||
	ERR_FAIL_COND_V(_sock.is_null() || !_sock->is_open(), ERR_UNAVAILABLE);
 | 
			
		||||
	return _sock->poll(p_type, p_timeout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerSocket::put_data(const uint8_t *p_data, int p_bytes) {
 | 
			
		||||
	int total;
 | 
			
		||||
	return write(p_data, p_bytes, total, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerSocket::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
 | 
			
		||||
	return write(p_data, p_bytes, r_sent, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerSocket::get_data(uint8_t *p_buffer, int p_bytes) {
 | 
			
		||||
	int total;
 | 
			
		||||
	return read(p_buffer, p_bytes, total, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerSocket::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
 | 
			
		||||
	return read(p_buffer, p_bytes, r_received, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int StreamPeerSocket::get_available_bytes() const {
 | 
			
		||||
	ERR_FAIL_COND_V(_sock.is_null(), -1);
 | 
			
		||||
	return _sock->get_available_bytes();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StreamPeerSocket::_bind_methods() {
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSocket::poll);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerSocket::get_status);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("disconnect_from_host"), &StreamPeerSocket::disconnect_from_host);
 | 
			
		||||
 | 
			
		||||
	BIND_ENUM_CONSTANT(STATUS_NONE);
 | 
			
		||||
	BIND_ENUM_CONSTANT(STATUS_CONNECTING);
 | 
			
		||||
	BIND_ENUM_CONSTANT(STATUS_CONNECTED);
 | 
			
		||||
	BIND_ENUM_CONSTANT(STATUS_ERROR);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
StreamPeerSocket::StreamPeerSocket() :
 | 
			
		||||
		_sock(NetSocket::create()) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
StreamPeerSocket::~StreamPeerSocket() {
 | 
			
		||||
	disconnect_from_host();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										93
									
								
								core/io/stream_peer_socket.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								core/io/stream_peer_socket.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*  stream_peer_socket.h                                                  */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
/*                        https://godotengine.org                         */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
 | 
			
		||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* Permission is hereby granted, free of charge, to any person obtaining  */
 | 
			
		||||
/* a copy of this software and associated documentation files (the        */
 | 
			
		||||
/* "Software"), to deal in the Software without restriction, including    */
 | 
			
		||||
/* without limitation the rights to use, copy, modify, merge, publish,    */
 | 
			
		||||
/* distribute, sublicense, and/or sell copies of the Software, and to     */
 | 
			
		||||
/* permit persons to whom the Software is furnished to do so, subject to  */
 | 
			
		||||
/* the following conditions:                                              */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* The above copyright notice and this permission notice shall be         */
 | 
			
		||||
/* included in all copies or substantial portions of the Software.        */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
 | 
			
		||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
 | 
			
		||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
 | 
			
		||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
 | 
			
		||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
 | 
			
		||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
 | 
			
		||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "core/io/net_socket.h"
 | 
			
		||||
#include "core/io/stream_peer.h"
 | 
			
		||||
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
namespace compat::StreamPeerTCP {
 | 
			
		||||
enum class Status;
 | 
			
		||||
} //namespace compat::StreamPeerTCP
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
class StreamPeerSocket : public StreamPeer {
 | 
			
		||||
	GDCLASS(StreamPeerSocket, StreamPeer);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	enum Status {
 | 
			
		||||
		STATUS_NONE,
 | 
			
		||||
		STATUS_CONNECTING,
 | 
			
		||||
		STATUS_CONNECTED,
 | 
			
		||||
		STATUS_ERROR,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
	compat::StreamPeerTCP::Status _get_status_compat_107954() const;
 | 
			
		||||
	static void _bind_compatibility_methods();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	Ref<NetSocket> _sock;
 | 
			
		||||
	uint64_t timeout = 0;
 | 
			
		||||
	Status status = STATUS_NONE;
 | 
			
		||||
	NetSocket::Address peer_address;
 | 
			
		||||
 | 
			
		||||
	Error write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block);
 | 
			
		||||
	Error read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block);
 | 
			
		||||
 | 
			
		||||
	static void _bind_methods();
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	virtual void accept_socket(Ref<NetSocket> p_sock, const NetSocket::Address &p_addr) = 0;
 | 
			
		||||
 | 
			
		||||
	void disconnect_from_host();
 | 
			
		||||
 | 
			
		||||
	int get_available_bytes() const override;
 | 
			
		||||
	Status get_status() const;
 | 
			
		||||
 | 
			
		||||
	// Poll socket updating its state.
 | 
			
		||||
	Error poll();
 | 
			
		||||
 | 
			
		||||
	// Wait or check for writable, readable.
 | 
			
		||||
	Error wait(NetSocket::PollType p_type, int p_timeout = 0);
 | 
			
		||||
 | 
			
		||||
	// Read/Write from StreamPeer
 | 
			
		||||
	Error put_data(const uint8_t *p_data, int p_bytes) override;
 | 
			
		||||
	Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override;
 | 
			
		||||
	Error get_data(uint8_t *p_buffer, int p_bytes) override;
 | 
			
		||||
	Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override;
 | 
			
		||||
 | 
			
		||||
	StreamPeerSocket();
 | 
			
		||||
	virtual ~StreamPeerSocket();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
VARIANT_ENUM_CAST(StreamPeerSocket::Status);
 | 
			
		||||
@@ -32,60 +32,14 @@
 | 
			
		||||
 | 
			
		||||
#include "core/config/project_settings.h"
 | 
			
		||||
 | 
			
		||||
Error StreamPeerTCP::poll() {
 | 
			
		||||
	if (status == STATUS_CONNECTED) {
 | 
			
		||||
		Error err;
 | 
			
		||||
		err = _sock->poll(NetSocket::POLL_TYPE_IN, 0);
 | 
			
		||||
		if (err == OK) {
 | 
			
		||||
			// FIN received
 | 
			
		||||
			if (_sock->get_available_bytes() == 0) {
 | 
			
		||||
				disconnect_from_host();
 | 
			
		||||
				return OK;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// Also poll write
 | 
			
		||||
		err = _sock->poll(NetSocket::POLL_TYPE_IN_OUT, 0);
 | 
			
		||||
		if (err != OK && err != ERR_BUSY) {
 | 
			
		||||
			// Got an error
 | 
			
		||||
			disconnect_from_host();
 | 
			
		||||
			status = STATUS_ERROR;
 | 
			
		||||
			return err;
 | 
			
		||||
		}
 | 
			
		||||
		return OK;
 | 
			
		||||
	} else if (status != STATUS_CONNECTING) {
 | 
			
		||||
		return OK;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Error err = _sock->connect_to_host(peer_host, peer_port);
 | 
			
		||||
 | 
			
		||||
	if (err == OK) {
 | 
			
		||||
		status = STATUS_CONNECTED;
 | 
			
		||||
		return OK;
 | 
			
		||||
	} else if (err == ERR_BUSY) {
 | 
			
		||||
		// Check for connect timeout
 | 
			
		||||
		if (OS::get_singleton()->get_ticks_msec() > timeout) {
 | 
			
		||||
			disconnect_from_host();
 | 
			
		||||
			status = STATUS_ERROR;
 | 
			
		||||
			return ERR_CONNECTION_ERROR;
 | 
			
		||||
		}
 | 
			
		||||
		// Still trying to connect
 | 
			
		||||
		return OK;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	disconnect_from_host();
 | 
			
		||||
	status = STATUS_ERROR;
 | 
			
		||||
	return ERR_CONNECTION_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, IPAddress p_host, uint16_t p_port) {
 | 
			
		||||
void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, const NetSocket::Address &p_addr) {
 | 
			
		||||
	_sock = p_sock;
 | 
			
		||||
	_sock->set_blocking_enabled(false);
 | 
			
		||||
 | 
			
		||||
	timeout = OS::get_singleton()->get_ticks_msec() + (((uint64_t)GLOBAL_GET("network/limits/tcp/connect_timeout_seconds")) * 1000);
 | 
			
		||||
	status = STATUS_CONNECTED;
 | 
			
		||||
 | 
			
		||||
	peer_host = p_host;
 | 
			
		||||
	peer_port = p_port;
 | 
			
		||||
	peer_address = p_addr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerTCP::bind(int p_port, const IPAddress &p_host) {
 | 
			
		||||
@@ -97,12 +51,13 @@ Error StreamPeerTCP::bind(int p_port, const IPAddress &p_host) {
 | 
			
		||||
	if (p_host.is_wildcard()) {
 | 
			
		||||
		ip_type = IP::TYPE_ANY;
 | 
			
		||||
	}
 | 
			
		||||
	Error err = _sock->open(NetSocket::TYPE_TCP, ip_type);
 | 
			
		||||
	Error err = _sock->open(NetSocket::Family::INET, NetSocket::TYPE_TCP, ip_type);
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
	_sock->set_blocking_enabled(false);
 | 
			
		||||
	return _sock->bind(p_host, p_port);
 | 
			
		||||
	NetSocket::Address addr(p_host, p_port);
 | 
			
		||||
	return _sock->bind(addr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerTCP::connect_to_host(const IPAddress &p_host, int p_port) {
 | 
			
		||||
@@ -113,7 +68,7 @@ Error StreamPeerTCP::connect_to_host(const IPAddress &p_host, int p_port) {
 | 
			
		||||
 | 
			
		||||
	if (!_sock->is_open()) {
 | 
			
		||||
		IP::Type ip_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
 | 
			
		||||
		Error err = _sock->open(NetSocket::TYPE_TCP, ip_type);
 | 
			
		||||
		Error err = _sock->open(NetSocket::Family::INET, NetSocket::TYPE_TCP, ip_type);
 | 
			
		||||
		if (err != OK) {
 | 
			
		||||
			return err;
 | 
			
		||||
		}
 | 
			
		||||
@@ -121,7 +76,9 @@ Error StreamPeerTCP::connect_to_host(const IPAddress &p_host, int p_port) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout = OS::get_singleton()->get_ticks_msec() + (((uint64_t)GLOBAL_GET("network/limits/tcp/connect_timeout_seconds")) * 1000);
 | 
			
		||||
	Error err = _sock->connect_to_host(p_host, p_port);
 | 
			
		||||
 | 
			
		||||
	NetSocket::Address addr(p_host, p_port);
 | 
			
		||||
	Error err = _sock->connect_to_host(addr);
 | 
			
		||||
 | 
			
		||||
	if (err == OK) {
 | 
			
		||||
		status = STATUS_CONNECTED;
 | 
			
		||||
@@ -133,106 +90,7 @@ Error StreamPeerTCP::connect_to_host(const IPAddress &p_host, int p_port) {
 | 
			
		||||
		return FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	peer_host = p_host;
 | 
			
		||||
	peer_port = p_port;
 | 
			
		||||
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerTCP::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block) {
 | 
			
		||||
	ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
 | 
			
		||||
 | 
			
		||||
	if (status != STATUS_CONNECTED) {
 | 
			
		||||
		return FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Error err;
 | 
			
		||||
	int data_to_send = p_bytes;
 | 
			
		||||
	const uint8_t *offset = p_data;
 | 
			
		||||
	int total_sent = 0;
 | 
			
		||||
 | 
			
		||||
	while (data_to_send) {
 | 
			
		||||
		int sent_amount = 0;
 | 
			
		||||
		err = _sock->send(offset, data_to_send, sent_amount);
 | 
			
		||||
 | 
			
		||||
		if (err != OK) {
 | 
			
		||||
			if (err != ERR_BUSY) {
 | 
			
		||||
				disconnect_from_host();
 | 
			
		||||
				return FAILED;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (!p_block) {
 | 
			
		||||
				r_sent = total_sent;
 | 
			
		||||
				return OK;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Block and wait for the socket to accept more data
 | 
			
		||||
			err = _sock->poll(NetSocket::POLL_TYPE_OUT, -1);
 | 
			
		||||
			if (err != OK) {
 | 
			
		||||
				disconnect_from_host();
 | 
			
		||||
				return FAILED;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			data_to_send -= sent_amount;
 | 
			
		||||
			offset += sent_amount;
 | 
			
		||||
			total_sent += sent_amount;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r_sent = total_sent;
 | 
			
		||||
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerTCP::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block) {
 | 
			
		||||
	if (status != STATUS_CONNECTED) {
 | 
			
		||||
		return FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Error err;
 | 
			
		||||
	int to_read = p_bytes;
 | 
			
		||||
	int total_read = 0;
 | 
			
		||||
	r_received = 0;
 | 
			
		||||
 | 
			
		||||
	while (to_read) {
 | 
			
		||||
		int read = 0;
 | 
			
		||||
		err = _sock->recv(p_buffer + total_read, to_read, read);
 | 
			
		||||
 | 
			
		||||
		if (err != OK) {
 | 
			
		||||
			if (err != ERR_BUSY) {
 | 
			
		||||
				disconnect_from_host();
 | 
			
		||||
				return FAILED;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (!p_block) {
 | 
			
		||||
				r_received = total_read;
 | 
			
		||||
				return OK;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			err = _sock->poll(NetSocket::POLL_TYPE_IN, -1);
 | 
			
		||||
 | 
			
		||||
			if (err != OK) {
 | 
			
		||||
				disconnect_from_host();
 | 
			
		||||
				return FAILED;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		} else if (read == 0) {
 | 
			
		||||
			disconnect_from_host();
 | 
			
		||||
			r_received = total_read;
 | 
			
		||||
			return ERR_FILE_EOF;
 | 
			
		||||
 | 
			
		||||
		} else {
 | 
			
		||||
			to_read -= read;
 | 
			
		||||
			total_read += read;
 | 
			
		||||
 | 
			
		||||
			if (!p_block) {
 | 
			
		||||
				r_received = total_read;
 | 
			
		||||
				return OK;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r_received = total_read;
 | 
			
		||||
	peer_address = addr;
 | 
			
		||||
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
@@ -242,61 +100,18 @@ void StreamPeerTCP::set_no_delay(bool p_enabled) {
 | 
			
		||||
	_sock->set_tcp_no_delay_enabled(p_enabled);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
StreamPeerTCP::Status StreamPeerTCP::get_status() const {
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StreamPeerTCP::disconnect_from_host() {
 | 
			
		||||
	if (_sock.is_valid() && _sock->is_open()) {
 | 
			
		||||
		_sock->close();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout = 0;
 | 
			
		||||
	status = STATUS_NONE;
 | 
			
		||||
	peer_host = IPAddress();
 | 
			
		||||
	peer_port = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerTCP::wait(NetSocket::PollType p_type, int p_timeout) {
 | 
			
		||||
	ERR_FAIL_COND_V(_sock.is_null() || !_sock->is_open(), ERR_UNAVAILABLE);
 | 
			
		||||
	return _sock->poll(p_type, p_timeout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerTCP::put_data(const uint8_t *p_data, int p_bytes) {
 | 
			
		||||
	int total;
 | 
			
		||||
	return write(p_data, p_bytes, total, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerTCP::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
 | 
			
		||||
	return write(p_data, p_bytes, r_sent, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerTCP::get_data(uint8_t *p_buffer, int p_bytes) {
 | 
			
		||||
	int total;
 | 
			
		||||
	return read(p_buffer, p_bytes, total, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerTCP::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
 | 
			
		||||
	return read(p_buffer, p_bytes, r_received, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int StreamPeerTCP::get_available_bytes() const {
 | 
			
		||||
	ERR_FAIL_COND_V(_sock.is_null(), -1);
 | 
			
		||||
	return _sock->get_available_bytes();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IPAddress StreamPeerTCP::get_connected_host() const {
 | 
			
		||||
	return peer_host;
 | 
			
		||||
	return peer_address.ip();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int StreamPeerTCP::get_connected_port() const {
 | 
			
		||||
	return peer_port;
 | 
			
		||||
	return peer_address.port();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int StreamPeerTCP::get_local_port() const {
 | 
			
		||||
	uint16_t local_port;
 | 
			
		||||
	_sock->get_socket_address(nullptr, &local_port);
 | 
			
		||||
	return local_port;
 | 
			
		||||
	NetSocket::Address addr;
 | 
			
		||||
	_sock->get_socket_address(&addr);
 | 
			
		||||
	return addr.port();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerTCP::_connect(const String &p_address, int p_port) {
 | 
			
		||||
@@ -316,24 +131,8 @@ Error StreamPeerTCP::_connect(const String &p_address, int p_port) {
 | 
			
		||||
void StreamPeerTCP::_bind_methods() {
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("bind", "port", "host"), &StreamPeerTCP::bind, DEFVAL("*"));
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port"), &StreamPeerTCP::_connect);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("poll"), &StreamPeerTCP::poll);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerTCP::get_status);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_connected_host"), &StreamPeerTCP::get_connected_host);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_connected_port"), &StreamPeerTCP::get_connected_port);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_local_port"), &StreamPeerTCP::get_local_port);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("disconnect_from_host"), &StreamPeerTCP::disconnect_from_host);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("set_no_delay", "enabled"), &StreamPeerTCP::set_no_delay);
 | 
			
		||||
 | 
			
		||||
	BIND_ENUM_CONSTANT(STATUS_NONE);
 | 
			
		||||
	BIND_ENUM_CONSTANT(STATUS_CONNECTING);
 | 
			
		||||
	BIND_ENUM_CONSTANT(STATUS_CONNECTED);
 | 
			
		||||
	BIND_ENUM_CONSTANT(STATUS_ERROR);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
StreamPeerTCP::StreamPeerTCP() :
 | 
			
		||||
		_sock(Ref<NetSocket>(NetSocket::create())) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
StreamPeerTCP::~StreamPeerTCP() {
 | 
			
		||||
	disconnect_from_host();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -32,62 +32,24 @@
 | 
			
		||||
 | 
			
		||||
#include "core/io/ip.h"
 | 
			
		||||
#include "core/io/ip_address.h"
 | 
			
		||||
#include "core/io/net_socket.h"
 | 
			
		||||
#include "core/io/stream_peer.h"
 | 
			
		||||
#include "core/io/stream_peer_socket.h"
 | 
			
		||||
 | 
			
		||||
class StreamPeerTCP : public StreamPeer {
 | 
			
		||||
	GDCLASS(StreamPeerTCP, StreamPeer);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	enum Status {
 | 
			
		||||
		STATUS_NONE,
 | 
			
		||||
		STATUS_CONNECTING,
 | 
			
		||||
		STATUS_CONNECTED,
 | 
			
		||||
		STATUS_ERROR,
 | 
			
		||||
	};
 | 
			
		||||
class StreamPeerTCP : public StreamPeerSocket {
 | 
			
		||||
	GDCLASS(StreamPeerTCP, StreamPeerSocket);
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	Ref<NetSocket> _sock;
 | 
			
		||||
	uint64_t timeout = 0;
 | 
			
		||||
	Status status = STATUS_NONE;
 | 
			
		||||
	IPAddress peer_host;
 | 
			
		||||
	uint16_t peer_port = 0;
 | 
			
		||||
 | 
			
		||||
	Error _connect(const String &p_address, int p_port);
 | 
			
		||||
	Error write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block);
 | 
			
		||||
	Error read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block);
 | 
			
		||||
 | 
			
		||||
	static void _bind_methods();
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	void accept_socket(Ref<NetSocket> p_sock, IPAddress p_host, uint16_t p_port);
 | 
			
		||||
	void accept_socket(Ref<NetSocket> p_sock, const NetSocket::Address &p_addr) override;
 | 
			
		||||
 | 
			
		||||
	Error bind(int p_port, const IPAddress &p_host);
 | 
			
		||||
	Error connect_to_host(const IPAddress &p_host, int p_port);
 | 
			
		||||
	IPAddress get_connected_host() const;
 | 
			
		||||
	int get_connected_port() const;
 | 
			
		||||
	int get_local_port() const;
 | 
			
		||||
	void disconnect_from_host();
 | 
			
		||||
 | 
			
		||||
	int get_available_bytes() const override;
 | 
			
		||||
	Status get_status() const;
 | 
			
		||||
 | 
			
		||||
	void set_no_delay(bool p_enabled);
 | 
			
		||||
 | 
			
		||||
	// Poll socket updating its state.
 | 
			
		||||
	Error poll();
 | 
			
		||||
 | 
			
		||||
	// Wait or check for writable, readable.
 | 
			
		||||
	Error wait(NetSocket::PollType p_type, int p_timeout = 0);
 | 
			
		||||
 | 
			
		||||
	// Read/Write from StreamPeer
 | 
			
		||||
	Error put_data(const uint8_t *p_data, int p_bytes) override;
 | 
			
		||||
	Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override;
 | 
			
		||||
	Error get_data(uint8_t *p_buffer, int p_bytes) override;
 | 
			
		||||
	Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override;
 | 
			
		||||
 | 
			
		||||
	StreamPeerTCP();
 | 
			
		||||
	~StreamPeerTCP();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
VARIANT_ENUM_CAST(StreamPeerTCP::Status);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										99
									
								
								core/io/stream_peer_uds.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								core/io/stream_peer_uds.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*  stream_peer_uds.cpp                                                   */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
/*                        https://godotengine.org                         */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
 | 
			
		||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* Permission is hereby granted, free of charge, to any person obtaining  */
 | 
			
		||||
/* a copy of this software and associated documentation files (the        */
 | 
			
		||||
/* "Software"), to deal in the Software without restriction, including    */
 | 
			
		||||
/* without limitation the rights to use, copy, modify, merge, publish,    */
 | 
			
		||||
/* distribute, sublicense, and/or sell copies of the Software, and to     */
 | 
			
		||||
/* permit persons to whom the Software is furnished to do so, subject to  */
 | 
			
		||||
/* the following conditions:                                              */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* The above copyright notice and this permission notice shall be         */
 | 
			
		||||
/* included in all copies or substantial portions of the Software.        */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
 | 
			
		||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
 | 
			
		||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
 | 
			
		||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
 | 
			
		||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
 | 
			
		||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
 | 
			
		||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "stream_peer_uds.h"
 | 
			
		||||
 | 
			
		||||
#include "core/config/project_settings.h"
 | 
			
		||||
 | 
			
		||||
void StreamPeerUDS::_bind_methods() {
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("bind", "path"), &StreamPeerUDS::bind);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("connect_to_host", "path"), &StreamPeerUDS::connect_to_host);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_connected_path"), &StreamPeerUDS::get_connected_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StreamPeerUDS::accept_socket(Ref<NetSocket> p_sock, const NetSocket::Address &p_addr) {
 | 
			
		||||
	_sock = p_sock;
 | 
			
		||||
	_sock->set_blocking_enabled(false);
 | 
			
		||||
 | 
			
		||||
	timeout = OS::get_singleton()->get_ticks_msec() + (((uint64_t)GLOBAL_GET("network/limits/unix/connect_timeout_seconds")) * 1000);
 | 
			
		||||
	status = STATUS_CONNECTED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerUDS::bind(const String &p_path) {
 | 
			
		||||
	ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
 | 
			
		||||
	ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
 | 
			
		||||
 | 
			
		||||
	IP::Type ip_type = IP::TYPE_NONE;
 | 
			
		||||
	Error err = _sock->open(NetSocket::Family::UNIX, NetSocket::TYPE_NONE, ip_type);
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
	_sock->set_blocking_enabled(false);
 | 
			
		||||
	NetSocket::Address addr(p_path);
 | 
			
		||||
	return _sock->bind(addr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error StreamPeerUDS::connect_to_host(const String &p_path) {
 | 
			
		||||
	ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
 | 
			
		||||
	ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
 | 
			
		||||
	ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER);
 | 
			
		||||
 | 
			
		||||
	if (!_sock->is_open()) {
 | 
			
		||||
		IP::Type ip_type = IP::TYPE_NONE;
 | 
			
		||||
		Error err = _sock->open(NetSocket::Family::UNIX, NetSocket::TYPE_NONE, ip_type);
 | 
			
		||||
		if (err != OK) {
 | 
			
		||||
			return err;
 | 
			
		||||
		}
 | 
			
		||||
		_sock->set_blocking_enabled(false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout = OS::get_singleton()->get_ticks_msec() + (((uint64_t)GLOBAL_GET("network/limits/unix/connect_timeout_seconds")) * 1000);
 | 
			
		||||
	NetSocket::Address addr(p_path);
 | 
			
		||||
	Error err = _sock->connect_to_host(addr);
 | 
			
		||||
 | 
			
		||||
	if (err == OK) {
 | 
			
		||||
		status = STATUS_CONNECTED;
 | 
			
		||||
	} else if (err == ERR_BUSY) {
 | 
			
		||||
		status = STATUS_CONNECTING;
 | 
			
		||||
	} else {
 | 
			
		||||
		ERR_PRINT("Connection to remote host failed!");
 | 
			
		||||
		disconnect_from_host();
 | 
			
		||||
		return FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	peer_address = addr;
 | 
			
		||||
	peer_path = p_path;
 | 
			
		||||
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const String StreamPeerUDS::get_connected_path() const {
 | 
			
		||||
	return String(peer_address.get_path().get_data());
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										48
									
								
								core/io/stream_peer_uds.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								core/io/stream_peer_uds.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*  stream_peer_uds.h                                                     */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
/*                        https://godotengine.org                         */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
 | 
			
		||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* Permission is hereby granted, free of charge, to any person obtaining  */
 | 
			
		||||
/* a copy of this software and associated documentation files (the        */
 | 
			
		||||
/* "Software"), to deal in the Software without restriction, including    */
 | 
			
		||||
/* without limitation the rights to use, copy, modify, merge, publish,    */
 | 
			
		||||
/* distribute, sublicense, and/or sell copies of the Software, and to     */
 | 
			
		||||
/* permit persons to whom the Software is furnished to do so, subject to  */
 | 
			
		||||
/* the following conditions:                                              */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* The above copyright notice and this permission notice shall be         */
 | 
			
		||||
/* included in all copies or substantial portions of the Software.        */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
 | 
			
		||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
 | 
			
		||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
 | 
			
		||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
 | 
			
		||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
 | 
			
		||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
 | 
			
		||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "core/io/stream_peer_socket.h"
 | 
			
		||||
 | 
			
		||||
class StreamPeerUDS : public StreamPeerSocket {
 | 
			
		||||
	GDCLASS(StreamPeerUDS, StreamPeerSocket);
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	String peer_path;
 | 
			
		||||
	static void _bind_methods();
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	void accept_socket(Ref<NetSocket> p_sock, const NetSocket::Address &p_addr) override;
 | 
			
		||||
 | 
			
		||||
	Error bind(const String &p_path);
 | 
			
		||||
	Error connect_to_host(const String &p_path);
 | 
			
		||||
	const String get_connected_path() const;
 | 
			
		||||
};
 | 
			
		||||
@@ -32,11 +32,8 @@
 | 
			
		||||
 | 
			
		||||
void TCPServer::_bind_methods() {
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &TCPServer::listen, DEFVAL("*"));
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("is_connection_available"), &TCPServer::is_connection_available);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("is_listening"), &TCPServer::is_listening);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_local_port"), &TCPServer::get_local_port);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("take_connection"), &TCPServer::take_connection);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("stop"), &TCPServer::stop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error TCPServer::listen(uint16_t p_port, const IPAddress &p_bind_address) {
 | 
			
		||||
@@ -52,81 +49,21 @@ Error TCPServer::listen(uint16_t p_port, const IPAddress &p_bind_address) {
 | 
			
		||||
		ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = _sock->open(NetSocket::TYPE_TCP, ip_type);
 | 
			
		||||
	err = _sock->open(NetSocket::Family::INET, NetSocket::TYPE_TCP, ip_type);
 | 
			
		||||
 | 
			
		||||
	ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
 | 
			
		||||
 | 
			
		||||
	_sock->set_blocking_enabled(false);
 | 
			
		||||
	_sock->set_reuse_address_enabled(true);
 | 
			
		||||
 | 
			
		||||
	err = _sock->bind(p_bind_address, p_port);
 | 
			
		||||
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		_sock->close();
 | 
			
		||||
		return ERR_ALREADY_IN_USE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = _sock->listen(MAX_PENDING_CONNECTIONS);
 | 
			
		||||
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		_sock->close();
 | 
			
		||||
		return FAILED;
 | 
			
		||||
	}
 | 
			
		||||
	return OK;
 | 
			
		||||
	return _listen(NetSocket::Address(p_bind_address, p_port));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int TCPServer::get_local_port() const {
 | 
			
		||||
	uint16_t local_port;
 | 
			
		||||
	_sock->get_socket_address(nullptr, &local_port);
 | 
			
		||||
	return local_port;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool TCPServer::is_listening() const {
 | 
			
		||||
	ERR_FAIL_COND_V(_sock.is_null(), false);
 | 
			
		||||
 | 
			
		||||
	return _sock->is_open();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool TCPServer::is_connection_available() const {
 | 
			
		||||
	ERR_FAIL_COND_V(_sock.is_null(), false);
 | 
			
		||||
 | 
			
		||||
	if (!_sock->is_open()) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Error err = _sock->poll(NetSocket::POLL_TYPE_IN, 0);
 | 
			
		||||
	return (err == OK);
 | 
			
		||||
	NetSocket::Address addr;
 | 
			
		||||
	_sock->get_socket_address(&addr);
 | 
			
		||||
	return addr.port();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<StreamPeerTCP> TCPServer::take_connection() {
 | 
			
		||||
	Ref<StreamPeerTCP> conn;
 | 
			
		||||
	if (!is_connection_available()) {
 | 
			
		||||
		return conn;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Ref<NetSocket> ns;
 | 
			
		||||
	IPAddress ip;
 | 
			
		||||
	uint16_t port = 0;
 | 
			
		||||
	ns = _sock->accept(ip, port);
 | 
			
		||||
	if (ns.is_null()) {
 | 
			
		||||
		return conn;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn.instantiate();
 | 
			
		||||
	conn->accept_socket(ns, ip, port);
 | 
			
		||||
	return conn;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TCPServer::stop() {
 | 
			
		||||
	if (_sock.is_valid()) {
 | 
			
		||||
		_sock->close();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TCPServer::TCPServer() :
 | 
			
		||||
		_sock(Ref<NetSocket>(NetSocket::create())) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TCPServer::~TCPServer() {
 | 
			
		||||
	stop();
 | 
			
		||||
	return _take_connection<StreamPeerTCP>();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,30 +31,18 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "core/io/ip.h"
 | 
			
		||||
#include "core/io/net_socket.h"
 | 
			
		||||
#include "core/io/stream_peer.h"
 | 
			
		||||
#include "core/io/socket_server.h"
 | 
			
		||||
#include "core/io/stream_peer_tcp.h"
 | 
			
		||||
 | 
			
		||||
class TCPServer : public RefCounted {
 | 
			
		||||
	GDCLASS(TCPServer, RefCounted);
 | 
			
		||||
class TCPServer : public SocketServer {
 | 
			
		||||
	GDCLASS(TCPServer, SocketServer);
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	enum {
 | 
			
		||||
		MAX_PENDING_CONNECTIONS = 8
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Ref<NetSocket> _sock;
 | 
			
		||||
	static void _bind_methods();
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	Error listen(uint16_t p_port, const IPAddress &p_bind_address = IPAddress("*"));
 | 
			
		||||
	int get_local_port() const;
 | 
			
		||||
	bool is_listening() const;
 | 
			
		||||
	bool is_connection_available() const;
 | 
			
		||||
	Ref<StreamPeerTCP> take_connection();
 | 
			
		||||
 | 
			
		||||
	void stop(); // Stop listening
 | 
			
		||||
 | 
			
		||||
	TCPServer();
 | 
			
		||||
	~TCPServer();
 | 
			
		||||
	Ref<StreamPeerSocket> take_socket_connection() override { return take_connection(); }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -99,7 +99,7 @@ Error UDPServer::listen(uint16_t p_port, const IPAddress &p_bind_address) {
 | 
			
		||||
		ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = _sock->open(NetSocket::TYPE_UDP, ip_type);
 | 
			
		||||
	err = _sock->open(NetSocket::Family::INET, NetSocket::TYPE_UDP, ip_type);
 | 
			
		||||
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		return ERR_CANT_CREATE;
 | 
			
		||||
@@ -107,7 +107,8 @@ Error UDPServer::listen(uint16_t p_port, const IPAddress &p_bind_address) {
 | 
			
		||||
 | 
			
		||||
	_sock->set_blocking_enabled(false);
 | 
			
		||||
	_sock->set_reuse_address_enabled(true);
 | 
			
		||||
	err = _sock->bind(p_bind_address, p_port);
 | 
			
		||||
	NetSocket::Address addr(p_bind_address, p_port);
 | 
			
		||||
	err = _sock->bind(addr);
 | 
			
		||||
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		stop();
 | 
			
		||||
@@ -117,9 +118,9 @@ Error UDPServer::listen(uint16_t p_port, const IPAddress &p_bind_address) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UDPServer::get_local_port() const {
 | 
			
		||||
	uint16_t local_port;
 | 
			
		||||
	_sock->get_socket_address(nullptr, &local_port);
 | 
			
		||||
	return local_port;
 | 
			
		||||
	NetSocket::Address addr;
 | 
			
		||||
	_sock->get_socket_address(&addr);
 | 
			
		||||
	return addr.port();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool UDPServer::is_listening() const {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										52
									
								
								core/io/uds_server.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								core/io/uds_server.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*  uds_server.cpp                                                        */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
/*                        https://godotengine.org                         */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
 | 
			
		||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* Permission is hereby granted, free of charge, to any person obtaining  */
 | 
			
		||||
/* a copy of this software and associated documentation files (the        */
 | 
			
		||||
/* "Software"), to deal in the Software without restriction, including    */
 | 
			
		||||
/* without limitation the rights to use, copy, modify, merge, publish,    */
 | 
			
		||||
/* distribute, sublicense, and/or sell copies of the Software, and to     */
 | 
			
		||||
/* permit persons to whom the Software is furnished to do so, subject to  */
 | 
			
		||||
/* the following conditions:                                              */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* The above copyright notice and this permission notice shall be         */
 | 
			
		||||
/* included in all copies or substantial portions of the Software.        */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
 | 
			
		||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
 | 
			
		||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
 | 
			
		||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
 | 
			
		||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
 | 
			
		||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
 | 
			
		||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "uds_server.h"
 | 
			
		||||
 | 
			
		||||
void UDSServer::_bind_methods() {
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("listen", "path"), &UDSServer::listen);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("take_connection"), &UDSServer::take_connection);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error UDSServer::listen(const String &p_path) {
 | 
			
		||||
	ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
 | 
			
		||||
	ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
 | 
			
		||||
	ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER);
 | 
			
		||||
 | 
			
		||||
	IP::Type ip_type = IP::TYPE_NONE;
 | 
			
		||||
	Error err = _sock->open(NetSocket::Family::UNIX, NetSocket::TYPE_NONE, ip_type);
 | 
			
		||||
	ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
 | 
			
		||||
 | 
			
		||||
	return _listen(p_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<StreamPeerUDS> UDSServer::take_connection() {
 | 
			
		||||
	return _take_connection<StreamPeerUDS>();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								core/io/uds_server.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								core/io/uds_server.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*  uds_server.h                                                          */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
/*                        https://godotengine.org                         */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
 | 
			
		||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* Permission is hereby granted, free of charge, to any person obtaining  */
 | 
			
		||||
/* a copy of this software and associated documentation files (the        */
 | 
			
		||||
/* "Software"), to deal in the Software without restriction, including    */
 | 
			
		||||
/* without limitation the rights to use, copy, modify, merge, publish,    */
 | 
			
		||||
/* distribute, sublicense, and/or sell copies of the Software, and to     */
 | 
			
		||||
/* permit persons to whom the Software is furnished to do so, subject to  */
 | 
			
		||||
/* the following conditions:                                              */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* The above copyright notice and this permission notice shall be         */
 | 
			
		||||
/* included in all copies or substantial portions of the Software.        */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
 | 
			
		||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
 | 
			
		||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
 | 
			
		||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
 | 
			
		||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
 | 
			
		||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
 | 
			
		||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "core/io/socket_server.h"
 | 
			
		||||
#include "core/io/stream_peer_uds.h"
 | 
			
		||||
 | 
			
		||||
class UDSServer : public SocketServer {
 | 
			
		||||
	GDCLASS(UDSServer, SocketServer);
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	static void _bind_methods();
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	Error listen(const String &p_path);
 | 
			
		||||
	Ref<StreamPeerUDS> take_connection();
 | 
			
		||||
	Ref<StreamPeerSocket> take_socket_connection() override { return take_connection(); }
 | 
			
		||||
};
 | 
			
		||||
@@ -63,6 +63,7 @@
 | 
			
		||||
#include "core/io/tcp_server.h"
 | 
			
		||||
#include "core/io/translation_loader_po.h"
 | 
			
		||||
#include "core/io/udp_server.h"
 | 
			
		||||
#include "core/io/uds_server.h"
 | 
			
		||||
#include "core/io/xml_parser.h"
 | 
			
		||||
#include "core/math/a_star.h"
 | 
			
		||||
#include "core/math/a_star_grid_2d.h"
 | 
			
		||||
@@ -198,12 +199,18 @@ void register_core_types() {
 | 
			
		||||
	GDREGISTER_ABSTRACT_CLASS(IP);
 | 
			
		||||
 | 
			
		||||
	GDREGISTER_ABSTRACT_CLASS(StreamPeer);
 | 
			
		||||
	GDREGISTER_ABSTRACT_CLASS(StreamPeerSocket);
 | 
			
		||||
	GDREGISTER_ABSTRACT_CLASS(SocketServer);
 | 
			
		||||
	GDREGISTER_CLASS(StreamPeerExtension);
 | 
			
		||||
	GDREGISTER_CLASS(StreamPeerBuffer);
 | 
			
		||||
	GDREGISTER_CLASS(StreamPeerGZIP);
 | 
			
		||||
	GDREGISTER_CLASS(StreamPeerTCP);
 | 
			
		||||
	GDREGISTER_CLASS(TCPServer);
 | 
			
		||||
 | 
			
		||||
	// IPC using UNIX domain sockets.
 | 
			
		||||
	GDREGISTER_CLASS(StreamPeerUDS);
 | 
			
		||||
	GDREGISTER_CLASS(UDSServer);
 | 
			
		||||
 | 
			
		||||
	GDREGISTER_ABSTRACT_CLASS(PacketPeer);
 | 
			
		||||
	GDREGISTER_CLASS(PacketPeerExtension);
 | 
			
		||||
	GDREGISTER_CLASS(PacketPeerStream);
 | 
			
		||||
@@ -322,6 +329,7 @@ void register_core_types() {
 | 
			
		||||
void register_core_settings() {
 | 
			
		||||
	// Since in register core types, globals may not be present.
 | 
			
		||||
	GLOBAL_DEF(PropertyInfo(Variant::INT, "network/limits/tcp/connect_timeout_seconds", PROPERTY_HINT_RANGE, "1,1800,1"), (30));
 | 
			
		||||
	GLOBAL_DEF(PropertyInfo(Variant::INT, "network/limits/unix/connect_timeout_seconds", PROPERTY_HINT_RANGE, "1,1800,1"), (30));
 | 
			
		||||
	GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "network/limits/packet_peer_stream/max_buffer_po2", PROPERTY_HINT_RANGE, "8,64,1,or_greater"), (16));
 | 
			
		||||
	GLOBAL_DEF(PropertyInfo(Variant::STRING, "network/tls/certificate_bundle_override", PROPERTY_HINT_FILE, "*.crt"), "");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2377,6 +2377,9 @@
 | 
			
		||||
		<member name="network/limits/tcp/connect_timeout_seconds" type="int" setter="" getter="" default="30">
 | 
			
		||||
			Timeout (in seconds) for connection attempts using TCP.
 | 
			
		||||
		</member>
 | 
			
		||||
		<member name="network/limits/unix/connect_timeout_seconds" type="int" setter="" getter="" default="30">
 | 
			
		||||
			Timeout (in seconds) for connection attempts using UNIX domain socket.
 | 
			
		||||
		</member>
 | 
			
		||||
		<member name="network/limits/webrtc/max_channel_in_buffer_kb" type="int" setter="" getter="" default="64">
 | 
			
		||||
			Maximum size (in kiB) for the [WebRTCDataChannel] input buffer.
 | 
			
		||||
		</member>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								doc/classes/SocketServer.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								doc/classes/SocketServer.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" ?>
 | 
			
		||||
<class name="SocketServer" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
 | 
			
		||||
	<brief_description>
 | 
			
		||||
		An abstract class for servers based on sockets.
 | 
			
		||||
	</brief_description>
 | 
			
		||||
	<description>
 | 
			
		||||
		A socket server.
 | 
			
		||||
	</description>
 | 
			
		||||
	<tutorials>
 | 
			
		||||
	</tutorials>
 | 
			
		||||
	<methods>
 | 
			
		||||
		<method name="is_connection_available" qualifiers="const">
 | 
			
		||||
			<return type="bool" />
 | 
			
		||||
			<description>
 | 
			
		||||
				Returns [code]true[/code] if a connection is available for taking.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="is_listening" qualifiers="const">
 | 
			
		||||
			<return type="bool" />
 | 
			
		||||
			<description>
 | 
			
		||||
				Returns [code]true[/code] if the server is currently listening for connections.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="stop">
 | 
			
		||||
			<return type="void" />
 | 
			
		||||
			<description>
 | 
			
		||||
				Stops listening.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="take_socket_connection">
 | 
			
		||||
			<return type="StreamPeerSocket" />
 | 
			
		||||
			<description>
 | 
			
		||||
				If a connection is available, returns a StreamPeerSocket with the connection.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
	</methods>
 | 
			
		||||
</class>
 | 
			
		||||
							
								
								
									
										45
									
								
								doc/classes/StreamPeerSocket.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								doc/classes/StreamPeerSocket.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" ?>
 | 
			
		||||
<class name="StreamPeerSocket" inherits="StreamPeer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
 | 
			
		||||
	<brief_description>
 | 
			
		||||
		Abstract base class for interacting with socket streams.
 | 
			
		||||
	</brief_description>
 | 
			
		||||
	<description>
 | 
			
		||||
		StreamPeerSocket is an abstract base class that defines common behavior for socket-based streams.
 | 
			
		||||
	</description>
 | 
			
		||||
	<tutorials>
 | 
			
		||||
	</tutorials>
 | 
			
		||||
	<methods>
 | 
			
		||||
		<method name="disconnect_from_host">
 | 
			
		||||
			<return type="void" />
 | 
			
		||||
			<description>
 | 
			
		||||
				Disconnects from host.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="get_status" qualifiers="const">
 | 
			
		||||
			<return type="int" enum="StreamPeerSocket.Status" />
 | 
			
		||||
			<description>
 | 
			
		||||
				Returns the status of the connection.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="poll">
 | 
			
		||||
			<return type="int" enum="Error" />
 | 
			
		||||
			<description>
 | 
			
		||||
				Polls the socket, updating its state. See [method get_status].
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
	</methods>
 | 
			
		||||
	<constants>
 | 
			
		||||
		<constant name="STATUS_NONE" value="0" enum="Status">
 | 
			
		||||
			The initial status of the [StreamPeerSocket]. This is also the status after disconnecting.
 | 
			
		||||
		</constant>
 | 
			
		||||
		<constant name="STATUS_CONNECTING" value="1" enum="Status">
 | 
			
		||||
			A status representing a [StreamPeerSocket] that is connecting to a host.
 | 
			
		||||
		</constant>
 | 
			
		||||
		<constant name="STATUS_CONNECTED" value="2" enum="Status">
 | 
			
		||||
			A status representing a [StreamPeerSocket] that is connected to a host.
 | 
			
		||||
		</constant>
 | 
			
		||||
		<constant name="STATUS_ERROR" value="3" enum="Status">
 | 
			
		||||
			A status representing a [StreamPeerSocket] in error state.
 | 
			
		||||
		</constant>
 | 
			
		||||
	</constants>
 | 
			
		||||
</class>
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" ?>
 | 
			
		||||
<class name="StreamPeerTCP" inherits="StreamPeer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
 | 
			
		||||
<class name="StreamPeerTCP" inherits="StreamPeerSocket" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
 | 
			
		||||
	<brief_description>
 | 
			
		||||
		A stream peer that handles TCP connections.
 | 
			
		||||
	</brief_description>
 | 
			
		||||
@@ -27,12 +27,6 @@
 | 
			
		||||
				Connects to the specified [code]host:port[/code] pair. A hostname will be resolved if valid. Returns [constant OK] on success.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="disconnect_from_host">
 | 
			
		||||
			<return type="void" />
 | 
			
		||||
			<description>
 | 
			
		||||
				Disconnects from host.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="get_connected_host" qualifiers="const">
 | 
			
		||||
			<return type="String" />
 | 
			
		||||
			<description>
 | 
			
		||||
@@ -51,18 +45,6 @@
 | 
			
		||||
				Returns the local port to which this peer is bound.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="get_status" qualifiers="const">
 | 
			
		||||
			<return type="int" enum="StreamPeerTCP.Status" />
 | 
			
		||||
			<description>
 | 
			
		||||
				Returns the status of the connection.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="poll">
 | 
			
		||||
			<return type="int" enum="Error" />
 | 
			
		||||
			<description>
 | 
			
		||||
				Poll the socket, updating its state. See [method get_status].
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="set_no_delay">
 | 
			
		||||
			<return type="void" />
 | 
			
		||||
			<param index="0" name="enabled" type="bool" />
 | 
			
		||||
@@ -72,18 +54,4 @@
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
	</methods>
 | 
			
		||||
	<constants>
 | 
			
		||||
		<constant name="STATUS_NONE" value="0" enum="Status">
 | 
			
		||||
			The initial status of the [StreamPeerTCP]. This is also the status after disconnecting.
 | 
			
		||||
		</constant>
 | 
			
		||||
		<constant name="STATUS_CONNECTING" value="1" enum="Status">
 | 
			
		||||
			A status representing a [StreamPeerTCP] that is connecting to a host.
 | 
			
		||||
		</constant>
 | 
			
		||||
		<constant name="STATUS_CONNECTED" value="2" enum="Status">
 | 
			
		||||
			A status representing a [StreamPeerTCP] that is connected to a host.
 | 
			
		||||
		</constant>
 | 
			
		||||
		<constant name="STATUS_ERROR" value="3" enum="Status">
 | 
			
		||||
			A status representing a [StreamPeerTCP] in error state.
 | 
			
		||||
		</constant>
 | 
			
		||||
	</constants>
 | 
			
		||||
</class>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								doc/classes/StreamPeerUDS.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								doc/classes/StreamPeerUDS.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" ?>
 | 
			
		||||
<class name="StreamPeerUDS" inherits="StreamPeerSocket" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
 | 
			
		||||
	<brief_description>
 | 
			
		||||
		A stream peer that handles UNIX Domain Socket (UDS) connections.
 | 
			
		||||
	</brief_description>
 | 
			
		||||
	<description>
 | 
			
		||||
		A stream peer that handles UNIX Domain Socket (UDS) connections. This object can be used to connect to UDS servers, or also is returned by a UDS server. Unix Domain Sockets provide inter-process communication on the same machine using the filesystem namespace.
 | 
			
		||||
		[b]Note:[/b] UNIX Domain Sockets are only available on UNIX-like systems (Linux, macOS, etc.) and are not supported on Windows.
 | 
			
		||||
	</description>
 | 
			
		||||
	<tutorials>
 | 
			
		||||
	</tutorials>
 | 
			
		||||
	<methods>
 | 
			
		||||
		<method name="bind">
 | 
			
		||||
			<return type="int" enum="Error" />
 | 
			
		||||
			<param index="0" name="path" type="String" />
 | 
			
		||||
			<description>
 | 
			
		||||
				Opens the UDS socket, and binds it to the specified socket path.
 | 
			
		||||
				This method is generally not needed, and only used to force the subsequent call to [method connect_to_host] to use the specified [param path] as the source address.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="connect_to_host">
 | 
			
		||||
			<return type="int" enum="Error" />
 | 
			
		||||
			<param index="0" name="path" type="String" />
 | 
			
		||||
			<description>
 | 
			
		||||
				Connects to the specified UNIX Domain Socket path. Returns [constant OK] on success.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="get_connected_path" qualifiers="const">
 | 
			
		||||
			<return type="String" />
 | 
			
		||||
			<description>
 | 
			
		||||
				Returns the socket path of this peer.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
	</methods>
 | 
			
		||||
</class>
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" ?>
 | 
			
		||||
<class name="TCPServer" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
 | 
			
		||||
<class name="TCPServer" inherits="SocketServer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
 | 
			
		||||
	<brief_description>
 | 
			
		||||
		A TCP server.
 | 
			
		||||
	</brief_description>
 | 
			
		||||
@@ -16,18 +16,6 @@
 | 
			
		||||
				Returns the local port this server is listening to.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="is_connection_available" qualifiers="const">
 | 
			
		||||
			<return type="bool" />
 | 
			
		||||
			<description>
 | 
			
		||||
				Returns [code]true[/code] if a connection is available for taking.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="is_listening" qualifiers="const">
 | 
			
		||||
			<return type="bool" />
 | 
			
		||||
			<description>
 | 
			
		||||
				Returns [code]true[/code] if the server is currently listening for connections.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="listen">
 | 
			
		||||
			<return type="int" enum="Error" />
 | 
			
		||||
			<param index="0" name="port" type="int" />
 | 
			
		||||
@@ -39,12 +27,6 @@
 | 
			
		||||
				If [param bind_address] is set to any valid address (e.g. [code]"192.168.1.101"[/code], [code]"::1"[/code], etc.), the server will only listen on the interface with that address (or fail if no interface with the given address exists).
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="stop">
 | 
			
		||||
			<return type="void" />
 | 
			
		||||
			<description>
 | 
			
		||||
				Stops listening.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="take_connection">
 | 
			
		||||
			<return type="StreamPeerTCP" />
 | 
			
		||||
			<description>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								doc/classes/UDSServer.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								doc/classes/UDSServer.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" ?>
 | 
			
		||||
<class name="UDSServer" inherits="SocketServer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
 | 
			
		||||
	<brief_description>
 | 
			
		||||
		A Unix Domain Socket (UDS) server.
 | 
			
		||||
	</brief_description>
 | 
			
		||||
	<description>
 | 
			
		||||
		A Unix Domain Socket (UDS) server. Listens to connections on a socket path and returns a [StreamPeerUDS] when it gets an incoming connection. Unix Domain Sockets provide inter-process communication on the same machine using the filesystem namespace.
 | 
			
		||||
		[b]Note:[/b] Unix Domain Sockets are only available on Unix-like systems (Linux, macOS, etc.) and are not supported on Windows.
 | 
			
		||||
	</description>
 | 
			
		||||
	<tutorials>
 | 
			
		||||
	</tutorials>
 | 
			
		||||
	<methods>
 | 
			
		||||
		<method name="listen">
 | 
			
		||||
			<return type="int" enum="Error" />
 | 
			
		||||
			<param index="0" name="path" type="String" />
 | 
			
		||||
			<description>
 | 
			
		||||
				Listens on the socket at [param path]. The socket file will be created at the specified path.
 | 
			
		||||
				[b]Note:[/b] The socket file must not already exist at the specified path. You may need to remove any existing socket file before calling this method.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
		<method name="take_connection">
 | 
			
		||||
			<return type="StreamPeerUDS" />
 | 
			
		||||
			<description>
 | 
			
		||||
				If a connection is available, returns a StreamPeerUDS with the connection.
 | 
			
		||||
			</description>
 | 
			
		||||
		</method>
 | 
			
		||||
	</methods>
 | 
			
		||||
</class>
 | 
			
		||||
@@ -41,6 +41,7 @@
 | 
			
		||||
#include <poll.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
@@ -94,6 +95,20 @@ size_t NetSocketUnix::_set_addr_storage(struct sockaddr_storage *p_addr, const I
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
socklen_t NetSocketUnix::_unix_set_sockaddr(struct sockaddr_un *p_addr, const CharString &p_path) {
 | 
			
		||||
	memset(p_addr, 0, sizeof(struct sockaddr_un));
 | 
			
		||||
	p_addr->sun_family = AF_UNIX;
 | 
			
		||||
 | 
			
		||||
	// Path must not exceed maximum path length for Unix domain socket
 | 
			
		||||
	size_t path_len = p_path.length();
 | 
			
		||||
	ERR_FAIL_COND_V(path_len >= sizeof(p_addr->sun_path) - 1, 0);
 | 
			
		||||
 | 
			
		||||
	// Regular file system socket
 | 
			
		||||
	memcpy(p_addr->sun_path, p_path.get_data(), path_len);
 | 
			
		||||
	p_addr->sun_path[path_len] = '\0';
 | 
			
		||||
	return sizeof(struct sockaddr_un);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NetSocketUnix::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port) {
 | 
			
		||||
	if (p_addr->ss_family == AF_INET) {
 | 
			
		||||
		struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr;
 | 
			
		||||
@@ -172,8 +187,14 @@ bool NetSocketUnix::_can_use_ip(const IPAddress &p_ip, const bool p_for_bind) co
 | 
			
		||||
	return !(_ip_type != IP::TYPE_ANY && !p_ip.is_wildcard() && _ip_type != type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool NetSocketUnix::_can_use_path(const CharString &p_path) const {
 | 
			
		||||
	// Path must not exceed maximum path length for Unix domain socket
 | 
			
		||||
	return !p_path.is_empty() && (size_t)p_path.length() < sizeof(((sockaddr_un *)0)->sun_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_FORCE_INLINE_ Error NetSocketUnix::_change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add) {
 | 
			
		||||
	ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
 | 
			
		||||
	ERR_FAIL_COND_V(_family != Family::INET, ERR_UNAVAILABLE);
 | 
			
		||||
	ERR_FAIL_COND_V(!_can_use_ip(p_ip, false), ERR_INVALID_PARAMETER);
 | 
			
		||||
 | 
			
		||||
	// Need to force level and af_family to IP(v4) when using dual stacking and provided multicast group is IPv4.
 | 
			
		||||
@@ -240,36 +261,36 @@ void NetSocketUnix::_set_close_exec_enabled(bool p_enabled) {
 | 
			
		||||
	fcntl(_sock, F_SETFD, opts | FD_CLOEXEC);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error NetSocketUnix::open(Type p_sock_type, IP::Type &ip_type) {
 | 
			
		||||
	ERR_FAIL_COND_V(is_open(), ERR_ALREADY_IN_USE);
 | 
			
		||||
	ERR_FAIL_COND_V(ip_type > IP::TYPE_ANY || ip_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER);
 | 
			
		||||
Error NetSocketUnix::_inet_open(Type p_sock_type, IP::Type &r_ip_type) {
 | 
			
		||||
	ERR_FAIL_COND_V(r_ip_type > IP::TYPE_ANY || r_ip_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER);
 | 
			
		||||
 | 
			
		||||
#if defined(__OpenBSD__)
 | 
			
		||||
	// OpenBSD does not support dual stacking, fallback to IPv4 only.
 | 
			
		||||
	if (ip_type == IP::TYPE_ANY) {
 | 
			
		||||
		ip_type = IP::TYPE_IPV4;
 | 
			
		||||
	if (r_ip_type == IP::TYPE_ANY) {
 | 
			
		||||
		r_ip_type = IP::TYPE_IPV4;
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	int family = ip_type == IP::TYPE_IPV4 ? AF_INET : AF_INET6;
 | 
			
		||||
	int family = r_ip_type == IP::TYPE_IPV4 ? AF_INET : AF_INET6;
 | 
			
		||||
	int protocol = p_sock_type == TYPE_TCP ? IPPROTO_TCP : IPPROTO_UDP;
 | 
			
		||||
	int type = p_sock_type == TYPE_TCP ? SOCK_STREAM : SOCK_DGRAM;
 | 
			
		||||
	_sock = socket(family, type, protocol);
 | 
			
		||||
 | 
			
		||||
	if (_sock == -1 && ip_type == IP::TYPE_ANY) {
 | 
			
		||||
	if (_sock == -1 && r_ip_type == IP::TYPE_ANY) {
 | 
			
		||||
		// Careful here, changing the referenced parameter so the caller knows that we are using an IPv4 socket
 | 
			
		||||
		// in place of a dual stack one, and further calls to _set_sock_addr will work as expected.
 | 
			
		||||
		ip_type = IP::TYPE_IPV4;
 | 
			
		||||
		r_ip_type = IP::TYPE_IPV4;
 | 
			
		||||
		family = AF_INET;
 | 
			
		||||
		_sock = socket(family, type, protocol);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ERR_FAIL_COND_V(_sock == -1, FAILED);
 | 
			
		||||
	_ip_type = ip_type;
 | 
			
		||||
	_ip_type = r_ip_type;
 | 
			
		||||
	_family = Family::INET;
 | 
			
		||||
 | 
			
		||||
	if (family == AF_INET6) {
 | 
			
		||||
		// Select IPv4 over IPv6 mapping.
 | 
			
		||||
		set_ipv6_only_enabled(ip_type != IP::TYPE_ANY);
 | 
			
		||||
		set_ipv6_only_enabled(r_ip_type != IP::TYPE_ANY);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (protocol == IPPROTO_UDP) {
 | 
			
		||||
@@ -293,18 +314,59 @@ Error NetSocketUnix::open(Type p_sock_type, IP::Type &ip_type) {
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error NetSocketUnix::_unix_open() {
 | 
			
		||||
	_sock = socket(AF_UNIX, SOCK_STREAM, 0);
 | 
			
		||||
	ERR_FAIL_COND_V(_sock == -1, FAILED);
 | 
			
		||||
 | 
			
		||||
	_family = Family::UNIX;
 | 
			
		||||
 | 
			
		||||
	_set_close_exec_enabled(true);
 | 
			
		||||
 | 
			
		||||
#if defined(SO_NOSIGPIPE)
 | 
			
		||||
	// Disable SIGPIPE (should only be relevant to stream sockets, but seems to affect UDP too on iOS).
 | 
			
		||||
	int par = 1;
 | 
			
		||||
	if (setsockopt(_sock, SOL_SOCKET, SO_NOSIGPIPE, &par, sizeof(int)) != 0) {
 | 
			
		||||
		print_verbose("Unable to turn off SIGPIPE on socket.");
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error NetSocketUnix::open(NetSocket::Family p_family, NetSocket::Type p_sock_type, IP::Type &r_ip_type) {
 | 
			
		||||
	ERR_FAIL_COND_V(is_open(), ERR_ALREADY_IN_USE);
 | 
			
		||||
 | 
			
		||||
	switch (p_family) {
 | 
			
		||||
		case Family::INET:
 | 
			
		||||
			return _inet_open(p_sock_type, r_ip_type);
 | 
			
		||||
		case Family::UNIX:
 | 
			
		||||
			return _unix_open();
 | 
			
		||||
		case Family::NONE:
 | 
			
		||||
		default:
 | 
			
		||||
			return ERR_INVALID_PARAMETER;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NetSocketUnix::close() {
 | 
			
		||||
	if (_sock != -1) {
 | 
			
		||||
		::close(_sock);
 | 
			
		||||
 | 
			
		||||
		if (_family == Family::UNIX) {
 | 
			
		||||
			if (_unlink_on_close) {
 | 
			
		||||
				::unlink(_unix_path.get_data());
 | 
			
		||||
				_unlink_on_close = false;
 | 
			
		||||
				_unix_path = CharString();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_sock = -1;
 | 
			
		||||
	_family = Family::NONE;
 | 
			
		||||
	_ip_type = IP::TYPE_NONE;
 | 
			
		||||
	_is_stream = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error NetSocketUnix::bind(IPAddress p_addr, uint16_t p_port) {
 | 
			
		||||
	ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
 | 
			
		||||
Error NetSocketUnix::_inet_bind(IPAddress p_addr, uint16_t p_port) {
 | 
			
		||||
	ERR_FAIL_COND_V(!_can_use_ip(p_addr, true), ERR_INVALID_PARAMETER);
 | 
			
		||||
 | 
			
		||||
	sockaddr_storage addr;
 | 
			
		||||
@@ -320,6 +382,69 @@ Error NetSocketUnix::bind(IPAddress p_addr, uint16_t p_port) {
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error NetSocketUnix::_unix_bind(const CharString &p_path) {
 | 
			
		||||
	ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER);
 | 
			
		||||
 | 
			
		||||
	struct sockaddr_un addr;
 | 
			
		||||
	socklen_t addr_size = _unix_set_sockaddr(&addr, p_path);
 | 
			
		||||
	ERR_FAIL_COND_V(addr_size == 0, ERR_INVALID_PARAMETER);
 | 
			
		||||
 | 
			
		||||
	// If the socket file exists, attempt to remove it.
 | 
			
		||||
	if (access(p_path.get_data(), F_OK) == 0) {
 | 
			
		||||
		// Check if it's a socket
 | 
			
		||||
		struct stat st;
 | 
			
		||||
		if (stat(p_path.get_data(), &st) == 0) {
 | 
			
		||||
			if (S_ISSOCK(st.st_mode)) {
 | 
			
		||||
				// It is a socket, try to remove it.
 | 
			
		||||
				if (unlink(p_path.get_data()) != 0) {
 | 
			
		||||
					// Failed to remove existing socket file.
 | 
			
		||||
					return FAILED;
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				// It's not a socket, don't remove it.
 | 
			
		||||
				return ERR_ALREADY_EXISTS;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_unlink_on_close = true;
 | 
			
		||||
 | 
			
		||||
	if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
 | 
			
		||||
		NetError err = _get_socket_error();
 | 
			
		||||
		print_verbose("Failed to bind socket. Error: " + itos(err) + ".");
 | 
			
		||||
		close();
 | 
			
		||||
		switch (err) {
 | 
			
		||||
			case ERR_NET_UNAUTHORIZED:
 | 
			
		||||
				return ERR_UNAUTHORIZED;
 | 
			
		||||
			default:
 | 
			
		||||
				return ERR_UNAVAILABLE;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error NetSocketUnix::bind(NetSocket::Address p_addr) {
 | 
			
		||||
	ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
 | 
			
		||||
	ERR_FAIL_COND_V(_family != p_addr.get_family(), ERR_INVALID_PARAMETER);
 | 
			
		||||
	switch (p_addr.get_family()) {
 | 
			
		||||
		case Family::INET: {
 | 
			
		||||
			Error res = _inet_bind(p_addr.ip(), p_addr.port());
 | 
			
		||||
			ERR_FAIL_COND_V(res != OK, res);
 | 
			
		||||
		} break;
 | 
			
		||||
		case Family::UNIX: {
 | 
			
		||||
			_unix_path = p_addr.get_path();
 | 
			
		||||
			Error res = _unix_bind(_unix_path);
 | 
			
		||||
			ERR_FAIL_COND_V(res != OK, res);
 | 
			
		||||
		} break;
 | 
			
		||||
		case Family::NONE:
 | 
			
		||||
		default:
 | 
			
		||||
			return ERR_INVALID_PARAMETER;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error NetSocketUnix::listen(int p_max_pending) {
 | 
			
		||||
	ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
 | 
			
		||||
 | 
			
		||||
@@ -333,8 +458,7 @@ Error NetSocketUnix::listen(int p_max_pending) {
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error NetSocketUnix::connect_to_host(IPAddress p_host, uint16_t p_port) {
 | 
			
		||||
	ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
 | 
			
		||||
Error NetSocketUnix::_inet_connect_to_host(IPAddress p_host, uint16_t p_port) {
 | 
			
		||||
	ERR_FAIL_COND_V(!_can_use_ip(p_host, false), ERR_INVALID_PARAMETER);
 | 
			
		||||
 | 
			
		||||
	struct sockaddr_storage addr;
 | 
			
		||||
@@ -361,6 +485,49 @@ Error NetSocketUnix::connect_to_host(IPAddress p_host, uint16_t p_port) {
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error NetSocketUnix::_unix_connect_to_host(const CharString &p_path) {
 | 
			
		||||
	ERR_FAIL_COND_V(!_can_use_path(p_path), ERR_INVALID_PARAMETER);
 | 
			
		||||
 | 
			
		||||
	struct sockaddr_un addr;
 | 
			
		||||
	socklen_t addr_size = _unix_set_sockaddr(&addr, p_path);
 | 
			
		||||
	ERR_FAIL_COND_V(addr_size == 0, ERR_INVALID_PARAMETER);
 | 
			
		||||
 | 
			
		||||
	if (::connect(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
 | 
			
		||||
		NetError err = _get_socket_error();
 | 
			
		||||
		switch (err) {
 | 
			
		||||
			case ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE:
 | 
			
		||||
				return ERR_INVALID_PARAMETER;
 | 
			
		||||
			// Still waiting to connect, try again in a while.
 | 
			
		||||
			case ERR_NET_WOULD_BLOCK:
 | 
			
		||||
			case ERR_NET_IN_PROGRESS:
 | 
			
		||||
				return ERR_BUSY;
 | 
			
		||||
			case ERR_NET_UNAUTHORIZED:
 | 
			
		||||
				return ERR_UNAUTHORIZED;
 | 
			
		||||
			default:
 | 
			
		||||
				print_verbose("Connection to host failed.");
 | 
			
		||||
				close();
 | 
			
		||||
				return FAILED;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error NetSocketUnix::connect_to_host(NetSocket::Address p_addr) {
 | 
			
		||||
	ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
 | 
			
		||||
	ERR_FAIL_COND_V(_family != p_addr.get_family(), ERR_INVALID_PARAMETER);
 | 
			
		||||
 | 
			
		||||
	switch (p_addr.get_family()) {
 | 
			
		||||
		case Family::INET:
 | 
			
		||||
			return _inet_connect_to_host(p_addr.ip(), p_addr.port());
 | 
			
		||||
		case Family::UNIX:
 | 
			
		||||
			return _unix_connect_to_host(p_addr.get_path());
 | 
			
		||||
		case Family::NONE:
 | 
			
		||||
		default:
 | 
			
		||||
			return ERR_INVALID_PARAMETER;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error NetSocketUnix::poll(PollType p_type, int p_timeout) const {
 | 
			
		||||
	ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
 | 
			
		||||
 | 
			
		||||
@@ -418,6 +585,7 @@ Error NetSocketUnix::recv(uint8_t *p_buffer, int p_len, int &r_read) {
 | 
			
		||||
 | 
			
		||||
Error NetSocketUnix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek) {
 | 
			
		||||
	ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
 | 
			
		||||
	ERR_FAIL_COND_V(_family != Family::INET, ERR_UNAVAILABLE);
 | 
			
		||||
 | 
			
		||||
	struct sockaddr_storage from;
 | 
			
		||||
	socklen_t len = sizeof(struct sockaddr_storage);
 | 
			
		||||
@@ -459,7 +627,7 @@ Error NetSocketUnix::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
 | 
			
		||||
 | 
			
		||||
	int flags = 0;
 | 
			
		||||
#ifdef MSG_NOSIGNAL
 | 
			
		||||
	if (_is_stream) {
 | 
			
		||||
	if (_is_stream || _family == Family::UNIX) {
 | 
			
		||||
		flags = MSG_NOSIGNAL;
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -482,6 +650,7 @@ Error NetSocketUnix::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
 | 
			
		||||
 | 
			
		||||
Error NetSocketUnix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) {
 | 
			
		||||
	ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
 | 
			
		||||
	ERR_FAIL_COND_V(_family != Family::INET, ERR_UNAVAILABLE);
 | 
			
		||||
 | 
			
		||||
	struct sockaddr_storage addr;
 | 
			
		||||
	size_t addr_size = _set_addr_storage(&addr, p_ip, p_port, _ip_type);
 | 
			
		||||
@@ -580,9 +749,7 @@ int NetSocketUnix::get_available_bytes() const {
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error NetSocketUnix::get_socket_address(IPAddress *r_ip, uint16_t *r_port) const {
 | 
			
		||||
	ERR_FAIL_COND_V(!is_open(), FAILED);
 | 
			
		||||
 | 
			
		||||
Error NetSocketUnix::_inet_get_socket_address(IPAddress *r_ip, uint16_t *r_port) const {
 | 
			
		||||
	struct sockaddr_storage saddr;
 | 
			
		||||
	socklen_t len = sizeof(saddr);
 | 
			
		||||
	if (getsockname(_sock, (struct sockaddr *)&saddr, &len) != 0) {
 | 
			
		||||
@@ -594,17 +761,40 @@ Error NetSocketUnix::get_socket_address(IPAddress *r_ip, uint16_t *r_port) const
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<NetSocket> NetSocketUnix::accept(IPAddress &r_ip, uint16_t &r_port) {
 | 
			
		||||
	Ref<NetSocket> out;
 | 
			
		||||
	ERR_FAIL_COND_V(!is_open(), out);
 | 
			
		||||
Error NetSocketUnix::get_socket_address(NetSocket::Address *r_addr) const {
 | 
			
		||||
	ERR_FAIL_COND_V(!is_open(), FAILED);
 | 
			
		||||
	switch (_family) {
 | 
			
		||||
		case Family::INET: {
 | 
			
		||||
			IPAddress ip;
 | 
			
		||||
			uint16_t port = 0;
 | 
			
		||||
			Error res = _inet_get_socket_address(&ip, &port);
 | 
			
		||||
			ERR_FAIL_COND_V(res != OK, res);
 | 
			
		||||
			if (r_addr) {
 | 
			
		||||
				Address addr(ip, port);
 | 
			
		||||
				*r_addr = addr;
 | 
			
		||||
			}
 | 
			
		||||
		} break;
 | 
			
		||||
		case Family::UNIX: {
 | 
			
		||||
			if (r_addr) {
 | 
			
		||||
				*r_addr = Address(_unix_path);
 | 
			
		||||
			}
 | 
			
		||||
		} break;
 | 
			
		||||
		case Family::NONE:
 | 
			
		||||
		default:
 | 
			
		||||
			return FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<NetSocket> NetSocketUnix::_inet_accept(IPAddress &r_ip, uint16_t &r_port) {
 | 
			
		||||
	struct sockaddr_storage their_addr;
 | 
			
		||||
	socklen_t size = sizeof(their_addr);
 | 
			
		||||
	int fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size);
 | 
			
		||||
	if (fd == -1) {
 | 
			
		||||
		_get_socket_error();
 | 
			
		||||
		print_verbose("Error when accepting socket connection.");
 | 
			
		||||
		return out;
 | 
			
		||||
		return Ref<NetSocket>();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_set_ip_port(&their_addr, &r_ip, &r_port);
 | 
			
		||||
@@ -615,6 +805,48 @@ Ref<NetSocket> NetSocketUnix::accept(IPAddress &r_ip, uint16_t &r_port) {
 | 
			
		||||
	return Ref<NetSocket>(ns);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<NetSocket> NetSocketUnix::_unix_accept() {
 | 
			
		||||
	struct sockaddr_un addr;
 | 
			
		||||
	socklen_t addr_len = sizeof(addr);
 | 
			
		||||
 | 
			
		||||
	int fd = ::accept(_sock, (struct sockaddr *)&addr, &addr_len);
 | 
			
		||||
	if (fd == -1) {
 | 
			
		||||
		_get_socket_error();
 | 
			
		||||
		print_verbose("Error when accepting socket connection.");
 | 
			
		||||
		return Ref<NetSocket>();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	NetSocketUnix *ret = memnew(NetSocketUnix);
 | 
			
		||||
	ret->_sock = fd;
 | 
			
		||||
	ret->_family = _family;
 | 
			
		||||
	ret->_unix_path = _unix_path;
 | 
			
		||||
	ret->set_blocking_enabled(false);
 | 
			
		||||
	return Ref<NetSocket>(ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<NetSocket> NetSocketUnix::accept(NetSocket::Address &r_addr) {
 | 
			
		||||
	Ref<NetSocket> out;
 | 
			
		||||
	ERR_FAIL_COND_V(!is_open(), out);
 | 
			
		||||
 | 
			
		||||
	switch (_family) {
 | 
			
		||||
		case Family::INET: {
 | 
			
		||||
			IPAddress ip;
 | 
			
		||||
			uint16_t port;
 | 
			
		||||
			out = _inet_accept(ip, port);
 | 
			
		||||
			if (out.is_valid()) {
 | 
			
		||||
				r_addr = Address(ip, port);
 | 
			
		||||
			}
 | 
			
		||||
		} break;
 | 
			
		||||
		case Family::UNIX: {
 | 
			
		||||
			out = _unix_accept();
 | 
			
		||||
		} break;
 | 
			
		||||
		case Family::NONE:
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error NetSocketUnix::join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) {
 | 
			
		||||
	return _change_multicast_group(p_multi_address, p_if_name, true);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -35,14 +35,19 @@
 | 
			
		||||
#include "core/io/net_socket.h"
 | 
			
		||||
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/un.h>
 | 
			
		||||
 | 
			
		||||
class NetSocketUnix : public NetSocket {
 | 
			
		||||
	GDSOFTCLASS(NetSocketUnix, NetSocket);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	int _sock = -1;
 | 
			
		||||
	Family _family = Family::NONE;
 | 
			
		||||
	IP::Type _ip_type = IP::TYPE_NONE;
 | 
			
		||||
	bool _is_stream = false;
 | 
			
		||||
	CharString _unix_path;
 | 
			
		||||
	// If this is Family::UNIX,
 | 
			
		||||
	bool _unlink_on_close = false;
 | 
			
		||||
 | 
			
		||||
	enum NetError {
 | 
			
		||||
		ERR_NET_WOULD_BLOCK,
 | 
			
		||||
@@ -63,6 +68,19 @@ protected:
 | 
			
		||||
	static NetSocket *_create_func();
 | 
			
		||||
 | 
			
		||||
	bool _can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const;
 | 
			
		||||
	bool _can_use_path(const CharString &p_path) const;
 | 
			
		||||
 | 
			
		||||
	Error _inet_open(Type p_sock_type, IP::Type &r_ip_type);
 | 
			
		||||
	Error _inet_bind(IPAddress p_addr, uint16_t p_port);
 | 
			
		||||
	Error _inet_connect_to_host(IPAddress p_addr, uint16_t p_port);
 | 
			
		||||
	Error _inet_get_socket_address(IPAddress *r_ip, uint16_t *r_port) const;
 | 
			
		||||
	Ref<NetSocket> _inet_accept(IPAddress &r_ip, uint16_t &r_port);
 | 
			
		||||
 | 
			
		||||
	static socklen_t _unix_set_sockaddr(struct sockaddr_un *p_addr, const CharString &p_path);
 | 
			
		||||
	Error _unix_open();
 | 
			
		||||
	Error _unix_bind(const CharString &p_path);
 | 
			
		||||
	Error _unix_connect_to_host(const CharString &p_path);
 | 
			
		||||
	Ref<NetSocket> _unix_accept();
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static void make_default();
 | 
			
		||||
@@ -70,21 +88,21 @@ public:
 | 
			
		||||
	static void _set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port);
 | 
			
		||||
	static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type);
 | 
			
		||||
 | 
			
		||||
	virtual Error open(Type p_sock_type, IP::Type &ip_type) override;
 | 
			
		||||
	virtual Error open(Family p_family, Type p_sock_type, IP::Type &r_ip_type) override;
 | 
			
		||||
	virtual void close() override;
 | 
			
		||||
	virtual Error bind(IPAddress p_addr, uint16_t p_port) override;
 | 
			
		||||
	virtual Error bind(Address p_addr) override;
 | 
			
		||||
	virtual Error listen(int p_max_pending) override;
 | 
			
		||||
	virtual Error connect_to_host(IPAddress p_host, uint16_t p_port) override;
 | 
			
		||||
	virtual Error connect_to_host(Address p_addr) override;
 | 
			
		||||
	virtual Error poll(PollType p_type, int timeout) const override;
 | 
			
		||||
	virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) override;
 | 
			
		||||
	virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false) override;
 | 
			
		||||
	virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) override;
 | 
			
		||||
	virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) override;
 | 
			
		||||
	virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port) override;
 | 
			
		||||
	virtual Ref<NetSocket> accept(Address &r_addr) override;
 | 
			
		||||
 | 
			
		||||
	virtual bool is_open() const override;
 | 
			
		||||
	virtual int get_available_bytes() const override;
 | 
			
		||||
	virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const override;
 | 
			
		||||
	virtual Error get_socket_address(Address *r_addr) const override;
 | 
			
		||||
 | 
			
		||||
	virtual Error set_broadcasting_enabled(bool p_enabled) override;
 | 
			
		||||
	virtual void set_blocking_enabled(bool p_enabled) override;
 | 
			
		||||
 
 | 
			
		||||
@@ -217,7 +217,8 @@ void NetSocketWinSock::_set_socket(SOCKET p_sock, IP::Type p_ip_type, bool p_is_
 | 
			
		||||
	_is_stream = p_is_stream;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error NetSocketWinSock::open(Type p_sock_type, IP::Type &ip_type) {
 | 
			
		||||
Error NetSocketWinSock::open(Family p_family, Type p_sock_type, IP::Type &ip_type) {
 | 
			
		||||
	ERR_FAIL_COND_V(p_family != Family::INET, ERR_UNAVAILABLE);
 | 
			
		||||
	ERR_FAIL_COND_V(is_open(), ERR_ALREADY_IN_USE);
 | 
			
		||||
	ERR_FAIL_COND_V(ip_type > IP::TYPE_ANY || ip_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER);
 | 
			
		||||
 | 
			
		||||
@@ -275,12 +276,13 @@ void NetSocketWinSock::close() {
 | 
			
		||||
	_is_stream = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error NetSocketWinSock::bind(IPAddress p_addr, uint16_t p_port) {
 | 
			
		||||
Error NetSocketWinSock::bind(Address p_addr) {
 | 
			
		||||
	ERR_FAIL_COND_V(!p_addr.is_inet(), ERR_UNAVAILABLE);
 | 
			
		||||
	ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
 | 
			
		||||
	ERR_FAIL_COND_V(!_can_use_ip(p_addr, true), ERR_INVALID_PARAMETER);
 | 
			
		||||
	ERR_FAIL_COND_V(!_can_use_ip(p_addr.ip(), true), ERR_INVALID_PARAMETER);
 | 
			
		||||
 | 
			
		||||
	sockaddr_storage addr;
 | 
			
		||||
	size_t addr_size = _set_addr_storage(&addr, p_addr, p_port, _ip_type);
 | 
			
		||||
	size_t addr_size = _set_addr_storage(&addr, p_addr.ip(), p_addr.port(), _ip_type);
 | 
			
		||||
 | 
			
		||||
	if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
 | 
			
		||||
		NetError err = _get_socket_error();
 | 
			
		||||
@@ -305,12 +307,13 @@ Error NetSocketWinSock::listen(int p_max_pending) {
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error NetSocketWinSock::connect_to_host(IPAddress p_host, uint16_t p_port) {
 | 
			
		||||
Error NetSocketWinSock::connect_to_host(Address p_addr) {
 | 
			
		||||
	ERR_FAIL_COND_V(!p_addr.is_inet(), ERR_UNAVAILABLE);
 | 
			
		||||
	ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
 | 
			
		||||
	ERR_FAIL_COND_V(!_can_use_ip(p_host, false), ERR_INVALID_PARAMETER);
 | 
			
		||||
	ERR_FAIL_COND_V(!_can_use_ip(p_addr.ip(), false), ERR_INVALID_PARAMETER);
 | 
			
		||||
 | 
			
		||||
	struct sockaddr_storage addr;
 | 
			
		||||
	size_t addr_size = _set_addr_storage(&addr, p_host, p_port, _ip_type);
 | 
			
		||||
	size_t addr_size = _set_addr_storage(&addr, p_addr.ip(), p_addr.port(), _ip_type);
 | 
			
		||||
 | 
			
		||||
	if (::WSAConnect(_sock, (struct sockaddr *)&addr, addr_size, nullptr, nullptr, nullptr, nullptr) != 0) {
 | 
			
		||||
		NetError err = _get_socket_error();
 | 
			
		||||
@@ -567,7 +570,7 @@ int NetSocketWinSock::get_available_bytes() const {
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error NetSocketWinSock::get_socket_address(IPAddress *r_ip, uint16_t *r_port) const {
 | 
			
		||||
Error NetSocketWinSock::get_socket_address(Address *r_addr) const {
 | 
			
		||||
	ERR_FAIL_COND_V(!is_open(), FAILED);
 | 
			
		||||
 | 
			
		||||
	struct sockaddr_storage saddr;
 | 
			
		||||
@@ -577,11 +580,16 @@ Error NetSocketWinSock::get_socket_address(IPAddress *r_ip, uint16_t *r_port) co
 | 
			
		||||
		print_verbose("Error when reading local socket address.");
 | 
			
		||||
		return FAILED;
 | 
			
		||||
	}
 | 
			
		||||
	_set_ip_port(&saddr, r_ip, r_port);
 | 
			
		||||
	IPAddress ip;
 | 
			
		||||
	uint16_t port = 0;
 | 
			
		||||
	_set_ip_port(&saddr, &ip, &port);
 | 
			
		||||
	if (r_addr) {
 | 
			
		||||
		*r_addr = Address(ip, port);
 | 
			
		||||
	}
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<NetSocket> NetSocketWinSock::accept(IPAddress &r_ip, uint16_t &r_port) {
 | 
			
		||||
Ref<NetSocket> NetSocketWinSock::accept(Address &r_addr) {
 | 
			
		||||
	Ref<NetSocket> out;
 | 
			
		||||
	ERR_FAIL_COND_V(!is_open(), out);
 | 
			
		||||
 | 
			
		||||
@@ -594,7 +602,10 @@ Ref<NetSocket> NetSocketWinSock::accept(IPAddress &r_ip, uint16_t &r_port) {
 | 
			
		||||
		return out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_set_ip_port(&their_addr, &r_ip, &r_port);
 | 
			
		||||
	IPAddress ip;
 | 
			
		||||
	uint16_t port = 0;
 | 
			
		||||
	_set_ip_port(&their_addr, &ip, &port);
 | 
			
		||||
	r_addr = Address(ip, port);
 | 
			
		||||
 | 
			
		||||
	NetSocketWinSock *ns = memnew(NetSocketWinSock);
 | 
			
		||||
	ns->_set_socket(fd, _ip_type, _is_stream);
 | 
			
		||||
 
 | 
			
		||||
@@ -70,21 +70,21 @@ public:
 | 
			
		||||
	static void _set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port);
 | 
			
		||||
	static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type);
 | 
			
		||||
 | 
			
		||||
	virtual Error open(Type p_sock_type, IP::Type &ip_type) override;
 | 
			
		||||
	virtual Error open(Family p_family, Type p_sock_type, IP::Type &ip_type) override;
 | 
			
		||||
	virtual void close() override;
 | 
			
		||||
	virtual Error bind(IPAddress p_addr, uint16_t p_port) override;
 | 
			
		||||
	virtual Error bind(Address p_addr) override;
 | 
			
		||||
	virtual Error listen(int p_max_pending) override;
 | 
			
		||||
	virtual Error connect_to_host(IPAddress p_host, uint16_t p_port) override;
 | 
			
		||||
	virtual Error connect_to_host(Address p_addr) override;
 | 
			
		||||
	virtual Error poll(PollType p_type, int timeout) const override;
 | 
			
		||||
	virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) override;
 | 
			
		||||
	virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false) override;
 | 
			
		||||
	virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) override;
 | 
			
		||||
	virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) override;
 | 
			
		||||
	virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port) override;
 | 
			
		||||
	virtual Ref<NetSocket> accept(Address &r_addr) override;
 | 
			
		||||
 | 
			
		||||
	virtual bool is_open() const override;
 | 
			
		||||
	virtual int get_available_bytes() const override;
 | 
			
		||||
	virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const override;
 | 
			
		||||
	virtual Error get_socket_address(Address *r_addr) const override;
 | 
			
		||||
 | 
			
		||||
	virtual Error set_broadcasting_enabled(bool p_enabled) override;
 | 
			
		||||
	virtual void set_blocking_enabled(bool p_enabled) override;
 | 
			
		||||
 
 | 
			
		||||
@@ -243,8 +243,7 @@ ScriptEditorDebugger *EditorDebuggerNode::get_default_debugger() const {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String EditorDebuggerNode::get_server_uri() const {
 | 
			
		||||
	ERR_FAIL_COND_V(server.is_null(), "");
 | 
			
		||||
	return server->get_uri();
 | 
			
		||||
	return server.is_valid() ? server->get_uri() : "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EditorDebuggerNode::set_keep_open(bool p_keep_open) {
 | 
			
		||||
 
 | 
			
		||||
@@ -31,30 +31,36 @@
 | 
			
		||||
#include "editor_debugger_server.h"
 | 
			
		||||
 | 
			
		||||
#include "core/io/tcp_server.h"
 | 
			
		||||
#include "core/io/uds_server.h"
 | 
			
		||||
#include "core/os/thread.h"
 | 
			
		||||
#include "editor/editor_log.h"
 | 
			
		||||
#include "editor/editor_node.h"
 | 
			
		||||
#include "editor/settings/editor_settings.h"
 | 
			
		||||
 | 
			
		||||
class EditorDebuggerServerTCP : public EditorDebuggerServer {
 | 
			
		||||
	GDSOFTCLASS(EditorDebuggerServerTCP, EditorDebuggerServer);
 | 
			
		||||
template <typename T>
 | 
			
		||||
class EditorDebuggerServerSocket : public EditorDebuggerServer {
 | 
			
		||||
	GDSOFTCLASS(EditorDebuggerServerSocket, EditorDebuggerServer);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Ref<TCPServer> server;
 | 
			
		||||
protected:
 | 
			
		||||
	Ref<T> server;
 | 
			
		||||
	String endpoint;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static EditorDebuggerServer *create(const String &p_protocol);
 | 
			
		||||
 | 
			
		||||
	virtual void poll() override {}
 | 
			
		||||
	virtual String get_uri() const override;
 | 
			
		||||
	virtual Error start(const String &p_uri) override;
 | 
			
		||||
	virtual void stop() override;
 | 
			
		||||
	virtual bool is_active() const override;
 | 
			
		||||
	virtual bool is_connection_available() const override;
 | 
			
		||||
	virtual Ref<RemoteDebuggerPeer> take_connection() override;
 | 
			
		||||
 | 
			
		||||
	EditorDebuggerServerTCP();
 | 
			
		||||
	EditorDebuggerServerSocket();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class EditorDebuggerServerTCP : public EditorDebuggerServerSocket<TCPServer> {
 | 
			
		||||
public:
 | 
			
		||||
	static EditorDebuggerServer *create(const String &p_protocol);
 | 
			
		||||
 | 
			
		||||
	virtual Error start(const String &p_uri) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
EditorDebuggerServer *EditorDebuggerServerTCP::create(const String &p_protocol) {
 | 
			
		||||
@@ -62,11 +68,13 @@ EditorDebuggerServer *EditorDebuggerServerTCP::create(const String &p_protocol)
 | 
			
		||||
	return memnew(EditorDebuggerServerTCP);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EditorDebuggerServerTCP::EditorDebuggerServerTCP() {
 | 
			
		||||
template <typename T>
 | 
			
		||||
EditorDebuggerServerSocket<T>::EditorDebuggerServerSocket() {
 | 
			
		||||
	server.instantiate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String EditorDebuggerServerTCP::get_uri() const {
 | 
			
		||||
template <typename T>
 | 
			
		||||
String EditorDebuggerServerSocket<T>::get_uri() const {
 | 
			
		||||
	return endpoint;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -104,29 +112,61 @@ Error EditorDebuggerServerTCP::start(const String &p_uri) {
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EditorDebuggerServerTCP::stop() {
 | 
			
		||||
template <typename T>
 | 
			
		||||
void EditorDebuggerServerSocket<T>::stop() {
 | 
			
		||||
	server->stop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool EditorDebuggerServerTCP::is_active() const {
 | 
			
		||||
template <typename T>
 | 
			
		||||
bool EditorDebuggerServerSocket<T>::is_active() const {
 | 
			
		||||
	return server->is_listening();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool EditorDebuggerServerTCP::is_connection_available() const {
 | 
			
		||||
template <typename T>
 | 
			
		||||
bool EditorDebuggerServerSocket<T>::is_connection_available() const {
 | 
			
		||||
	return server->is_listening() && server->is_connection_available();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<RemoteDebuggerPeer> EditorDebuggerServerTCP::take_connection() {
 | 
			
		||||
	ERR_FAIL_COND_V(!is_connection_available(), Ref<RemoteDebuggerPeer>());
 | 
			
		||||
	return memnew(RemoteDebuggerPeerTCP(server->take_connection()));
 | 
			
		||||
template <typename T>
 | 
			
		||||
Ref<RemoteDebuggerPeer> EditorDebuggerServerSocket<T>::take_connection() {
 | 
			
		||||
	const Ref<RemoteDebuggerPeer> out;
 | 
			
		||||
	ERR_FAIL_COND_V(!is_connection_available(), out);
 | 
			
		||||
	Ref<StreamPeerSocket> stream = server->take_socket_connection();
 | 
			
		||||
	ERR_FAIL_COND_V(stream.is_null(), out);
 | 
			
		||||
	return memnew(RemoteDebuggerPeerTCP(stream));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class EditorDebuggerServerUDS : public EditorDebuggerServerSocket<UDSServer> {
 | 
			
		||||
public:
 | 
			
		||||
	static EditorDebuggerServer *create(const String &p_protocol);
 | 
			
		||||
 | 
			
		||||
	virtual Error start(const String &p_uri) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
EditorDebuggerServer *EditorDebuggerServerUDS::create(const String &p_protocol) {
 | 
			
		||||
	ERR_FAIL_COND_V(p_protocol != "unix://", nullptr);
 | 
			
		||||
	return memnew(EditorDebuggerServerUDS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error EditorDebuggerServerUDS::start(const String &p_uri) {
 | 
			
		||||
	String bind_path = p_uri.is_empty() ? String("/tmp/godot_debugger.sock") : p_uri.replace("unix://", "");
 | 
			
		||||
 | 
			
		||||
	const Error err = server->listen(bind_path);
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		EditorNode::get_log()->add_message(vformat("Cannot listen at path %s, remote debugging unavailable.", bind_path), EditorLog::MSG_TYPE_ERROR);
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
	endpoint = "unix://" + bind_path;
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// EditorDebuggerServer
 | 
			
		||||
HashMap<StringName, EditorDebuggerServer::CreateServerFunc> EditorDebuggerServer::protocols;
 | 
			
		||||
 | 
			
		||||
EditorDebuggerServer *EditorDebuggerServer::create(const String &p_protocol) {
 | 
			
		||||
	ERR_FAIL_COND_V(!protocols.has(p_protocol), nullptr);
 | 
			
		||||
	return protocols[p_protocol](p_protocol);
 | 
			
		||||
	CreateServerFunc *create_fn = protocols.getptr(p_protocol);
 | 
			
		||||
	ERR_FAIL_NULL_V(create_fn, nullptr);
 | 
			
		||||
	return (*create_fn)(p_protocol);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EditorDebuggerServer::register_protocol_handler(const String &p_protocol, CreateServerFunc p_func) {
 | 
			
		||||
@@ -136,6 +176,9 @@ void EditorDebuggerServer::register_protocol_handler(const String &p_protocol, C
 | 
			
		||||
 | 
			
		||||
void EditorDebuggerServer::initialize() {
 | 
			
		||||
	register_protocol_handler("tcp://", EditorDebuggerServerTCP::create);
 | 
			
		||||
#if defined(UNIX_ENABLED)
 | 
			
		||||
	register_protocol_handler("unix://", EditorDebuggerServerUDS::create);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EditorDebuggerServer::deinitialize() {
 | 
			
		||||
 
 | 
			
		||||
@@ -314,8 +314,12 @@ void EditorRunBar::_run_scene(const String &p_scene_path, const Vector<String> &
 | 
			
		||||
	if (!EditorNode::get_singleton()->call_build()) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	EditorDebuggerNode::get_singleton()->start();
 | 
			
		||||
	// Use the existing URI, in case it is overridden by the CLI.
 | 
			
		||||
	String uri = EditorDebuggerNode::get_singleton()->get_server_uri();
 | 
			
		||||
	if (uri.is_empty()) {
 | 
			
		||||
		uri = "tcp://";
 | 
			
		||||
	}
 | 
			
		||||
	EditorDebuggerNode::get_singleton()->start(uri);
 | 
			
		||||
	Error error = editor_run.run(run_filename, write_movie_file, p_run_args);
 | 
			
		||||
	if (error != OK) {
 | 
			
		||||
		EditorDebuggerNode::get_singleton()->stop();
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,18 @@ Add new entries at the end of the file.
 | 
			
		||||
 | 
			
		||||
## Changes between 4.5-stable and 4.6-stable
 | 
			
		||||
 | 
			
		||||
GH-107954
 | 
			
		||||
---------
 | 
			
		||||
Validate extension JSON: API was removed: classes/TCPServer/methods/is_connection_available
 | 
			
		||||
Validate extension JSON: API was removed: classes/TCPServer/methods/is_listening
 | 
			
		||||
Validate extension JSON: API was removed: classes/TCPServer/methods/stop
 | 
			
		||||
Validate extension JSON: API was removed: classes/StreamPeerTCP/methods/disconnect_from_host
 | 
			
		||||
Validate extension JSON: API was removed: classes/StreamPeerTCP/methods/get_status
 | 
			
		||||
Validate extension JSON: API was removed: classes/StreamPeerTCP/methods/poll
 | 
			
		||||
 | 
			
		||||
These were moved to the parent classes, and are still available.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GH-110250
 | 
			
		||||
---------
 | 
			
		||||
Validate extension JSON: JSON file: Field was added in a way that breaks compatibility 'classes/Control/methods/grab_focus': arguments
 | 
			
		||||
 
 | 
			
		||||
@@ -43,21 +43,21 @@ protected:
 | 
			
		||||
public:
 | 
			
		||||
	static void make_default();
 | 
			
		||||
 | 
			
		||||
	virtual Error open(Type p_sock_type, IP::Type &ip_type) override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual Error open(Family p_family, Type p_sock_type, IP::Type &ip_type) override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual void close() override {}
 | 
			
		||||
	virtual Error bind(IPAddress p_addr, uint16_t p_port) override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual Error bind(Address p_addr) override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual Error listen(int p_max_pending) override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual Error connect_to_host(IPAddress p_host, uint16_t p_port) override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual Error connect_to_host(Address p_addr) override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual Error poll(PollType p_type, int timeout) const override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false) override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port) override { return Ref<NetSocket>(); }
 | 
			
		||||
	virtual Ref<NetSocket> accept(Address &r_addr) override { return Ref<NetSocket>(); }
 | 
			
		||||
 | 
			
		||||
	virtual bool is_open() const override { return false; }
 | 
			
		||||
	virtual int get_available_bytes() const override { return -1; }
 | 
			
		||||
	virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual Error get_socket_address(Address *r_addr) const override { return ERR_UNAVAILABLE; }
 | 
			
		||||
 | 
			
		||||
	virtual Error set_broadcasting_enabled(bool p_enabled) override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual void set_blocking_enabled(bool p_enabled) override {}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										302
									
								
								tests/core/io/test_uds_server.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								tests/core/io/test_uds_server.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,302 @@
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*  test_uds_server.h                                                     */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
/*                        https://godotengine.org                         */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
 | 
			
		||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* Permission is hereby granted, free of charge, to any person obtaining  */
 | 
			
		||||
/* a copy of this software and associated documentation files (the        */
 | 
			
		||||
/* "Software"), to deal in the Software without restriction, including    */
 | 
			
		||||
/* without limitation the rights to use, copy, modify, merge, publish,    */
 | 
			
		||||
/* distribute, sublicense, and/or sell copies of the Software, and to     */
 | 
			
		||||
/* permit persons to whom the Software is furnished to do so, subject to  */
 | 
			
		||||
/* the following conditions:                                              */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* The above copyright notice and this permission notice shall be         */
 | 
			
		||||
/* included in all copies or substantial portions of the Software.        */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
 | 
			
		||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
 | 
			
		||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
 | 
			
		||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
 | 
			
		||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
 | 
			
		||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
 | 
			
		||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "core/io/file_access.h"
 | 
			
		||||
#include "core/io/stream_peer_uds.h"
 | 
			
		||||
#include "core/io/uds_server.h"
 | 
			
		||||
#include "tests/test_macros.h"
 | 
			
		||||
 | 
			
		||||
#include <functional>
 | 
			
		||||
 | 
			
		||||
namespace TestUDSServer {
 | 
			
		||||
 | 
			
		||||
#ifdef UNIX_ENABLED
 | 
			
		||||
 | 
			
		||||
const String SOCKET_PATH = "/tmp/godot_test_uds_socket";
 | 
			
		||||
const uint32_t SLEEP_DURATION = 1000;
 | 
			
		||||
const uint64_t MAX_WAIT_USEC = 2000000;
 | 
			
		||||
 | 
			
		||||
void wait_for_condition(std::function<bool()> f_test) {
 | 
			
		||||
	const uint64_t time = OS::get_singleton()->get_ticks_usec();
 | 
			
		||||
	while (!f_test() && (OS::get_singleton()->get_ticks_usec() - time) < MAX_WAIT_USEC) {
 | 
			
		||||
		OS::get_singleton()->delay_usec(SLEEP_DURATION);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cleanup_socket_file() {
 | 
			
		||||
	// Remove socket file if it exists
 | 
			
		||||
	if (FileAccess::exists(SOCKET_PATH)) {
 | 
			
		||||
		DirAccess::remove_absolute(SOCKET_PATH);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<UDSServer> create_server(const String &p_path) {
 | 
			
		||||
	cleanup_socket_file();
 | 
			
		||||
 | 
			
		||||
	Ref<UDSServer> server;
 | 
			
		||||
	server.instantiate();
 | 
			
		||||
 | 
			
		||||
	REQUIRE_EQ(server->listen(p_path), Error::OK);
 | 
			
		||||
	REQUIRE(server->is_listening());
 | 
			
		||||
	CHECK_FALSE(server->is_connection_available());
 | 
			
		||||
 | 
			
		||||
	return server;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<StreamPeerUDS> create_client(const String &p_path) {
 | 
			
		||||
	Ref<StreamPeerUDS> client;
 | 
			
		||||
	client.instantiate();
 | 
			
		||||
 | 
			
		||||
	Error err = client->connect_to_host(p_path);
 | 
			
		||||
	REQUIRE_EQ(err, Error::OK);
 | 
			
		||||
 | 
			
		||||
	// UDS connections may be immediately connected or in connecting state
 | 
			
		||||
	StreamPeerUDS::Status status = client->get_status();
 | 
			
		||||
	REQUIRE((status == StreamPeerUDS::STATUS_CONNECTED || status == StreamPeerUDS::STATUS_CONNECTING));
 | 
			
		||||
 | 
			
		||||
	if (status == StreamPeerUDS::STATUS_CONNECTED) {
 | 
			
		||||
		CHECK_EQ(client->get_connected_path(), p_path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return client;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<StreamPeerUDS> accept_connection(Ref<UDSServer> &p_server) {
 | 
			
		||||
	wait_for_condition([&]() {
 | 
			
		||||
		return p_server->is_connection_available();
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	REQUIRE(p_server->is_connection_available());
 | 
			
		||||
	Ref<StreamPeerUDS> client_from_server = p_server->take_connection();
 | 
			
		||||
	REQUIRE(client_from_server.is_valid());
 | 
			
		||||
	CHECK_EQ(client_from_server->get_status(), StreamPeerUDS::STATUS_CONNECTED);
 | 
			
		||||
 | 
			
		||||
	return client_from_server;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE("[UDSServer] Instantiation") {
 | 
			
		||||
	Ref<UDSServer> server;
 | 
			
		||||
	server.instantiate();
 | 
			
		||||
 | 
			
		||||
	REQUIRE(server.is_valid());
 | 
			
		||||
	CHECK_FALSE(server->is_listening());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE("[UDSServer] Accept a connection and receive/send data") {
 | 
			
		||||
	Ref<UDSServer> server = create_server(SOCKET_PATH);
 | 
			
		||||
	Ref<StreamPeerUDS> client = create_client(SOCKET_PATH);
 | 
			
		||||
	Ref<StreamPeerUDS> client_from_server = accept_connection(server);
 | 
			
		||||
 | 
			
		||||
	wait_for_condition([&]() {
 | 
			
		||||
		return client->poll() != Error::OK || client->get_status() == StreamPeerUDS::STATUS_CONNECTED;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	CHECK_EQ(client->get_status(), StreamPeerUDS::STATUS_CONNECTED);
 | 
			
		||||
 | 
			
		||||
	// Sending data from client to server.
 | 
			
		||||
	const String hello_world = "Hello World!";
 | 
			
		||||
	client->put_string(hello_world);
 | 
			
		||||
	CHECK_EQ(client_from_server->get_string(), hello_world);
 | 
			
		||||
 | 
			
		||||
	// Sending data from server to client.
 | 
			
		||||
	const float pi = 3.1415;
 | 
			
		||||
	client_from_server->put_float(pi);
 | 
			
		||||
	CHECK_EQ(client->get_float(), pi);
 | 
			
		||||
 | 
			
		||||
	client->disconnect_from_host();
 | 
			
		||||
	server->stop();
 | 
			
		||||
	CHECK_FALSE(server->is_listening());
 | 
			
		||||
 | 
			
		||||
	cleanup_socket_file();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE("[UDSServer] Handle multiple clients at the same time") {
 | 
			
		||||
	Ref<UDSServer> server = create_server(SOCKET_PATH);
 | 
			
		||||
 | 
			
		||||
	Vector<Ref<StreamPeerUDS>> clients;
 | 
			
		||||
	for (int i = 0; i < 5; i++) {
 | 
			
		||||
		clients.push_back(create_client(SOCKET_PATH));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Vector<Ref<StreamPeerUDS>> clients_from_server;
 | 
			
		||||
	for (int i = 0; i < clients.size(); i++) {
 | 
			
		||||
		clients_from_server.push_back(accept_connection(server));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wait_for_condition([&]() {
 | 
			
		||||
		bool should_exit = true;
 | 
			
		||||
		for (Ref<StreamPeerUDS> &c : clients) {
 | 
			
		||||
			if (c->poll() != Error::OK) {
 | 
			
		||||
				return true;
 | 
			
		||||
			}
 | 
			
		||||
			StreamPeerUDS::Status status = c->get_status();
 | 
			
		||||
			if (status != StreamPeerUDS::STATUS_CONNECTED && status != StreamPeerUDS::STATUS_CONNECTING) {
 | 
			
		||||
				return true;
 | 
			
		||||
			}
 | 
			
		||||
			if (status != StreamPeerUDS::STATUS_CONNECTED) {
 | 
			
		||||
				should_exit = false;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return should_exit;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	for (Ref<StreamPeerUDS> &c : clients) {
 | 
			
		||||
		REQUIRE_EQ(c->get_status(), StreamPeerUDS::STATUS_CONNECTED);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Sending data from each client to server.
 | 
			
		||||
	for (int i = 0; i < clients.size(); i++) {
 | 
			
		||||
		String hello_client = "Hello " + itos(i);
 | 
			
		||||
		clients[i]->put_string(hello_client);
 | 
			
		||||
		CHECK_EQ(clients_from_server[i]->get_string(), hello_client);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (Ref<StreamPeerUDS> &c : clients) {
 | 
			
		||||
		c->disconnect_from_host();
 | 
			
		||||
	}
 | 
			
		||||
	server->stop();
 | 
			
		||||
 | 
			
		||||
	cleanup_socket_file();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE("[UDSServer] When stopped shouldn't accept new connections") {
 | 
			
		||||
	Ref<UDSServer> server = create_server(SOCKET_PATH);
 | 
			
		||||
	Ref<StreamPeerUDS> client = create_client(SOCKET_PATH);
 | 
			
		||||
	Ref<StreamPeerUDS> client_from_server = accept_connection(server);
 | 
			
		||||
 | 
			
		||||
	wait_for_condition([&]() {
 | 
			
		||||
		return client->poll() != Error::OK || client->get_status() == StreamPeerUDS::STATUS_CONNECTED;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	CHECK_EQ(client->get_status(), StreamPeerUDS::STATUS_CONNECTED);
 | 
			
		||||
 | 
			
		||||
	// Sending data from client to server.
 | 
			
		||||
	const String hello_world = "Hello World!";
 | 
			
		||||
	client->put_string(hello_world);
 | 
			
		||||
	CHECK_EQ(client_from_server->get_string(), hello_world);
 | 
			
		||||
 | 
			
		||||
	client->disconnect_from_host();
 | 
			
		||||
	server->stop();
 | 
			
		||||
	CHECK_FALSE(server->is_listening());
 | 
			
		||||
 | 
			
		||||
	// Clean up the socket file after server stops
 | 
			
		||||
	cleanup_socket_file();
 | 
			
		||||
 | 
			
		||||
	// Try to connect to non-existent socket
 | 
			
		||||
	Ref<StreamPeerUDS> new_client;
 | 
			
		||||
	new_client.instantiate();
 | 
			
		||||
	Error err = new_client->connect_to_host(SOCKET_PATH);
 | 
			
		||||
 | 
			
		||||
	// Connection should fail since socket doesn't exist
 | 
			
		||||
	CHECK_NE(err, Error::OK);
 | 
			
		||||
	CHECK_FALSE(server->is_connection_available());
 | 
			
		||||
 | 
			
		||||
	cleanup_socket_file();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE("[UDSServer] Should disconnect client") {
 | 
			
		||||
	Ref<UDSServer> server = create_server(SOCKET_PATH);
 | 
			
		||||
	Ref<StreamPeerUDS> client = create_client(SOCKET_PATH);
 | 
			
		||||
	Ref<StreamPeerUDS> client_from_server = accept_connection(server);
 | 
			
		||||
 | 
			
		||||
	wait_for_condition([&]() {
 | 
			
		||||
		return client->poll() != Error::OK || client->get_status() == StreamPeerUDS::STATUS_CONNECTED;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	CHECK_EQ(client->get_status(), StreamPeerUDS::STATUS_CONNECTED);
 | 
			
		||||
 | 
			
		||||
	// Sending data from client to server.
 | 
			
		||||
	const String hello_world = "Hello World!";
 | 
			
		||||
	client->put_string(hello_world);
 | 
			
		||||
	CHECK_EQ(client_from_server->get_string(), hello_world);
 | 
			
		||||
 | 
			
		||||
	client_from_server->disconnect_from_host();
 | 
			
		||||
	server->stop();
 | 
			
		||||
	CHECK_FALSE(server->is_listening());
 | 
			
		||||
 | 
			
		||||
	// Wait for disconnection
 | 
			
		||||
	wait_for_condition([&]() {
 | 
			
		||||
		return client->poll() != Error::OK || client->get_status() == StreamPeerUDS::STATUS_NONE;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	// Wait for disconnection
 | 
			
		||||
	wait_for_condition([&]() {
 | 
			
		||||
		return client_from_server->poll() != Error::OK || client_from_server->get_status() == StreamPeerUDS::STATUS_NONE;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	CHECK_EQ(client->get_status(), StreamPeerUDS::STATUS_NONE);
 | 
			
		||||
	CHECK_EQ(client_from_server->get_status(), StreamPeerUDS::STATUS_NONE);
 | 
			
		||||
 | 
			
		||||
	ERR_PRINT_OFF;
 | 
			
		||||
	CHECK_EQ(client->get_string(), String());
 | 
			
		||||
	CHECK_EQ(client_from_server->get_string(), String());
 | 
			
		||||
	ERR_PRINT_ON;
 | 
			
		||||
 | 
			
		||||
	cleanup_socket_file();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE("[UDSServer] Test with different socket paths") {
 | 
			
		||||
	// Test with a different socket path
 | 
			
		||||
	const String alt_socket_path = "/tmp/godot_test_uds_socket_alt";
 | 
			
		||||
 | 
			
		||||
	// Clean up before test
 | 
			
		||||
	if (FileAccess::exists(alt_socket_path)) {
 | 
			
		||||
		DirAccess::remove_absolute(alt_socket_path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Ref<UDSServer> server = create_server(alt_socket_path);
 | 
			
		||||
	Ref<StreamPeerUDS> client = create_client(alt_socket_path);
 | 
			
		||||
	Ref<StreamPeerUDS> client_from_server = accept_connection(server);
 | 
			
		||||
 | 
			
		||||
	wait_for_condition([&]() {
 | 
			
		||||
		return client->poll() != Error::OK || client->get_status() == StreamPeerUDS::STATUS_CONNECTED;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	CHECK_EQ(client->get_status(), StreamPeerUDS::STATUS_CONNECTED);
 | 
			
		||||
 | 
			
		||||
	// Test data exchange
 | 
			
		||||
	const int test_number = 42;
 | 
			
		||||
	client->put_32(test_number);
 | 
			
		||||
	CHECK_EQ(client_from_server->get_32(), test_number);
 | 
			
		||||
 | 
			
		||||
	client->disconnect_from_host();
 | 
			
		||||
	server->stop();
 | 
			
		||||
 | 
			
		||||
	// Clean up
 | 
			
		||||
	if (FileAccess::exists(alt_socket_path)) {
 | 
			
		||||
		DirAccess::remove_absolute(alt_socket_path);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
} // namespace TestUDSServer
 | 
			
		||||
@@ -62,6 +62,7 @@
 | 
			
		||||
#include "tests/core/io/test_stream_peer_gzip.h"
 | 
			
		||||
#include "tests/core/io/test_tcp_server.h"
 | 
			
		||||
#include "tests/core/io/test_udp_server.h"
 | 
			
		||||
#include "tests/core/io/test_uds_server.h"
 | 
			
		||||
#include "tests/core/io/test_xml_parser.h"
 | 
			
		||||
#include "tests/core/math/test_aabb.h"
 | 
			
		||||
#include "tests/core/math/test_astar.h"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								thirdparty/enet/enet_godot.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								thirdparty/enet/enet_godot.cpp
									
									
									
									
										vendored
									
									
								
							@@ -74,7 +74,7 @@ public:
 | 
			
		||||
	ENetUDP() {
 | 
			
		||||
		sock = Ref<NetSocket>(NetSocket::create());
 | 
			
		||||
		IP::Type ip_type = IP::TYPE_ANY;
 | 
			
		||||
		sock->open(NetSocket::TYPE_UDP, ip_type);
 | 
			
		||||
		sock->open(NetSocket::Family::INET, NetSocket::TYPE_UDP, ip_type);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	~ENetUDP() {
 | 
			
		||||
@@ -88,11 +88,15 @@ public:
 | 
			
		||||
	Error bind(IPAddress p_ip, uint16_t p_port) {
 | 
			
		||||
		local_address = p_ip;
 | 
			
		||||
		bound = true;
 | 
			
		||||
		return sock->bind(p_ip, p_port);
 | 
			
		||||
		NetSocket::Address addr(p_ip, p_port);
 | 
			
		||||
		return sock->bind(addr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) {
 | 
			
		||||
		Error err = sock->get_socket_address(r_ip, r_port);
 | 
			
		||||
		NetSocket::Address addr;
 | 
			
		||||
		Error err = sock->get_socket_address(&addr);
 | 
			
		||||
		*r_ip = addr.ip();
 | 
			
		||||
		*r_port = addr.port();
 | 
			
		||||
		if (bound) {
 | 
			
		||||
			*r_ip = local_address;
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user