(Cthulhu88) New Netplay MITM Part 2

This commit is contained in:
twinaphex 2021-11-12 06:30:01 +01:00
parent 109ab785ee
commit 34c374737c
5 changed files with 122 additions and 178 deletions

View File

@ -40,7 +40,6 @@
#define NETPLAY_HOST_LONGSTR_LEN 256 #define NETPLAY_HOST_LONGSTR_LEN 256
#define NETPLAY_MITM_MAX_PENDING 8 #define NETPLAY_MITM_MAX_PENDING 8
#define NETPLAY_MITM_ID_SIZE 16
enum rarch_netplay_ctl_state enum rarch_netplay_ctl_state
{ {
@ -188,14 +187,22 @@ struct netplay_host_list
size_t size; size_t size;
}; };
#pragma pack(push, 1)
typedef struct mitm_id
{
uint32_t magic;
uint8_t unique[12];
} mitm_id_t;
#pragma pack(pop)
struct netplay_mitm_pending struct netplay_mitm_pending
{ {
int *fds; int *fds;
uint8_t *ids[NETPLAY_MITM_MAX_PENDING]; mitm_id_t *ids;
retro_time_t *timeouts; retro_time_t *timeouts;
int current; mitm_id_t id_buf;
int next; size_t id_recvd;
}; };
typedef struct typedef struct
@ -223,7 +230,7 @@ typedef struct
bool in_netplay; bool in_netplay;
bool netplay_client_deferred; bool netplay_client_deferred;
bool is_mitm; bool is_mitm;
uint8_t mitm_id[NETPLAY_MITM_ID_SIZE]; mitm_id_t mitm_session_id;
struct netplay_mitm_pending mitm_pending; struct netplay_mitm_pending mitm_pending;
bool has_set_netplay_mode; bool has_set_netplay_mode;
bool has_set_netplay_ip_address; bool has_set_netplay_ip_address;
@ -343,7 +350,6 @@ void deinit_netplay(void);
/** /**
* init_netplay * init_netplay
* @direct_host : Host to connect to directly, if applicable (client only)
* @server : server address to connect to (client only) * @server : server address to connect to (client only)
* @port : TCP port to host on/connect to * @port : TCP port to host on/connect to
* *
@ -353,7 +359,7 @@ void deinit_netplay(void);
* *
* Returns: true (1) if successful, otherwise false (0). * Returns: true (1) if successful, otherwise false (0).
**/ **/
bool init_netplay(void *direct_host, const char *server, unsigned port); bool init_netplay(const char *server, unsigned port);
bool init_netplay_deferred(const char* server, unsigned port); bool init_netplay_deferred(const char* server, unsigned port);

View File

@ -91,6 +91,10 @@
#define FULL_MAGIC 0x46554C4C /* FULL */ #define FULL_MAGIC 0x46554C4C /* FULL */
#define POKE_MAGIC 0x504F4B45 /* POKE */ #define POKE_MAGIC 0x504F4B45 /* POKE */
/* MITM magics */
#define MITM_SESSION_MAGIC 0x52415453 /* RATS */
#define MITM_CONNECT_MAGIC 0x52415443 /* RATC */
#define MAX_CHAT_SIZE 256 #define MAX_CHAT_SIZE 256
#define CHAT_FRAME_TIME 600 #define CHAT_FRAME_TIME 600
@ -2172,7 +2176,7 @@ bool netplay_send(
/* Can only be that this is simply too big /* Can only be that this is simply too big
* for our buffer, in which case we just * for our buffer, in which case we just
* need to do a blocking send */ * need to do a blocking send */
if (!socket_send_all_blocking(sockfd, buf, len, false)) if (!socket_send_all_blocking(sockfd, buf, len, true))
return false; return false;
return true; return true;
} }
@ -2430,7 +2434,7 @@ static bool netplay_full(netplay_t *netplay, int sockfd)
This is fine; the header is just a warning This is fine; the header is just a warning
for the client. */ for the client. */
socket_send_all_nonblocking(sockfd, header, socket_send_all_nonblocking(sockfd, header,
sizeof(header), false); sizeof(header), true);
return true; return true;
} }
@ -5801,49 +5805,55 @@ static int init_tcp_connection(const struct addrinfo *res,
host, port); host, port);
dmsg = msg; dmsg = msg;
} }
#else else
dmsg = "Failed to connect to host.";
#endif #endif
dmsg = "Failed to connect to host.";
} }
else else
{ {
#ifdef __NETPLAY_MITM_NEW
if (use_mitm) if (use_mitm)
{ {
if (!socket_connect(fd, (void*)res, false)) if (!socket_connect(fd, (void*)res, false))
{ {
mitm_id_t new_session = {0};
mitm_id_t invalid_session = {0};
net_driver_state_t *net_st = &networking_driver_st; net_driver_state_t *net_st = &networking_driver_st;
/* To request a new session,
we send the magic with the rest of the ID zeroed. */
new_session.magic = htonl(MITM_SESSION_MAGIC);
/* Relay server should provide us with our session ID. */ /* Relay server should provide us with our session ID. */
if (socket_receive_all_blocking(fd, net_st->mitm_id, sizeof(net_st->mitm_id))) if (socket_send_all_blocking(fd,
&new_session, sizeof(new_session), true) &&
socket_receive_all_blocking(fd,
&net_st->mitm_session_id, sizeof(net_st->mitm_session_id)) &&
ntohl(net_st->mitm_session_id.magic) == MITM_SESSION_MAGIC &&
memcmp(&net_st->mitm_session_id.unique, &invalid_session.unique,
sizeof(net_st->mitm_session_id.unique)))
{ {
/* Initialize data for handling tunneled client connections. */ /* Initialize data for handling tunneled client connections. */
int *fds; int *fds;
uint8_t *ids; mitm_id_t *ids;
retro_time_t *timeouts; retro_time_t *timeouts;
fds = malloc( fds = malloc(
NETPLAY_MITM_MAX_PENDING * sizeof(*fds)); NETPLAY_MITM_MAX_PENDING * sizeof(*fds));
ids = malloc( ids = malloc(
NETPLAY_MITM_MAX_PENDING * NETPLAY_MITM_ID_SIZE * sizeof(*ids)); NETPLAY_MITM_MAX_PENDING * sizeof(*ids));
timeouts = malloc( timeouts = malloc(
NETPLAY_MITM_MAX_PENDING * sizeof(*timeouts)); NETPLAY_MITM_MAX_PENDING * sizeof(*timeouts));
if (fds && ids && timeouts) if (fds && ids && timeouts)
{ {
size_t i;
memset(fds, -1, memset(fds, -1,
NETPLAY_MITM_MAX_PENDING * sizeof(*fds)); NETPLAY_MITM_MAX_PENDING * sizeof(*fds));
for (i = 0; i < NETPLAY_MITM_MAX_PENDING; i++, ids += NETPLAY_MITM_ID_SIZE)
net_st->mitm_pending.ids[i] = ids;
net_st->mitm_pending.fds = fds; net_st->mitm_pending.fds = fds;
net_st->mitm_pending.ids = ids;
net_st->mitm_pending.timeouts = timeouts; net_st->mitm_pending.timeouts = timeouts;
net_st->mitm_pending.current = 0; net_st->mitm_pending.id_recvd = 0;
net_st->mitm_pending.next = -1;
return fd; return fd;
} }
@ -5867,14 +5877,13 @@ static int init_tcp_connection(const struct addrinfo *res,
host, port); host, port);
dmsg = msg; dmsg = msg;
} }
#else else
dmsg = "Failed to connect to relay server.";
#endif #endif
dmsg = "Failed to connect to relay server.";
} }
} }
else else
{ {
#endif
#if defined(HAVE_INET6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) #if defined(HAVE_INET6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
/* Make sure we accept connections on both IPv6 and IPv4 */ /* Make sure we accept connections on both IPv6 and IPv4 */
if (res->ai_family == AF_INET6) if (res->ai_family == AF_INET6)
@ -5882,7 +5891,7 @@ static int init_tcp_connection(const struct addrinfo *res,
int on = 0; int on = 0;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,
(const char*)&on, sizeof(on)) < 0) (const char*)&on, sizeof(on)) < 0)
RARCH_WARN("Failed to listen on both IPv6 and IPv4\n"); RARCH_WARN("Failed to listen on both IPv6 and IPv4.\n");
} }
#endif #endif
if (socket_bind(fd, (void*)res)) if (socket_bind(fd, (void*)res))
@ -5896,16 +5905,16 @@ static int init_tcp_connection(const struct addrinfo *res,
if (!getnameinfo(res->ai_addr, res->ai_addrlen, if (!getnameinfo(res->ai_addr, res->ai_addrlen,
NULL, 0, port, sizeof(port), NI_NUMERICSERV)) NULL, 0, port, sizeof(port), NI_NUMERICSERV))
{ {
snprintf(msg, sizeof(msg), "Failed to bind port %s.", port); snprintf(msg, sizeof(msg),
"Failed to bind port %s.",
port);
dmsg = msg; dmsg = msg;
} }
#else else
dmsg = "Failed to bind port.";
#endif #endif
dmsg = "Failed to bind port.";
} }
#ifdef __NETPLAY_MITM_NEW
} }
#endif
} }
socket_close(fd); socket_close(fd);
@ -6016,6 +6025,12 @@ try_ipv4:
return false; return false;
} }
if (!socket_nonblock(fd))
{
socket_close(fd);
return false;
}
if (server) if (server)
{ {
netplay->connections[0].active = true; netplay->connections[0].active = true;
@ -6029,13 +6044,13 @@ try_ipv4:
return true; return true;
} }
static bool init_socket(netplay_t *netplay, void *direct_host, static bool init_socket(netplay_t *netplay,
const char *server, uint16_t port) const char *server, const char *mitm, uint16_t port)
{ {
if (!network_init()) if (!network_init())
return false; return false;
if (!init_tcp_socket(netplay, server, NULL, port)) if (!init_tcp_socket(netplay, server, mitm, port))
return false; return false;
if (netplay->is_server && netplay->nat_traversal) if (netplay->is_server && netplay->nat_traversal)
@ -6211,8 +6226,8 @@ static bool netplay_init_buffers(netplay_t *netplay)
/** /**
* netplay_new: * netplay_new:
* @direct_host : Netplay host discovered from scanning.
* @server : IP address of server. * @server : IP address of server.
* @mitm : IP address of the MITM/tunnel server.
* @port : Port of server. * @port : Port of server.
* @stateless_mode : Shall we use stateless mode? * @stateless_mode : Shall we use stateless mode?
* @check_frames : Frequency with which to check CRCs. * @check_frames : Frequency with which to check CRCs.
@ -6226,21 +6241,21 @@ static bool netplay_init_buffers(netplay_t *netplay)
* *
* Returns: new netplay data. * Returns: new netplay data.
*/ */
netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port, netplay_t *netplay_new(const char *server, const char *mitm, uint16_t port,
bool stateless_mode, int check_frames, bool stateless_mode, int check_frames,
const struct retro_callbacks *cb, bool nat_traversal, const char *nick, const struct retro_callbacks *cb, bool nat_traversal, const char *nick,
uint64_t quirks) uint64_t quirks)
{ {
netplay_t *netplay = (netplay_t*)calloc(1, sizeof(*netplay)); netplay_t *netplay = calloc(1, sizeof(*netplay));
if (!netplay) if (!netplay)
return NULL; return NULL;
netplay->listen_fd = -1; netplay->listen_fd = -1;
netplay->tcp_port = port; netplay->tcp_port = port;
netplay->cbs = *cb; netplay->cbs = *cb;
netplay->is_server = (direct_host == NULL && server == NULL); netplay->is_server = !server;
netplay->is_connected = false; netplay->is_connected = false;
netplay->nat_traversal = netplay->is_server ? nat_traversal : false; netplay->nat_traversal = (!server && !mitm) ? nat_traversal : false;
netplay->stateless_mode = stateless_mode; netplay->stateless_mode = stateless_mode;
netplay->check_frames = check_frames; netplay->check_frames = check_frames;
netplay->crc_validity_checked = false; netplay->crc_validity_checked = false;
@ -6266,15 +6281,10 @@ netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port,
? nick : RARCH_DEFAULT_NICK, ? nick : RARCH_DEFAULT_NICK,
sizeof(netplay->nick)); sizeof(netplay->nick));
if (!init_socket(netplay, direct_host, server, port)) if (!init_socket(netplay, server, mitm, port) ||
!netplay_init_buffers(netplay))
{ {
free(netplay); netplay_free(netplay);
return NULL;
}
if (!netplay_init_buffers(netplay))
{
free(netplay);
return NULL; return NULL;
} }
@ -6304,30 +6314,7 @@ netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port,
netplay->self_mode = NETPLAY_CONNECTION_INIT; netplay->self_mode = NETPLAY_CONNECTION_INIT;
} }
/* FIXME: Not really the right place to do this,
* socket initialization needs to be fixed in general. */
if (netplay->is_server)
{
if (!socket_nonblock(netplay->listen_fd))
goto error;
}
else
{
if (!socket_nonblock(netplay->connections[0].fd))
goto error;
}
return netplay; return netplay;
error:
if (netplay->listen_fd >= 0)
socket_close(netplay->listen_fd);
if (netplay->connections && netplay->connections[0].fd >= 0)
socket_close(netplay->connections[0].fd);
free(netplay);
return NULL;
} }
/** /**
@ -7313,34 +7300,6 @@ static void netplay_announce_cb(retro_task_t *task,
} }
} }
if (mitm_ip && mitm_port)
{
ip_len = (unsigned)strlen(mitm_ip);
port_len = (unsigned)strlen(mitm_port);
/* Enable Netplay client mode */
if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL))
{
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
net_st->is_mitm = true;
host_room->host_method = NETPLAY_HOST_METHOD_MITM;
}
netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL);
host_string = (char*)calloc(1, ip_len + port_len + 2);
memcpy(host_string, mitm_ip, ip_len);
memcpy(host_string + ip_len, "|", 1);
memcpy(host_string + ip_len + 1, mitm_port, port_len);
/* Enable Netplay */
command_event(CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED, (void*)host_string);
command_event(CMD_EVENT_NETPLAY_INIT, (void*)host_string);
free(host_string);
}
#ifdef HAVE_DISCORD #ifdef HAVE_DISCORD
if (discord_state_get_ptr()->inited) if (discord_state_get_ptr()->inited)
{ {
@ -7602,11 +7561,10 @@ void deinit_netplay(void)
free(net_st->mitm_pending.fds); free(net_st->mitm_pending.fds);
net_st->mitm_pending.fds = NULL; net_st->mitm_pending.fds = NULL;
} }
if (net_st->mitm_pending.ids[0]) if (net_st->mitm_pending.ids)
{ {
free(net_st->mitm_pending.ids[0]); free(net_st->mitm_pending.ids);
memset(net_st->mitm_pending.ids, 0, net_st->mitm_pending.ids = NULL;
sizeof(net_st->mitm_pending.ids));
} }
if (net_st->mitm_pending.timeouts) if (net_st->mitm_pending.timeouts)
{ {
@ -7616,17 +7574,16 @@ void deinit_netplay(void)
core_unset_netplay_callbacks(); core_unset_netplay_callbacks();
} }
bool init_netplay(void *direct_host, const char *server, unsigned port) bool init_netplay(const char *server, unsigned port)
{ {
struct retro_callbacks cbs = {0}; struct retro_callbacks cbs = {0};
uint64_t serialization_quirks = 0; uint64_t serialization_quirks = 0;
uint64_t quirks = 0; uint64_t quirks = 0;
settings_t *settings = config_get_ptr(); settings_t *settings = config_get_ptr();
net_driver_state_t *net_st = &networking_driver_st; net_driver_state_t *net_st = &networking_driver_st;
bool _netplay_is_client = net_st->netplay_is_client; const char *mitm = NULL;
bool _netplay_enabled = net_st->netplay_enabled;
if (!_netplay_enabled) if (!net_st->netplay_enabled)
return false; return false;
core_set_default_callbacks(&cbs); core_set_default_callbacks(&cbs);
@ -7651,34 +7608,31 @@ bool init_netplay(void *direct_host, const char *server, unsigned port)
if (serialization_quirks & NETPLAY_QUIRK_MAP_PLATFORM_DEPENDENT) if (serialization_quirks & NETPLAY_QUIRK_MAP_PLATFORM_DEPENDENT)
quirks |= NETPLAY_QUIRK_PLATFORM_DEPENDENT; quirks |= NETPLAY_QUIRK_PLATFORM_DEPENDENT;
if (!_netplay_is_client) if (!net_st->netplay_is_client)
{ {
server = NULL;
if (!port)
port = RARCH_DEFAULT_PORT;
runloop_msg_queue_push( runloop_msg_queue_push(
msg_hash_to_str(MSG_WAITING_FOR_CLIENT), msg_hash_to_str(MSG_WAITING_FOR_CLIENT),
0, 180, false, 0, 180, false,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
}
if (settings->bools.netplay_public_announce) else
netplay_announce(); {
if (net_st->netplay_client_deferred)
{
server = (const char*)net_st->server_address_deferred;
port = net_st->server_port_deferred;
}
} }
net_st->data = (netplay_t*)netplay_new( net_st->data = netplay_new(
_netplay_is_client server, mitm, port,
? direct_host
: NULL,
_netplay_is_client
? (!net_st->netplay_client_deferred
? server
: net_st->server_address_deferred)
: NULL,
_netplay_is_client ? (!net_st->netplay_client_deferred
? port
: net_st->server_port_deferred)
: (port != 0 ? port : RARCH_DEFAULT_PORT),
settings->bools.netplay_stateless_mode, settings->bools.netplay_stateless_mode,
settings->ints.netplay_check_frames, settings->ints.netplay_check_frames,
&cbs, &cbs,
settings->bools.netplay_nat_traversal && !settings->bools.netplay_use_mitm_server, settings->bools.netplay_nat_traversal,
#ifdef HAVE_DISCORD #ifdef HAVE_DISCORD
discord_get_own_username() discord_get_own_username()
? discord_get_own_username() ? discord_get_own_username()
@ -7687,21 +7641,21 @@ bool init_netplay(void *direct_host, const char *server, unsigned port)
settings->paths.username, settings->paths.username,
quirks); quirks);
if (net_st->data) if (!net_st->data)
{ {
if ( net_st->data->is_server RARCH_WARN("%s\n", msg_hash_to_str(MSG_NETPLAY_FAILED));
&& !settings->bools.netplay_start_as_spectator) runloop_msg_queue_push(
netplay_toggle_play_spectate(net_st->data); msg_hash_to_str(MSG_NETPLAY_FAILED),
return true; 0, 180, false,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
return false;
} }
RARCH_WARN("%s\n", msg_hash_to_str(MSG_NETPLAY_FAILED)); if ( net_st->data->is_server
&& !settings->bools.netplay_start_as_spectator)
netplay_toggle_play_spectate(net_st->data);
runloop_msg_queue_push( return true;
msg_hash_to_str(MSG_NETPLAY_FAILED),
0, 180, false,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
return false;
} }
/** /**

View File

@ -760,8 +760,8 @@ bool netplay_wait_and_init_serialization(netplay_t *netplay);
/** /**
* netplay_new: * netplay_new:
* @direct_host : Netplay host discovered from scanning.
* @server : IP address of server. * @server : IP address of server.
* @mitm : IP address of the MITM/tunnel server.
* @port : Port of server. * @port : Port of server.
* @stateless_mode : Shall we run in stateless mode? * @stateless_mode : Shall we run in stateless mode?
* @check_frames : Frequency with which to check CRCs. * @check_frames : Frequency with which to check CRCs.
@ -775,8 +775,7 @@ bool netplay_wait_and_init_serialization(netplay_t *netplay);
* *
* Returns: new netplay data. * Returns: new netplay data.
*/ */
netplay_t *netplay_new(void *direct_host, netplay_t *netplay_new(const char *server, const char *mitm, uint16_t port,
const char *server, uint16_t port,
bool stateless_mode, int check_frames, bool stateless_mode, int check_frames,
const struct retro_callbacks *cb, const struct retro_callbacks *cb,
bool nat_traversal, const char *nick, bool nat_traversal, const char *nick,

View File

@ -18,6 +18,6 @@
#ifndef __RARCH_NETPLAY_PROTOCOL_H #ifndef __RARCH_NETPLAY_PROTOCOL_H
#define __RARCH_NETPLAY_PROTOCOL_H #define __RARCH_NETPLAY_PROTOCOL_H
#define NETPLAY_PROTOCOL_VERSION 6 #define NETPLAY_PROTOCOL_VERSION 5
#endif #endif

View File

@ -5239,11 +5239,11 @@ bool command_event(enum event_command cmd, void *data)
/* init netplay manually */ /* init netplay manually */
case CMD_EVENT_NETPLAY_INIT: case CMD_EVENT_NETPLAY_INIT:
{ {
char *hostname = (char*)data; bool success;
char *netplay_server = NULL; char *netplay_server = NULL;
unsigned netplay_port = 0; unsigned netplay_port = 0;
if (p_rarch->connect_host && !hostname) if (p_rarch->connect_host)
{ {
struct string_list *addr_port = string_split(p_rarch->connect_host, "|"); struct string_list *addr_port = string_split(p_rarch->connect_host, "|");
@ -5281,22 +5281,7 @@ bool command_event(enum event_command cmd, void *data)
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL); command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
if (!init_netplay( success = init_netplay(netplay_server, netplay_port);
NULL,
hostname
? hostname
: netplay_server, netplay_port))
{
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
if (p_rarch->connect_host)
{
free(p_rarch->connect_host);
p_rarch->connect_host = NULL;
}
return false;
}
if (p_rarch->connect_host) if (p_rarch->connect_host)
{ {
@ -5304,6 +5289,12 @@ bool command_event(enum event_command cmd, void *data)
p_rarch->connect_host = NULL; p_rarch->connect_host = NULL;
} }
if (!success)
{
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
return false;
}
/* Disable rewind & SRAM autosave if it was enabled /* Disable rewind & SRAM autosave if it was enabled
* TODO/FIXME: Add a setting for these tweaks */ * TODO/FIXME: Add a setting for these tweaks */
#ifdef HAVE_REWIND #ifdef HAVE_REWIND
@ -5317,34 +5308,28 @@ bool command_event(enum event_command cmd, void *data)
/* Initialize netplay via lobby when content is loaded */ /* Initialize netplay via lobby when content is loaded */
case CMD_EVENT_NETPLAY_INIT_DIRECT: case CMD_EVENT_NETPLAY_INIT_DIRECT:
{ {
/* buf is expected to be address|port */ bool success;
static struct string_list *hostname = NULL; /* Expected to be address|port */
char *buf = (char *)data; struct string_list *hostname = string_split((char *)data, "|");
unsigned netplay_port = settings->uints.netplay_port; unsigned netplay_port = !string_is_empty(hostname->elems[1].data) ?
strtoul(hostname->elems[1].data, NULL, 10) :
hostname = string_split(buf, "|"); settings->uints.netplay_port;
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL); command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
RARCH_LOG("[Netplay]: Connecting to %s:%d (direct)\n", RARCH_LOG("[Netplay]: Connecting to %s:%d (direct)\n",
hostname->elems[0].data, !string_is_empty(hostname->elems[1].data) hostname->elems[0].data, netplay_port);
? atoi(hostname->elems[1].data)
: netplay_port);
if (!init_netplay( success = init_netplay(hostname->elems[0].data, netplay_port);
NULL,
hostname->elems[0].data,
!string_is_empty(hostname->elems[1].data)
? atoi(hostname->elems[1].data)
: netplay_port))
{
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
string_list_free(hostname);
return false;
}
string_list_free(hostname); string_list_free(hostname);
if (!success)
{
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
return false;
}
/* Disable rewind if it was enabled /* Disable rewind if it was enabled
TODO/FIXME: Add a setting for these tweaks */ TODO/FIXME: Add a setting for these tweaks */
#ifdef HAVE_REWIND #ifdef HAVE_REWIND