feat: updated engine version to 4.4-rc1

This commit is contained in:
Sara 2025-02-23 14:38:14 +01:00
parent ee00efde1f
commit 21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -397,14 +397,30 @@ Error DirAccessUnix::rename(String p_path, String p_new_path) {
}
p_path = fix_path(p_path);
if (p_path.ends_with("/")) {
p_path = p_path.left(-1);
}
if (p_new_path.is_relative_path()) {
p_new_path = get_current_dir().path_join(p_new_path);
}
p_new_path = fix_path(p_new_path);
if (p_new_path.ends_with("/")) {
p_new_path = p_new_path.left(-1);
}
return ::rename(p_path.utf8().get_data(), p_new_path.utf8().get_data()) == 0 ? OK : FAILED;
int res = ::rename(p_path.utf8().get_data(), p_new_path.utf8().get_data());
if (res != 0 && errno == EXDEV) { // Cross-device move, use copy and remove.
Error err = OK;
err = copy(p_path, p_new_path);
if (err != OK) {
return err;
}
return remove(p_path);
} else {
return (res == 0) ? OK : FAILED;
}
}
Error DirAccessUnix::remove(String p_path) {
@ -413,17 +429,28 @@ Error DirAccessUnix::remove(String p_path) {
}
p_path = fix_path(p_path);
if (p_path.ends_with("/")) {
p_path = p_path.left(-1);
}
struct stat flags = {};
if ((stat(p_path.utf8().get_data(), &flags) != 0)) {
if ((lstat(p_path.utf8().get_data(), &flags) != 0)) {
return FAILED;
}
int err;
if (S_ISDIR(flags.st_mode) && !is_link(p_path)) {
return ::rmdir(p_path.utf8().get_data()) == 0 ? OK : FAILED;
err = ::rmdir(p_path.utf8().get_data());
} else {
return ::unlink(p_path.utf8().get_data()) == 0 ? OK : FAILED;
err = ::unlink(p_path.utf8().get_data());
}
if (err != 0) {
return FAILED;
}
if (remove_notification_func != nullptr) {
remove_notification_func(p_path);
}
return OK;
}
bool DirAccessUnix::is_link(String p_file) {
@ -432,6 +459,9 @@ bool DirAccessUnix::is_link(String p_file) {
}
p_file = fix_path(p_file);
if (p_file.ends_with("/")) {
p_file = p_file.left(-1);
}
struct stat flags = {};
if ((lstat(p_file.utf8().get_data(), &flags) != 0)) {
@ -447,6 +477,9 @@ String DirAccessUnix::read_link(String p_file) {
}
p_file = fix_path(p_file);
if (p_file.ends_with("/")) {
p_file = p_file.left(-1);
}
char buf[256];
memset(buf, 0, 256);
@ -527,6 +560,8 @@ DirAccessUnix::DirAccessUnix() {
change_dir(current_dir);
}
DirAccessUnix::RemoveNotificationFunc DirAccessUnix::remove_notification_func = nullptr;
DirAccessUnix::~DirAccessUnix() {
list_dir_end();
}

View file

@ -52,6 +52,9 @@ protected:
virtual bool is_hidden(const String &p_name);
public:
typedef void (*RemoveNotificationFunc)(const String &p_file);
static RemoveNotificationFunc remove_notification_func;
virtual Error list_dir_begin() override; ///< This starts dir listing
virtual String get_next() override;
virtual bool current_is_dir() const override;

View file

@ -41,10 +41,23 @@
#include <sys/types.h>
#include <unistd.h>
void FileAccessUnix::check_errors() const {
#if defined(TOOLS_ENABLED)
#include <limits.h>
#include <stdlib.h>
#endif
void FileAccessUnix::check_errors(bool p_write) const {
ERR_FAIL_NULL_MSG(f, "File must be opened before use.");
if (feof(f)) {
last_error = OK;
if (ferror(f)) {
if (p_write) {
last_error = ERR_FILE_CANT_WRITE;
} else {
last_error = ERR_FILE_CANT_READ;
}
}
if (!p_write && feof(f)) {
last_error = ERR_FILE_EOF;
}
}
@ -87,6 +100,22 @@ Error FileAccessUnix::open_internal(const String &p_path, int p_mode_flags) {
}
}
#if defined(TOOLS_ENABLED)
if (p_mode_flags & READ) {
String real_path = get_real_path();
if (real_path != path) {
// Don't warn on symlinks, since they can be used to simply share addons on multiple projects.
if (real_path.to_lower() == path.to_lower()) {
// The File system is case insensitive, but other platforms can be sensitive to it
// To ease cross-platform development, we issue a warning if users try to access
// a file using the wrong case (which *works* on Windows and macOS, but won't on other
// platforms).
WARN_PRINT(vformat("Case mismatch opening requested file '%s', stored as '%s' in the filesystem. This file will not open when exported to other case-sensitive platforms.", path, real_path));
}
}
}
#endif
if (is_backup_save_enabled() && (p_mode_flags == WRITE)) {
save_path = path;
// Create a temporary file in the same directory as the target file.
@ -97,7 +126,7 @@ Error FileAccessUnix::open_internal(const String &p_path, int p_mode_flags) {
last_error = ERR_FILE_CANT_OPEN;
return last_error;
}
fchmod(fd, 0666);
fchmod(fd, 0644);
path = String::utf8(cs.ptr());
f = fdopen(fd, mode_string);
@ -173,10 +202,29 @@ String FileAccessUnix::get_path_absolute() const {
return path;
}
#if defined(TOOLS_ENABLED)
String FileAccessUnix::get_real_path() const {
char *resolved_path = ::realpath(path.utf8().get_data(), nullptr);
if (!resolved_path) {
return path;
}
String result;
Error parse_ok = result.parse_utf8(resolved_path);
::free(resolved_path);
if (parse_ok != OK) {
return path;
}
return result.simplify_path();
}
#endif
void FileAccessUnix::seek(uint64_t p_position) {
ERR_FAIL_NULL_MSG(f, "File must be opened before use.");
last_error = OK;
if (fseeko(f, p_position, SEEK_SET)) {
check_errors();
}
@ -215,70 +263,16 @@ uint64_t FileAccessUnix::get_length() const {
}
bool FileAccessUnix::eof_reached() const {
return last_error == ERR_FILE_EOF;
}
uint8_t FileAccessUnix::get_8() const {
ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use.");
uint8_t b;
if (fread(&b, 1, 1, f) == 0) {
check_errors();
b = '\0';
}
return b;
}
uint16_t FileAccessUnix::get_16() const {
ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use.");
uint16_t b = 0;
if (fread(&b, 1, 2, f) != 2) {
check_errors();
}
if (big_endian) {
b = BSWAP16(b);
}
return b;
}
uint32_t FileAccessUnix::get_32() const {
ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use.");
uint32_t b = 0;
if (fread(&b, 1, 4, f) != 4) {
check_errors();
}
if (big_endian) {
b = BSWAP32(b);
}
return b;
}
uint64_t FileAccessUnix::get_64() const {
ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use.");
uint64_t b = 0;
if (fread(&b, 1, 8, f) != 8) {
check_errors();
}
if (big_endian) {
b = BSWAP64(b);
}
return b;
return feof(f);
}
uint64_t FileAccessUnix::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
ERR_FAIL_NULL_V_MSG(f, -1, "File must be opened before use.");
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
uint64_t read = fread(p_dst, 1, p_length, f);
check_errors();
return read;
}
@ -308,60 +302,25 @@ void FileAccessUnix::flush() {
fflush(f);
}
void FileAccessUnix::store_8(uint8_t p_dest) {
ERR_FAIL_NULL_MSG(f, "File must be opened before use.");
ERR_FAIL_COND(fwrite(&p_dest, 1, 1, f) != 1);
}
void FileAccessUnix::store_16(uint16_t p_dest) {
ERR_FAIL_NULL_MSG(f, "File must be opened before use.");
if (big_endian) {
p_dest = BSWAP16(p_dest);
}
ERR_FAIL_COND(fwrite(&p_dest, 1, 2, f) != 2);
}
void FileAccessUnix::store_32(uint32_t p_dest) {
ERR_FAIL_NULL_MSG(f, "File must be opened before use.");
if (big_endian) {
p_dest = BSWAP32(p_dest);
}
ERR_FAIL_COND(fwrite(&p_dest, 1, 4, f) != 4);
}
void FileAccessUnix::store_64(uint64_t p_dest) {
ERR_FAIL_NULL_MSG(f, "File must be opened before use.");
if (big_endian) {
p_dest = BSWAP64(p_dest);
}
ERR_FAIL_COND(fwrite(&p_dest, 1, 8, f) != 8);
}
void FileAccessUnix::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_NULL_MSG(f, "File must be opened before use.");
ERR_FAIL_COND(!p_src && p_length > 0);
ERR_FAIL_COND(fwrite(p_src, 1, p_length, f) != p_length);
bool FileAccessUnix::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_NULL_V_MSG(f, false, "File must be opened before use.");
ERR_FAIL_COND_V(!p_src && p_length > 0, false);
bool res = fwrite(p_src, 1, p_length, f) == p_length;
check_errors(true);
return res;
}
bool FileAccessUnix::file_exists(const String &p_path) {
int err;
struct stat st = {};
String filename = fix_path(p_path);
const CharString filename_utf8 = fix_path(p_path).utf8();
// Does the name exist at all?
err = stat(filename.utf8().get_data(), &st);
if (err) {
if (stat(filename_utf8.get_data(), &st)) {
return false;
}
// See if we have access to the file
if (access(filename.utf8().get_data(), F_OK)) {
if (access(filename_utf8.get_data(), F_OK)) {
return false;
}
@ -381,9 +340,18 @@ uint64_t FileAccessUnix::_get_modified_time(const String &p_file) {
int err = stat(file.utf8().get_data(), &status);
if (!err) {
return status.st_mtime;
uint64_t modified_time = status.st_mtime;
#ifdef ANDROID_ENABLED
// Workaround for GH-101007
//FIXME: After saving, all timestamps (st_mtime, st_ctime, st_atime) are set to the same value.
// After exporting or after some time, only 'modified_time' resets to a past timestamp.
uint64_t created_time = status.st_ctime;
if (modified_time < created_time) {
modified_time = created_time;
}
#endif
return modified_time;
} else {
print_verbose("Failed to get modified time for: " + p_file + "");
return 0;
}
}
@ -483,7 +451,7 @@ void FileAccessUnix::close() {
_close();
}
CloseNotificationFunc FileAccessUnix::close_notification_func = nullptr;
FileAccessUnix::CloseNotificationFunc FileAccessUnix::close_notification_func = nullptr;
FileAccessUnix::~FileAccessUnix() {
_close();

View file

@ -38,12 +38,10 @@
#if defined(UNIX_ENABLED)
typedef void (*CloseNotificationFunc)(const String &p_file, int p_flags);
class FileAccessUnix : public FileAccess {
FILE *f = nullptr;
int flags = 0;
void check_errors() const;
void check_errors(bool p_write = false) const;
mutable Error last_error = OK;
String save_path;
String path;
@ -51,7 +49,12 @@ class FileAccessUnix : public FileAccess {
void _close();
#if defined(TOOLS_ENABLED)
String get_real_path() const; // Returns the resolved real path for the current open file.
#endif
public:
typedef void (*CloseNotificationFunc)(const String &p_file, int p_flags);
static CloseNotificationFunc close_notification_func;
virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
@ -67,21 +70,13 @@ public:
virtual bool eof_reached() const override; ///< reading passed EOF
virtual uint8_t get_8() const override; ///< get a byte
virtual uint16_t get_16() const override;
virtual uint32_t get_32() const override;
virtual uint64_t get_64() const override;
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
virtual Error get_error() const override; ///< get last error
virtual Error resize(int64_t p_length) override;
virtual void flush() override;
virtual void store_8(uint8_t p_dest) override; ///< store a byte
virtual void store_16(uint16_t p_dest) override;
virtual void store_32(uint32_t p_dest) override;
virtual void store_64(uint64_t p_dest) override;
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
virtual bool file_exists(const String &p_path) override; ///< return true if a file exists

View file

@ -37,11 +37,12 @@
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
Error FileAccessUnixPipe::open_existing(int p_rfd, int p_wfd) {
Error FileAccessUnixPipe::open_existing(int p_rfd, int p_wfd, bool p_blocking) {
// Open pipe using handles created by pipe(fd) call in the OS.execute_with_pipe.
_close();
@ -51,6 +52,11 @@ Error FileAccessUnixPipe::open_existing(int p_rfd, int p_wfd) {
fd[0] = p_rfd;
fd[1] = p_wfd;
if (!p_blocking) {
fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL) | O_NONBLOCK);
fcntl(fd[1], F_SETFL, fcntl(fd[1], F_GETFL) | O_NONBLOCK);
}
last_error = OK;
return OK;
}
@ -62,10 +68,12 @@ Error FileAccessUnixPipe::open_internal(const String &p_path, int p_mode_flags)
ERR_FAIL_COND_V_MSG(fd[0] >= 0 || fd[1] >= 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
path = String("/tmp/") + p_path.replace("pipe://", "").replace("/", "_");
const CharString path_utf8 = path.utf8();
struct stat st = {};
int err = stat(path.utf8().get_data(), &st);
int err = stat(path_utf8.get_data(), &st);
if (err) {
if (mkfifo(path.utf8().get_data(), 0666) != 0) {
if (mkfifo(path_utf8.get_data(), 0600) != 0) {
last_error = ERR_FILE_CANT_OPEN;
return last_error;
}
@ -74,7 +82,7 @@ Error FileAccessUnixPipe::open_internal(const String &p_path, int p_mode_flags)
ERR_FAIL_COND_V_MSG(!S_ISFIFO(st.st_mode), ERR_ALREADY_IN_USE, "Pipe name is already used by file.");
}
int f = ::open(path.utf8().get_data(), O_RDWR | O_CLOEXEC);
int f = ::open(path_utf8.get_data(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (f < 0) {
switch (errno) {
case ENOENT: {
@ -125,25 +133,23 @@ String FileAccessUnixPipe::get_path_absolute() const {
return path_src;
}
uint8_t FileAccessUnixPipe::get_8() const {
uint64_t FileAccessUnixPipe::get_length() const {
ERR_FAIL_COND_V_MSG(fd[0] < 0, 0, "Pipe must be opened before use.");
uint8_t b;
if (::read(fd[0], &b, 1) == 0) {
last_error = ERR_FILE_CANT_READ;
b = '\0';
} else {
last_error = OK;
}
return b;
int buf_rem = 0;
ERR_FAIL_COND_V(ioctl(fd[0], FIONREAD, &buf_rem) != 0, 0);
return buf_rem;
}
uint64_t FileAccessUnixPipe::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
ERR_FAIL_COND_V_MSG(fd[0] < 0, -1, "Pipe must be opened before use.");
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
uint64_t read = ::read(fd[0], p_dst, p_length);
if (read == p_length) {
ssize_t read = ::read(fd[0], p_dst, p_length);
if (read == -1) {
last_error = ERR_FILE_CANT_READ;
read = 0;
} else if (read != (ssize_t)p_length) {
last_error = ERR_FILE_CANT_READ;
} else {
last_error = OK;
@ -155,22 +161,16 @@ Error FileAccessUnixPipe::get_error() const {
return last_error;
}
void FileAccessUnixPipe::store_8(uint8_t p_src) {
ERR_FAIL_COND_MSG(fd[1] < 0, "Pipe must be opened before use.");
if (::write(fd[1], &p_src, 1) != 1) {
last_error = ERR_FILE_CANT_WRITE;
} else {
last_error = OK;
}
}
bool FileAccessUnixPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_COND_V_MSG(fd[1] < 0, false, "Pipe must be opened before use.");
ERR_FAIL_COND_V(!p_src && p_length > 0, false);
void FileAccessUnixPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_COND_MSG(fd[1] < 0, "Pipe must be opened before use.");
ERR_FAIL_COND(!p_src && p_length > 0);
if (::write(fd[1], p_src, p_length) != (ssize_t)p_length) {
last_error = ERR_FILE_CANT_WRITE;
return false;
} else {
last_error = OK;
return true;
}
}

View file

@ -50,7 +50,7 @@ class FileAccessUnixPipe : public FileAccess {
void _close();
public:
Error open_existing(int p_rfd, int p_wfd);
Error open_existing(int p_rfd, int p_wfd, bool p_blocking);
virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
virtual bool is_open() const override; ///< true when file is open
@ -61,19 +61,17 @@ public:
virtual void seek(uint64_t p_position) override {}
virtual void seek_end(int64_t p_position = 0) override {}
virtual uint64_t get_position() const override { return 0; }
virtual uint64_t get_length() const override { return 0; }
virtual uint64_t get_length() const override;
virtual bool eof_reached() const override { return false; }
virtual uint8_t get_8() const override; ///< get a byte
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
virtual Error get_error() const override; ///< get last error
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
virtual void flush() override {}
virtual void store_8(uint8_t p_src) override; ///< store a byte
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
virtual bool file_exists(const String &p_path) override { return false; }

View file

@ -28,23 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#if defined(UNIX_ENABLED) && !defined(UNIX_SOCKET_UNAVAILABLE)
#include "ip_unix.h"
#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED)
#ifdef WINDOWS_ENABLED
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>
#else // UNIX
#include <netdb.h>
#ifdef ANDROID_ENABLED
@ -67,8 +54,6 @@
#include <net/if.h> // Order is important on OpenBSD, leave as last.
#endif // UNIX
#include <string.h>
static IPAddress _sockaddr2ip(struct sockaddr *p_addr) {
@ -108,7 +93,7 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos
}
if (result == nullptr || result->ai_addr == nullptr) {
print_verbose("Invalid response from getaddrinfo");
print_verbose("Invalid response from getaddrinfo.");
if (result) {
freeaddrinfo(result);
}
@ -132,56 +117,6 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos
freeaddrinfo(result);
}
#if defined(WINDOWS_ENABLED)
void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const {
ULONG buf_size = 1024;
IP_ADAPTER_ADDRESSES *addrs;
while (true) {
addrs = (IP_ADAPTER_ADDRESSES *)memalloc(buf_size);
int err = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME,
nullptr, addrs, &buf_size);
if (err == NO_ERROR) {
break;
}
memfree(addrs);
if (err == ERROR_BUFFER_OVERFLOW) {
continue; // will go back and alloc the right size
}
ERR_FAIL_MSG("Call to GetAdaptersAddresses failed with error " + itos(err) + ".");
}
IP_ADAPTER_ADDRESSES *adapter = addrs;
while (adapter != nullptr) {
Interface_Info info;
info.name = adapter->AdapterName;
info.name_friendly = adapter->FriendlyName;
info.index = String::num_uint64(adapter->IfIndex);
IP_ADAPTER_UNICAST_ADDRESS *address = adapter->FirstUnicastAddress;
while (address != nullptr) {
int family = address->Address.lpSockaddr->sa_family;
if (family != AF_INET && family != AF_INET6) {
continue;
}
info.ip_addresses.push_front(_sockaddr2ip(address->Address.lpSockaddr));
address = address->Next;
}
adapter = adapter->Next;
// Only add interface if it has at least one IP
if (info.ip_addresses.size() > 0) {
r_interfaces->insert(info.name, info);
}
}
memfree(addrs);
}
#else // UNIX
void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const {
struct ifaddrs *ifAddrStruct = nullptr;
struct ifaddrs *ifa = nullptr;
@ -219,8 +154,6 @@ void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces)
}
}
#endif // UNIX
void IPUnix::make_default() {
_create = _create_unix;
}
@ -232,4 +165,4 @@ IP *IPUnix::_create_unix() {
IPUnix::IPUnix() {
}
#endif // UNIX_ENABLED || WINDOWS_ENABLED
#endif // UNIX_ENABLED

View file

@ -31,9 +31,9 @@
#ifndef IP_UNIX_H
#define IP_UNIX_H
#include "core/io/ip.h"
#if defined(UNIX_ENABLED) && !defined(UNIX_SOCKET_UNAVAILABLE)
#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED)
#include "core/io/ip.h"
class IPUnix : public IP {
GDCLASS(IPUnix, IP);
@ -49,6 +49,6 @@ public:
IPUnix();
};
#endif
#endif // UNIX_ENABLED
#endif // IP_UNIX_H

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* net_socket_posix.cpp */
/* net_socket_unix.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,13 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "net_socket_posix.h"
// Some proprietary Unix-derived platforms don't expose Unix sockets
// so this allows skipping this file to reimplement this API differently.
#ifndef UNIX_SOCKET_UNAVAILABLE
#if defined(UNIX_ENABLED) && !defined(UNIX_SOCKET_UNAVAILABLE)
#if defined(UNIX_ENABLED)
#include "net_socket_unix.h"
#include <errno.h>
#include <fcntl.h>
@ -62,42 +60,11 @@
#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
#endif
// Some custom defines to minimize ifdefs
#define SOCK_EMPTY -1
#define SOCK_BUF(x) x
#define SOCK_CBUF(x) x
#define SOCK_IOCTL ioctl
#define SOCK_CLOSE ::close
#define SOCK_CONNECT(p_sock, p_addr, p_addr_len) ::connect(p_sock, p_addr, p_addr_len)
/* Windows */
#elif defined(WINDOWS_ENABLED)
#include <winsock2.h>
#include <ws2tcpip.h>
#include <mswsock.h>
// Some custom defines to minimize ifdefs
#define SOCK_EMPTY INVALID_SOCKET
#define SOCK_BUF(x) (char *)(x)
#define SOCK_CBUF(x) (const char *)(x)
#define SOCK_IOCTL ioctlsocket
#define SOCK_CLOSE closesocket
// connect is broken on windows under certain conditions, reasons unknown:
// See https://github.com/godotengine/webrtc-native/issues/6
#define SOCK_CONNECT(p_sock, p_addr, p_addr_len) ::WSAConnect(p_sock, p_addr, p_addr_len, nullptr, nullptr, nullptr, nullptr)
// Workaround missing flag in MinGW
#if defined(__MINGW32__) && !defined(SIO_UDP_NETRESET)
#define SIO_UDP_NETRESET _WSAIOW(IOC_VENDOR, 15)
#endif
#endif // UNIX_ENABLED
size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type) {
size_t NetSocketUnix::_set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type) {
memset(p_addr, 0, sizeof(struct sockaddr_storage));
if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket
if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket.
// IPv6 only socket with IPv4 address
// IPv6 only socket with IPv4 address.
ERR_FAIL_COND_V(!p_ip.is_wildcard() && p_ip_type == IP::TYPE_IPV6 && p_ip.is_ipv4(), 0);
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr;
@ -109,14 +76,14 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const
addr6->sin6_addr = in6addr_any;
}
return sizeof(sockaddr_in6);
} else { // IPv4 socket
} else { // IPv4 socket.
// IPv4 socket with IPv6 address
// IPv4 socket with IPv6 address.
ERR_FAIL_COND_V(!p_ip.is_wildcard() && !p_ip.is_ipv4(), 0);
struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr;
addr4->sin_family = AF_INET;
addr4->sin_port = htons(p_port); // short, network byte order
addr4->sin_port = htons(p_port); // Short, network byte order.
if (p_ip.is_valid()) {
memcpy(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4);
@ -128,7 +95,7 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const
}
}
void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port) {
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;
if (r_ip) {
@ -148,34 +115,21 @@ void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_
}
}
NetSocket *NetSocketPosix::_create_func() {
return memnew(NetSocketPosix);
NetSocket *NetSocketUnix::_create_func() {
return memnew(NetSocketUnix);
}
void NetSocketPosix::make_default() {
#if defined(WINDOWS_ENABLED)
if (_create == nullptr) {
WSADATA data;
WSAStartup(MAKEWORD(2, 2), &data);
}
#endif
void NetSocketUnix::make_default() {
_create = _create_func;
}
void NetSocketPosix::cleanup() {
#if defined(WINDOWS_ENABLED)
if (_create != nullptr) {
WSACleanup();
}
_create = nullptr;
#endif
void NetSocketUnix::cleanup() {
}
NetSocketPosix::NetSocketPosix() :
_sock(SOCK_EMPTY) {
NetSocketUnix::NetSocketUnix() {
}
NetSocketPosix::~NetSocketPosix() {
NetSocketUnix::~NetSocketUnix() {
close();
}
@ -186,30 +140,7 @@ NetSocketPosix::~NetSocketPosix() {
#pragma GCC diagnostic ignored "-Wlogical-op"
#endif
NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const {
#if defined(WINDOWS_ENABLED)
int err = WSAGetLastError();
if (err == WSAEISCONN) {
return ERR_NET_IS_CONNECTED;
}
if (err == WSAEINPROGRESS || err == WSAEALREADY) {
return ERR_NET_IN_PROGRESS;
}
if (err == WSAEWOULDBLOCK) {
return ERR_NET_WOULD_BLOCK;
}
if (err == WSAEADDRINUSE || err == WSAEADDRNOTAVAIL) {
return ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE;
}
if (err == WSAEACCES) {
return ERR_NET_UNAUTHORIZED;
}
if (err == WSAEMSGSIZE || err == WSAENOBUFS) {
return ERR_NET_BUFFER_TOO_SMALL;
}
print_verbose("Socket error: " + itos(err));
return ERR_NET_OTHER;
#else
NetSocketUnix::NetError NetSocketUnix::_get_socket_error() const {
if (errno == EISCONN) {
return ERR_NET_IS_CONNECTED;
}
@ -228,16 +159,15 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const {
if (errno == ENOBUFS) {
return ERR_NET_BUFFER_TOO_SMALL;
}
print_verbose("Socket error: " + itos(errno));
print_verbose("Socket error: " + itos(errno) + ".");
return ERR_NET_OTHER;
#endif
}
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
bool NetSocketPosix::_can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const {
bool NetSocketUnix::_can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const {
if (p_for_bind && !(p_ip.is_valid() || p_ip.is_wildcard())) {
return false;
} else if (!p_for_bind && !p_ip.is_valid()) {
@ -248,11 +178,11 @@ bool NetSocketPosix::_can_use_ip(const IPAddress &p_ip, const bool p_for_bind) c
return !(_ip_type != IP::TYPE_ANY && !p_ip.is_wildcard() && _ip_type != type);
}
_FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add) {
_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(!_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
// Need to force level and af_family to IP(v4) when using dual stacking and provided multicast group is IPv4.
IP::Type type = _ip_type == IP::TYPE_ANY && p_ip.is_ipv4() ? IP::TYPE_IPV4 : _ip_type;
// This needs to be the proper level for the multicast group, no matter if the socket is dual stacking.
int level = type == IP::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6;
@ -275,7 +205,7 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, Str
for (const IPAddress &F : c.ip_addresses) {
if (!F.is_ipv4()) {
continue; // Wrong IP type
continue; // Wrong IP type.
}
if_ip = F;
break;
@ -302,7 +232,7 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, Str
return OK;
}
void NetSocketPosix::_set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_is_stream) {
void NetSocketUnix::_set_socket(int p_sock, IP::Type p_ip_type, bool p_is_stream) {
_sock = p_sock;
_ip_type = p_ip_type;
_is_stream = p_is_stream;
@ -310,15 +240,13 @@ void NetSocketPosix::_set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_
_set_close_exec_enabled(true);
}
void NetSocketPosix::_set_close_exec_enabled(bool p_enabled) {
#ifndef WINDOWS_ENABLED
void NetSocketUnix::_set_close_exec_enabled(bool p_enabled) {
// Enable close on exec to avoid sharing with subprocesses. Off by default on Windows.
int opts = fcntl(_sock, F_GETFD);
fcntl(_sock, F_SETFD, opts | FD_CLOEXEC);
#endif
}
Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) {
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);
@ -334,7 +262,7 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) {
int type = p_sock_type == TYPE_TCP ? SOCK_STREAM : SOCK_DGRAM;
_sock = socket(family, type, protocol);
if (_sock == SOCK_EMPTY && ip_type == IP::TYPE_ANY) {
if (_sock == -1 && 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;
@ -342,11 +270,11 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) {
_sock = socket(family, type, protocol);
}
ERR_FAIL_COND_V(_sock == SOCK_EMPTY, FAILED);
ERR_FAIL_COND_V(_sock == -1, FAILED);
_ip_type = ip_type;
if (family == AF_INET6) {
// Select IPv4 over IPv6 mapping
// Select IPv4 over IPv6 mapping.
set_ipv6_only_enabled(ip_type != IP::TYPE_ANY);
}
@ -361,41 +289,27 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) {
// Disable descriptor sharing with subprocesses.
_set_close_exec_enabled(true);
#if defined(WINDOWS_ENABLED)
if (!_is_stream) {
// Disable windows feature/bug reporting WSAECONNRESET/WSAENETRESET when
// recv/recvfrom and an ICMP reply was received from a previous send/sendto.
unsigned long disable = 0;
if (ioctlsocket(_sock, SIO_UDP_CONNRESET, &disable) == SOCKET_ERROR) {
print_verbose("Unable to turn off UDP WSAECONNRESET behavior on Windows");
}
if (ioctlsocket(_sock, SIO_UDP_NETRESET, &disable) == SOCKET_ERROR) {
// This feature seems not to be supported on wine.
print_verbose("Unable to turn off UDP WSAENETRESET behavior on Windows");
}
}
#endif
#if defined(SO_NOSIGPIPE)
// Disable SIGPIPE (should only be relevant to stream sockets, but seems to affect UDP too on iOS)
// 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, SOCK_CBUF(&par), sizeof(int)) != 0) {
print_verbose("Unable to turn off SIGPIPE on socket");
if (setsockopt(_sock, SOL_SOCKET, SO_NOSIGPIPE, &par, sizeof(int)) != 0) {
print_verbose("Unable to turn off SIGPIPE on socket.");
}
#endif
return OK;
}
void NetSocketPosix::close() {
if (_sock != SOCK_EMPTY) {
SOCK_CLOSE(_sock);
void NetSocketUnix::close() {
if (_sock != -1) {
::close(_sock);
}
_sock = SOCK_EMPTY;
_sock = -1;
_ip_type = IP::TYPE_NONE;
_is_stream = false;
}
Error NetSocketPosix::bind(IPAddress p_addr, uint16_t p_port) {
Error NetSocketUnix::bind(IPAddress p_addr, uint16_t p_port) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
ERR_FAIL_COND_V(!_can_use_ip(p_addr, true), ERR_INVALID_PARAMETER);
@ -404,7 +318,7 @@ Error NetSocketPosix::bind(IPAddress p_addr, uint16_t p_port) {
if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
NetError err = _get_socket_error();
print_verbose("Failed to bind socket. Error: " + itos(err));
print_verbose("Failed to bind socket. Error: " + itos(err) + ".");
close();
return ERR_UNAVAILABLE;
}
@ -412,7 +326,7 @@ Error NetSocketPosix::bind(IPAddress p_addr, uint16_t p_port) {
return OK;
}
Error NetSocketPosix::listen(int p_max_pending) {
Error NetSocketUnix::listen(int p_max_pending) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
if (::listen(_sock, p_max_pending) != 0) {
@ -425,26 +339,26 @@ Error NetSocketPosix::listen(int p_max_pending) {
return OK;
}
Error NetSocketPosix::connect_to_host(IPAddress p_host, uint16_t p_port) {
Error NetSocketUnix::connect_to_host(IPAddress p_host, uint16_t p_port) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
ERR_FAIL_COND_V(!_can_use_ip(p_host, false), ERR_INVALID_PARAMETER);
struct sockaddr_storage addr;
size_t addr_size = _set_addr_storage(&addr, p_host, p_port, _ip_type);
if (SOCK_CONNECT(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
if (::connect(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
NetError err = _get_socket_error();
switch (err) {
// We are already connected
// We are already connected.
case ERR_NET_IS_CONNECTED:
return OK;
// Still waiting to connect, try again in a while
// Still waiting to connect, try again in a while.
case ERR_NET_WOULD_BLOCK:
case ERR_NET_IN_PROGRESS:
return ERR_BUSY;
default:
print_verbose("Connection to remote host failed!");
print_verbose("Connection to remote host failed.");
close();
return FAILED;
}
@ -453,66 +367,9 @@ Error NetSocketPosix::connect_to_host(IPAddress p_host, uint16_t p_port) {
return OK;
}
Error NetSocketPosix::poll(PollType p_type, int p_timeout) const {
Error NetSocketUnix::poll(PollType p_type, int p_timeout) const {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
#if defined(WINDOWS_ENABLED)
bool ready = false;
fd_set rd, wr, ex;
fd_set *rdp = nullptr;
fd_set *wrp = nullptr;
FD_ZERO(&rd);
FD_ZERO(&wr);
FD_ZERO(&ex);
FD_SET(_sock, &ex);
struct timeval timeout = { p_timeout / 1000, (p_timeout % 1000) * 1000 };
// For blocking operation, pass nullptr timeout pointer to select.
struct timeval *tp = nullptr;
if (p_timeout >= 0) {
// If timeout is non-negative, we want to specify the timeout instead.
tp = &timeout;
}
switch (p_type) {
case POLL_TYPE_IN:
FD_SET(_sock, &rd);
rdp = &rd;
break;
case POLL_TYPE_OUT:
FD_SET(_sock, &wr);
wrp = &wr;
break;
case POLL_TYPE_IN_OUT:
FD_SET(_sock, &rd);
FD_SET(_sock, &wr);
rdp = &rd;
wrp = &wr;
}
int ret = select(1, rdp, wrp, &ex, tp);
if (ret == SOCKET_ERROR) {
return FAILED;
}
if (ret == 0) {
return ERR_BUSY;
}
if (FD_ISSET(_sock, &ex)) {
_get_socket_error();
print_verbose("Exception when polling socket.");
return FAILED;
}
if (rdp && FD_ISSET(_sock, rdp)) {
ready = true;
}
if (wrp && FD_ISSET(_sock, wrp)) {
ready = true;
}
return ready ? OK : ERR_BUSY;
#else
struct pollfd pfd;
pfd.fd = _sock;
pfd.events = POLLIN;
@ -542,13 +399,12 @@ Error NetSocketPosix::poll(PollType p_type, int p_timeout) const {
}
return OK;
#endif
}
Error NetSocketPosix::recv(uint8_t *p_buffer, int p_len, int &r_read) {
Error NetSocketUnix::recv(uint8_t *p_buffer, int p_len, int &r_read) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
r_read = ::recv(_sock, SOCK_BUF(p_buffer), p_len, 0);
r_read = ::recv(_sock, p_buffer, p_len, 0);
if (r_read < 0) {
NetError err = _get_socket_error();
@ -566,14 +422,14 @@ Error NetSocketPosix::recv(uint8_t *p_buffer, int p_len, int &r_read) {
return OK;
}
Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek) {
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);
struct sockaddr_storage from;
socklen_t len = sizeof(struct sockaddr_storage);
memset(&from, 0, len);
r_read = ::recvfrom(_sock, SOCK_BUF(p_buffer), p_len, p_peek ? MSG_PEEK : 0, (struct sockaddr *)&from, &len);
r_read = ::recvfrom(_sock, p_buffer, p_len, p_peek ? MSG_PEEK : 0, (struct sockaddr *)&from, &len);
if (r_read < 0) {
NetError err = _get_socket_error();
@ -604,7 +460,7 @@ Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddr
return OK;
}
Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
Error NetSocketUnix::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
int flags = 0;
@ -613,7 +469,7 @@ Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
flags = MSG_NOSIGNAL;
}
#endif
r_sent = ::send(_sock, SOCK_CBUF(p_buffer), p_len, flags);
r_sent = ::send(_sock, p_buffer, p_len, flags);
if (r_sent < 0) {
NetError err = _get_socket_error();
@ -630,12 +486,12 @@ Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
return OK;
}
Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) {
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);
struct sockaddr_storage addr;
size_t addr_size = _set_addr_storage(&addr, p_ip, p_port, _ip_type);
r_sent = ::sendto(_sock, SOCK_CBUF(p_buffer), p_len, 0, (struct sockaddr *)&addr, addr_size);
r_sent = ::sendto(_sock, p_buffer, p_len, 0, (struct sockaddr *)&addr, addr_size);
if (r_sent < 0) {
NetError err = _get_socket_error();
@ -652,7 +508,7 @@ Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP
return OK;
}
Error NetSocketPosix::set_broadcasting_enabled(bool p_enabled) {
Error NetSocketUnix::set_broadcasting_enabled(bool p_enabled) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
// IPv6 has no broadcast support.
if (_ip_type == IP::TYPE_IPV6) {
@ -660,90 +516,68 @@ Error NetSocketPosix::set_broadcasting_enabled(bool p_enabled) {
}
int par = p_enabled ? 1 : 0;
if (setsockopt(_sock, SOL_SOCKET, SO_BROADCAST, SOCK_CBUF(&par), sizeof(int)) != 0) {
WARN_PRINT("Unable to change broadcast setting");
if (setsockopt(_sock, SOL_SOCKET, SO_BROADCAST, &par, sizeof(int)) != 0) {
WARN_PRINT("Unable to change broadcast setting.");
return FAILED;
}
return OK;
}
void NetSocketPosix::set_blocking_enabled(bool p_enabled) {
void NetSocketUnix::set_blocking_enabled(bool p_enabled) {
ERR_FAIL_COND(!is_open());
int ret = 0;
#if defined(WINDOWS_ENABLED)
unsigned long par = p_enabled ? 0 : 1;
ret = SOCK_IOCTL(_sock, FIONBIO, &par);
#else
int opts = fcntl(_sock, F_GETFL);
if (p_enabled) {
ret = fcntl(_sock, F_SETFL, opts & ~O_NONBLOCK);
} else {
ret = fcntl(_sock, F_SETFL, opts | O_NONBLOCK);
}
#endif
if (ret != 0) {
WARN_PRINT("Unable to change non-block mode");
WARN_PRINT("Unable to change non-block mode.");
}
}
void NetSocketPosix::set_ipv6_only_enabled(bool p_enabled) {
void NetSocketUnix::set_ipv6_only_enabled(bool p_enabled) {
ERR_FAIL_COND(!is_open());
// This option is only available in IPv6 sockets.
ERR_FAIL_COND(_ip_type == IP::TYPE_IPV4);
int par = p_enabled ? 1 : 0;
if (setsockopt(_sock, IPPROTO_IPV6, IPV6_V6ONLY, SOCK_CBUF(&par), sizeof(int)) != 0) {
WARN_PRINT("Unable to change IPv4 address mapping over IPv6 option");
if (setsockopt(_sock, IPPROTO_IPV6, IPV6_V6ONLY, &par, sizeof(int)) != 0) {
WARN_PRINT("Unable to change IPv4 address mapping over IPv6 option.");
}
}
void NetSocketPosix::set_tcp_no_delay_enabled(bool p_enabled) {
void NetSocketUnix::set_tcp_no_delay_enabled(bool p_enabled) {
ERR_FAIL_COND(!is_open());
ERR_FAIL_COND(!_is_stream); // Not TCP
ERR_FAIL_COND(!_is_stream); // Not TCP.
int par = p_enabled ? 1 : 0;
if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, SOCK_CBUF(&par), sizeof(int)) < 0) {
ERR_PRINT("Unable to set TCP no delay option");
if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, &par, sizeof(int)) < 0) {
WARN_PRINT("Unable to set TCP no delay option.");
}
}
void NetSocketPosix::set_reuse_address_enabled(bool p_enabled) {
void NetSocketUnix::set_reuse_address_enabled(bool p_enabled) {
ERR_FAIL_COND(!is_open());
// On Windows, enabling SO_REUSEADDR actually would also enable reuse port, very bad on TCP. Denying...
// Windows does not have this option, SO_REUSEADDR in this magical world means SO_REUSEPORT
#ifndef WINDOWS_ENABLED
int par = p_enabled ? 1 : 0;
if (setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, SOCK_CBUF(&par), sizeof(int)) < 0) {
WARN_PRINT("Unable to set socket REUSEADDR option!");
}
#endif
}
void NetSocketPosix::set_reuse_port_enabled(bool p_enabled) {
ERR_FAIL_COND(!is_open());
// See comment above...
#ifdef WINDOWS_ENABLED
#define SO_REUSEPORT SO_REUSEADDR
#endif
int par = p_enabled ? 1 : 0;
if (setsockopt(_sock, SOL_SOCKET, SO_REUSEPORT, SOCK_CBUF(&par), sizeof(int)) < 0) {
WARN_PRINT("Unable to set socket REUSEPORT option!");
if (setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, &par, sizeof(int)) < 0) {
WARN_PRINT("Unable to set socket REUSEADDR option.");
}
}
bool NetSocketPosix::is_open() const {
return _sock != SOCK_EMPTY;
bool NetSocketUnix::is_open() const {
return _sock != -1;
}
int NetSocketPosix::get_available_bytes() const {
int NetSocketUnix::get_available_bytes() const {
ERR_FAIL_COND_V(!is_open(), -1);
unsigned long len;
int ret = SOCK_IOCTL(_sock, FIONREAD, &len);
int len;
int ret = ioctl(_sock, FIONREAD, &len);
if (ret == -1) {
_get_socket_error();
print_verbose("Error when checking available bytes on socket.");
@ -752,7 +586,7 @@ int NetSocketPosix::get_available_bytes() const {
return len;
}
Error NetSocketPosix::get_socket_address(IPAddress *r_ip, uint16_t *r_port) const {
Error NetSocketUnix::get_socket_address(IPAddress *r_ip, uint16_t *r_port) const {
ERR_FAIL_COND_V(!is_open(), FAILED);
struct sockaddr_storage saddr;
@ -766,14 +600,14 @@ Error NetSocketPosix::get_socket_address(IPAddress *r_ip, uint16_t *r_port) cons
return OK;
}
Ref<NetSocket> NetSocketPosix::accept(IPAddress &r_ip, uint16_t &r_port) {
Ref<NetSocket> NetSocketUnix::accept(IPAddress &r_ip, uint16_t &r_port) {
Ref<NetSocket> out;
ERR_FAIL_COND_V(!is_open(), out);
struct sockaddr_storage their_addr;
socklen_t size = sizeof(their_addr);
SOCKET_TYPE fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size);
if (fd == SOCK_EMPTY) {
int fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size);
if (fd == -1) {
_get_socket_error();
print_verbose("Error when accepting socket connection.");
return out;
@ -781,18 +615,18 @@ Ref<NetSocket> NetSocketPosix::accept(IPAddress &r_ip, uint16_t &r_port) {
_set_ip_port(&their_addr, &r_ip, &r_port);
NetSocketPosix *ns = memnew(NetSocketPosix);
NetSocketUnix *ns = memnew(NetSocketUnix);
ns->_set_socket(fd, _ip_type, _is_stream);
ns->set_blocking_enabled(false);
return Ref<NetSocket>(ns);
}
Error NetSocketPosix::join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) {
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);
}
Error NetSocketPosix::leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) {
Error NetSocketUnix::leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) {
return _change_multicast_group(p_multi_address, p_if_name, false);
}
#endif // UNIX_SOCKET_UNAVAILABLE
#endif // UNIX_ENABLED && !UNIX_SOCKET_UNAVAILABLE

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* net_socket_posix.h */
/* net_socket_unix.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,25 +28,18 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef NET_SOCKET_POSIX_H
#define NET_SOCKET_POSIX_H
#ifndef NET_SOCKET_UNIX_H
#define NET_SOCKET_UNIX_H
#if defined(UNIX_ENABLED) && !defined(UNIX_SOCKET_UNAVAILABLE)
#include "core/io/net_socket.h"
#if defined(WINDOWS_ENABLED)
#include <winsock2.h>
#include <ws2tcpip.h>
#define SOCKET_TYPE SOCKET
#else
#include <sys/socket.h>
#define SOCKET_TYPE int
#endif
class NetSocketPosix : public NetSocket {
class NetSocketUnix : public NetSocket {
private:
SOCKET_TYPE _sock; // NOLINT - the default value is defined in the .cpp
int _sock = -1;
IP::Type _ip_type = IP::TYPE_NONE;
bool _is_stream = false;
@ -61,7 +54,7 @@ private:
};
NetError _get_socket_error() const;
void _set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_is_stream);
void _set_socket(int p_sock, IP::Type p_ip_type, bool p_is_stream);
_FORCE_INLINE_ Error _change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add);
_FORCE_INLINE_ void _set_close_exec_enabled(bool p_enabled);
@ -76,33 +69,34 @@ 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);
virtual void close();
virtual Error bind(IPAddress p_addr, uint16_t p_port);
virtual Error listen(int p_max_pending);
virtual Error connect_to_host(IPAddress p_host, uint16_t p_port);
virtual Error poll(PollType p_type, int timeout) const;
virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read);
virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false);
virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent);
virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port);
virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port);
virtual Error open(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 listen(int p_max_pending) override;
virtual Error connect_to_host(IPAddress p_host, uint16_t p_port) 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 bool is_open() const;
virtual int get_available_bytes() const;
virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const;
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 set_broadcasting_enabled(bool p_enabled);
virtual void set_blocking_enabled(bool p_enabled);
virtual void set_ipv6_only_enabled(bool p_enabled);
virtual void set_tcp_no_delay_enabled(bool p_enabled);
virtual void set_reuse_address_enabled(bool p_enabled);
virtual void set_reuse_port_enabled(bool p_enabled);
virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name);
virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name);
virtual Error set_broadcasting_enabled(bool p_enabled) override;
virtual void set_blocking_enabled(bool p_enabled) override;
virtual void set_ipv6_only_enabled(bool p_enabled) override;
virtual void set_tcp_no_delay_enabled(bool p_enabled) override;
virtual void set_reuse_address_enabled(bool p_enabled) override;
virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override;
virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override;
NetSocketPosix();
~NetSocketPosix();
NetSocketUnix();
~NetSocketUnix() override;
};
#endif // NET_SOCKET_POSIX_H
#endif // UNIX_ENABLED && !UNIX_SOCKET_UNAVAILABLE
#endif // NET_SOCKET_UNIX_H

View file

@ -38,7 +38,7 @@
#include "drivers/unix/dir_access_unix.h"
#include "drivers/unix/file_access_unix.h"
#include "drivers/unix/file_access_unix_pipe.h"
#include "drivers/unix/net_socket_posix.h"
#include "drivers/unix/net_socket_unix.h"
#include "drivers/unix/thread_posix.h"
#include "servers/rendering_server.h"
@ -77,6 +77,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <time.h>
@ -166,8 +167,10 @@ void OS_Unix::initialize_core() {
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM);
NetSocketPosix::make_default();
#ifndef UNIX_SOCKET_UNAVAILABLE
NetSocketUnix::make_default();
IPUnix::make_default();
#endif
process_map = memnew((HashMap<ProcessID, ProcessInfo>));
_setup_clock();
@ -175,16 +178,96 @@ void OS_Unix::initialize_core() {
void OS_Unix::finalize_core() {
memdelete(process_map);
NetSocketPosix::cleanup();
#ifndef UNIX_SOCKET_UNAVAILABLE
NetSocketUnix::cleanup();
#endif
}
Vector<String> OS_Unix::get_video_adapter_driver_info() const {
return Vector<String>();
}
String OS_Unix::get_stdin_string() {
char buff[1024];
return String::utf8(fgets(buff, 1024, stdin));
String OS_Unix::get_stdin_string(int64_t p_buffer_size) {
Vector<uint8_t> data;
data.resize(p_buffer_size);
if (fgets((char *)data.ptrw(), data.size(), stdin)) {
return String::utf8((char *)data.ptr()).replace("\r\n", "\n").rstrip("\n");
}
return String();
}
PackedByteArray OS_Unix::get_stdin_buffer(int64_t p_buffer_size) {
Vector<uint8_t> data;
data.resize(p_buffer_size);
size_t sz = fread((void *)data.ptrw(), 1, data.size(), stdin);
if (sz > 0) {
data.resize(sz);
return data;
}
return PackedByteArray();
}
OS_Unix::StdHandleType OS_Unix::get_stdin_type() const {
int h = fileno(stdin);
if (h == -1) {
return STD_HANDLE_INVALID;
}
if (isatty(h)) {
return STD_HANDLE_CONSOLE;
}
struct stat statbuf;
if (fstat(h, &statbuf) < 0) {
return STD_HANDLE_UNKNOWN;
}
if (S_ISFIFO(statbuf.st_mode)) {
return STD_HANDLE_PIPE;
} else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
return STD_HANDLE_FILE;
}
return STD_HANDLE_UNKNOWN;
}
OS_Unix::StdHandleType OS_Unix::get_stdout_type() const {
int h = fileno(stdout);
if (h == -1) {
return STD_HANDLE_INVALID;
}
if (isatty(h)) {
return STD_HANDLE_CONSOLE;
}
struct stat statbuf;
if (fstat(h, &statbuf) < 0) {
return STD_HANDLE_UNKNOWN;
}
if (S_ISFIFO(statbuf.st_mode)) {
return STD_HANDLE_PIPE;
} else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
return STD_HANDLE_FILE;
}
return STD_HANDLE_UNKNOWN;
}
OS_Unix::StdHandleType OS_Unix::get_stderr_type() const {
int h = fileno(stderr);
if (h == -1) {
return STD_HANDLE_INVALID;
}
if (isatty(h)) {
return STD_HANDLE_CONSOLE;
}
struct stat statbuf;
if (fstat(h, &statbuf) < 0) {
return STD_HANDLE_UNKNOWN;
}
if (S_ISFIFO(statbuf.st_mode)) {
return STD_HANDLE_PIPE;
} else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
return STD_HANDLE_FILE;
}
return STD_HANDLE_UNKNOWN;
}
Error OS_Unix::get_entropy(uint8_t *r_buffer, int p_bytes) {
@ -225,6 +308,10 @@ String OS_Unix::get_version() const {
return "";
}
String OS_Unix::get_temp_path() const {
return "/tmp";
}
double OS_Unix::get_unix_time() const {
struct timeval tv_now;
gettimeofday(&tv_now, nullptr);
@ -493,7 +580,7 @@ Dictionary OS_Unix::get_memory_info() const {
return meminfo;
}
Dictionary OS_Unix::execute_with_pipe(const String &p_path, const List<String> &p_arguments) {
Dictionary OS_Unix::execute_with_pipe(const String &p_path, const List<String> &p_arguments, bool p_blocking) {
#define CLEAN_PIPES \
if (pipe_in[0] >= 0) { \
::close(pipe_in[0]); \
@ -578,11 +665,11 @@ Dictionary OS_Unix::execute_with_pipe(const String &p_path, const List<String> &
Ref<FileAccessUnixPipe> main_pipe;
main_pipe.instantiate();
main_pipe->open_existing(pipe_out[0], pipe_in[1]);
main_pipe->open_existing(pipe_out[0], pipe_in[1], p_blocking);
Ref<FileAccessUnixPipe> err_pipe;
err_pipe.instantiate();
err_pipe->open_existing(pipe_err[0], 0);
err_pipe->open_existing(pipe_err[0], 0, p_blocking);
ProcessInfo pi;
process_map_mutex.lock();
@ -777,7 +864,7 @@ String OS_Unix::get_locale() const {
}
String locale = get_environment("LANG");
int tp = locale.find(".");
int tp = locale.find_char('.');
if (tp != -1) {
locale = locale.substr(0, tp);
}
@ -862,32 +949,18 @@ String OS_Unix::get_environment(const String &p_var) const {
}
void OS_Unix::set_environment(const String &p_var, const String &p_value) const {
ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains_char('='), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
int err = setenv(p_var.utf8().get_data(), p_value.utf8().get_data(), /* overwrite: */ 1);
ERR_FAIL_COND_MSG(err != 0, vformat("Failed setting environment variable '%s', the system is out of memory.", p_var));
}
void OS_Unix::unset_environment(const String &p_var) const {
ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains_char('='), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
unsetenv(p_var.utf8().get_data());
}
String OS_Unix::get_user_data_dir() const {
String appname = get_safe_dir_name(GLOBAL_GET("application/config/name"));
if (!appname.is_empty()) {
bool use_custom_dir = GLOBAL_GET("application/config/use_custom_user_dir");
if (use_custom_dir) {
String custom_dir = get_safe_dir_name(GLOBAL_GET("application/config/custom_user_dir_name"), true);
if (custom_dir.is_empty()) {
custom_dir = appname;
}
return get_data_path().path_join(custom_dir);
} else {
return get_data_path().path_join(get_godot_dir_name()).path_join("app_userdata").path_join(appname);
}
}
return get_data_path().path_join(get_godot_dir_name()).path_join("app_userdata").path_join("[unnamed project]");
String OS_Unix::get_user_data_dir(const String &p_user_dir) const {
return get_data_path().path_join(p_user_dir);
}
String OS_Unix::get_executable_path() const {

View file

@ -58,7 +58,11 @@ public:
virtual Vector<String> get_video_adapter_driver_info() const override;
virtual String get_stdin_string() override;
virtual String get_stdin_string(int64_t p_buffer_size = 1024) override;
virtual PackedByteArray get_stdin_buffer(int64_t p_buffer_size = 1024) override;
virtual StdHandleType get_stdin_type() const override;
virtual StdHandleType get_stdout_type() const override;
virtual StdHandleType get_stderr_type() const override;
virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) override;
@ -72,6 +76,8 @@ public:
virtual String get_distribution_name() const override;
virtual String get_version() const override;
virtual String get_temp_path() const override;
virtual DateTime get_datetime(bool p_utc) const override;
virtual TimeZoneInfo get_time_zone_info() const override;
@ -83,7 +89,7 @@ public:
virtual Dictionary get_memory_info() const override;
virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
virtual Dictionary execute_with_pipe(const String &p_path, const List<String> &p_arguments) override;
virtual Dictionary execute_with_pipe(const String &p_path, const List<String> &p_arguments, bool p_blocking = true) override;
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
virtual Error kill(const ProcessID &p_pid) override;
virtual int get_process_id() const override;
@ -100,7 +106,7 @@ public:
virtual void initialize_debugging() override;
virtual String get_executable_path() const override;
virtual String get_user_data_dir() const override;
virtual String get_user_data_dir(const String &p_user_dir) const override;
};
class UnixTerminalLogger : public StdLogger {