Revert "(Cthulhu88) New Netplay MITM Part 2"

This reverts commit 34c374737c1c09497bf28af090a045656ac07bde.
This commit is contained in:
twinaphex 2021-11-12 18:58:40 +01:00
parent 196cc8b33d
commit 70ee3fbca7
5 changed files with 176 additions and 120 deletions

View File

@ -40,6 +40,7 @@
#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
{ {
@ -187,22 +188,14 @@ 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;
mitm_id_t *ids; uint8_t *ids[NETPLAY_MITM_MAX_PENDING];
retro_time_t *timeouts; retro_time_t *timeouts;
mitm_id_t id_buf; int current;
size_t id_recvd; int next;
}; };
typedef struct typedef struct
@ -230,7 +223,7 @@ typedef struct
bool in_netplay; bool in_netplay;
bool netplay_client_deferred; bool netplay_client_deferred;
bool is_mitm; bool is_mitm;
mitm_id_t mitm_session_id; uint8_t mitm_id[NETPLAY_MITM_ID_SIZE];
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;
@ -350,6 +343,7 @@ 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
* *
@ -359,7 +353,7 @@ void deinit_netplay(void);
* *
* Returns: true (1) if successful, otherwise false (0). * Returns: true (1) if successful, otherwise false (0).
**/ **/
bool init_netplay(const char *server, unsigned port); bool init_netplay(void *direct_host, 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,10 +91,6 @@
#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
@ -2176,7 +2172,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, true)) if (!socket_send_all_blocking(sockfd, buf, len, false))
return false; return false;
return true; return true;
} }
@ -2434,7 +2430,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), true); sizeof(header), false);
return true; return true;
} }
@ -5805,55 +5801,49 @@ static int init_tcp_connection(const struct addrinfo *res,
host, port); host, port);
dmsg = msg; dmsg = msg;
} }
else #else
#endif
dmsg = "Failed to connect to host."; dmsg = "Failed to connect to host.";
#endif
} }
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_send_all_blocking(fd, if (socket_receive_all_blocking(fd, net_st->mitm_id, sizeof(net_st->mitm_id)))
&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;
mitm_id_t *ids; uint8_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 * sizeof(*ids)); NETPLAY_MITM_MAX_PENDING * NETPLAY_MITM_ID_SIZE * 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.id_recvd = 0; net_st->mitm_pending.current = 0;
net_st->mitm_pending.next = -1;
return fd; return fd;
} }
@ -5877,13 +5867,14 @@ static int init_tcp_connection(const struct addrinfo *res,
host, port); host, port);
dmsg = msg; dmsg = msg;
} }
else #else
#endif
dmsg = "Failed to connect to relay server."; dmsg = "Failed to connect to relay server.";
#endif
} }
} }
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)
@ -5891,7 +5882,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))
@ -5905,16 +5896,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), snprintf(msg, sizeof(msg), "Failed to bind port %s.", port);
"Failed to bind port %s.",
port);
dmsg = msg; dmsg = msg;
} }
else #else
#endif
dmsg = "Failed to bind port."; dmsg = "Failed to bind port.";
#endif
} }
#ifdef __NETPLAY_MITM_NEW
} }
#endif
} }
socket_close(fd); socket_close(fd);
@ -6025,12 +6016,6 @@ 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;
@ -6044,13 +6029,13 @@ try_ipv4:
return true; return true;
} }
static bool init_socket(netplay_t *netplay, static bool init_socket(netplay_t *netplay, void *direct_host,
const char *server, const char *mitm, uint16_t port) const char *server, uint16_t port)
{ {
if (!network_init()) if (!network_init())
return false; return false;
if (!init_tcp_socket(netplay, server, mitm, port)) if (!init_tcp_socket(netplay, server, NULL, port))
return false; return false;
if (netplay->is_server && netplay->nat_traversal) if (netplay->is_server && netplay->nat_traversal)
@ -6226,8 +6211,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.
@ -6241,21 +6226,21 @@ static bool netplay_init_buffers(netplay_t *netplay)
* *
* Returns: new netplay data. * Returns: new netplay data.
*/ */
netplay_t *netplay_new(const char *server, const char *mitm, uint16_t port, netplay_t *netplay_new(void *direct_host, const char *server, 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 = calloc(1, sizeof(*netplay)); netplay_t *netplay = (netplay_t*)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 = !server; netplay->is_server = (direct_host == NULL && server == NULL);
netplay->is_connected = false; netplay->is_connected = false;
netplay->nat_traversal = (!server && !mitm) ? nat_traversal : false; netplay->nat_traversal = netplay->is_server ? 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;
@ -6281,10 +6266,15 @@ netplay_t *netplay_new(const char *server, const char *mitm, uint16_t port,
? nick : RARCH_DEFAULT_NICK, ? nick : RARCH_DEFAULT_NICK,
sizeof(netplay->nick)); sizeof(netplay->nick));
if (!init_socket(netplay, server, mitm, port) || if (!init_socket(netplay, direct_host, server, port))
!netplay_init_buffers(netplay))
{ {
netplay_free(netplay); free(netplay);
return NULL;
}
if (!netplay_init_buffers(netplay))
{
free(netplay);
return NULL; return NULL;
} }
@ -6314,7 +6304,30 @@ netplay_t *netplay_new(const char *server, const char *mitm, 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;
} }
/** /**
@ -7300,6 +7313,34 @@ 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)
{ {
@ -7561,10 +7602,11 @@ 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) if (net_st->mitm_pending.ids[0])
{ {
free(net_st->mitm_pending.ids); free(net_st->mitm_pending.ids[0]);
net_st->mitm_pending.ids = NULL; memset(net_st->mitm_pending.ids, 0,
sizeof(net_st->mitm_pending.ids));
} }
if (net_st->mitm_pending.timeouts) if (net_st->mitm_pending.timeouts)
{ {
@ -7574,16 +7616,17 @@ void deinit_netplay(void)
core_unset_netplay_callbacks(); core_unset_netplay_callbacks();
} }
bool init_netplay(const char *server, unsigned port) bool init_netplay(void *direct_host, 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;
const char *mitm = NULL; bool _netplay_is_client = net_st->netplay_is_client;
bool _netplay_enabled = net_st->netplay_enabled;
if (!net_st->netplay_enabled) if (!_netplay_enabled)
return false; return false;
core_set_default_callbacks(&cbs); core_set_default_callbacks(&cbs);
@ -7608,31 +7651,34 @@ bool init_netplay(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 (!net_st->netplay_is_client) if (!_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);
}
else if (settings->bools.netplay_public_announce)
{ 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_new( net_st->data = (netplay_t*)netplay_new(
server, mitm, port, _netplay_is_client
? 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_nat_traversal && !settings->bools.netplay_use_mitm_server,
#ifdef HAVE_DISCORD #ifdef HAVE_DISCORD
discord_get_own_username() discord_get_own_username()
? discord_get_own_username() ? discord_get_own_username()
@ -7641,21 +7687,21 @@ bool init_netplay(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
&& !settings->bools.netplay_start_as_spectator)
netplay_toggle_play_spectate(net_st->data);
return true;
}
RARCH_WARN("%s\n", msg_hash_to_str(MSG_NETPLAY_FAILED)); RARCH_WARN("%s\n", msg_hash_to_str(MSG_NETPLAY_FAILED));
runloop_msg_queue_push( runloop_msg_queue_push(
msg_hash_to_str(MSG_NETPLAY_FAILED), msg_hash_to_str(MSG_NETPLAY_FAILED),
0, 180, false, 0, 180, false,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
return false; return false;
}
if ( net_st->data->is_server
&& !settings->bools.netplay_start_as_spectator)
netplay_toggle_play_spectate(net_st->data);
return true;
} }
/** /**

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,7 +775,8 @@ bool netplay_wait_and_init_serialization(netplay_t *netplay);
* *
* Returns: new netplay data. * Returns: new netplay data.
*/ */
netplay_t *netplay_new(const char *server, const char *mitm, uint16_t port, netplay_t *netplay_new(void *direct_host,
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 5 #define NETPLAY_PROTOCOL_VERSION 6
#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:
{ {
bool success; char *hostname = (char*)data;
char *netplay_server = NULL; char *netplay_server = NULL;
unsigned netplay_port = 0; unsigned netplay_port = 0;
if (p_rarch->connect_host) if (p_rarch->connect_host && !hostname)
{ {
struct string_list *addr_port = string_split(p_rarch->connect_host, "|"); struct string_list *addr_port = string_split(p_rarch->connect_host, "|");
@ -5281,7 +5281,13 @@ bool command_event(enum event_command cmd, void *data)
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL); command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
success = init_netplay(netplay_server, netplay_port); if (!init_netplay(
NULL,
hostname
? hostname
: netplay_server, netplay_port))
{
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
if (p_rarch->connect_host) if (p_rarch->connect_host)
{ {
@ -5289,12 +5295,15 @@ 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; return false;
} }
if (p_rarch->connect_host)
{
free(p_rarch->connect_host);
p_rarch->connect_host = NULL;
}
/* 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
@ -5308,28 +5317,34 @@ 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:
{ {
bool success; /* buf is expected to be address|port */
/* Expected to be address|port */ static struct string_list *hostname = NULL;
struct string_list *hostname = string_split((char *)data, "|"); char *buf = (char *)data;
unsigned netplay_port = !string_is_empty(hostname->elems[1].data) ? unsigned netplay_port = settings->uints.netplay_port;
strtoul(hostname->elems[1].data, NULL, 10) :
settings->uints.netplay_port; hostname = string_split(buf, "|");
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, netplay_port); hostname->elems[0].data, !string_is_empty(hostname->elems[1].data)
? atoi(hostname->elems[1].data)
: netplay_port);
success = init_netplay(hostname->elems[0].data, netplay_port); if (!init_netplay(
NULL,
string_list_free(hostname); hostname->elems[0].data,
!string_is_empty(hostname->elems[1].data)
if (!success) ? atoi(hostname->elems[1].data)
: netplay_port))
{ {
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL); command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
string_list_free(hostname);
return false; return false;
} }
string_list_free(hostname);
/* 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