2012-04-21 21:13:50 +00:00
|
|
|
/* RetroArch - A frontend for libretro.
|
2014-01-01 00:50:59 +00:00
|
|
|
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
2016-01-10 03:06:50 +00:00
|
|
|
* Copyright (C) 2011-2016 - Daniel De Matteis
|
2016-09-13 21:06:23 +00:00
|
|
|
* Copyright (C) 2016 - Gregor Richards
|
2011-02-13 15:40:24 +00:00
|
|
|
*
|
2012-04-21 21:13:50 +00:00
|
|
|
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
2011-02-13 15:40:24 +00:00
|
|
|
* of the GNU General Public License as published by the Free Software Found-
|
|
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
|
|
*
|
2012-04-21 21:13:50 +00:00
|
|
|
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
2011-02-13 15:40:24 +00:00
|
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
|
|
*
|
2012-04-21 21:31:57 +00:00
|
|
|
* You should have received a copy of the GNU General Public License along with RetroArch.
|
2011-02-13 15:40:24 +00:00
|
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
2011-12-04 17:03:08 +00:00
|
|
|
|
2012-11-15 13:32:06 +00:00
|
|
|
#if defined(_MSC_VER) && !defined(_XBOX)
|
2012-11-15 08:40:31 +00:00
|
|
|
#pragma comment(lib, "ws2_32")
|
2012-11-14 20:01:40 +00:00
|
|
|
#endif
|
|
|
|
|
2016-12-14 01:50:17 +00:00
|
|
|
#include <stdio.h>
|
2015-03-20 16:56:00 +00:00
|
|
|
#include <stdlib.h>
|
2016-12-14 01:50:17 +00:00
|
|
|
#include <sys/types.h>
|
2015-09-04 19:11:00 +00:00
|
|
|
|
2016-12-14 01:50:17 +00:00
|
|
|
#include <boolean.h>
|
2016-09-05 22:56:00 +00:00
|
|
|
#include <compat/strl.h>
|
2015-09-04 19:11:00 +00:00
|
|
|
|
2015-12-23 20:25:28 +00:00
|
|
|
#include "netplay_private.h"
|
2016-12-14 01:50:17 +00:00
|
|
|
|
2016-12-03 02:16:15 +00:00
|
|
|
#include "netplay_discovery.h"
|
2015-12-05 15:41:00 +00:00
|
|
|
|
2016-09-30 18:03:18 +00:00
|
|
|
#include "../../autosave.h"
|
2016-09-03 05:51:11 +00:00
|
|
|
#include "../../runloop.h"
|
2016-09-03 05:45:51 +00:00
|
|
|
|
2016-12-10 10:27:25 +00:00
|
|
|
#if defined(AF_INET6) && !defined(HAVE_SOCKET_LEGACY)
|
|
|
|
#define HAVE_INET6 1
|
|
|
|
#endif
|
|
|
|
|
2016-09-29 18:11:46 +00:00
|
|
|
static int init_tcp_connection(const struct addrinfo *res,
|
2016-12-10 04:04:39 +00:00
|
|
|
bool server,
|
2016-09-29 18:11:46 +00:00
|
|
|
struct sockaddr *other_addr, socklen_t addr_size)
|
|
|
|
{
|
|
|
|
bool ret = true;
|
|
|
|
int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
{
|
|
|
|
ret = false;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(IPPROTO_TCP) && defined(TCP_NODELAY)
|
|
|
|
{
|
|
|
|
int flag = 1;
|
2006-05-18 11:31:43 +00:00
|
|
|
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
|
|
|
|
#ifdef _WIN32
|
|
|
|
(const char*)
|
|
|
|
#else
|
|
|
|
(const void*)
|
|
|
|
#endif
|
|
|
|
&flag,
|
|
|
|
sizeof(int)) < 0)
|
2016-09-29 18:11:46 +00:00
|
|
|
RARCH_WARN("Could not set netplay TCP socket to nodelay. Expect jitter.\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(F_SETFD) && defined(FD_CLOEXEC)
|
|
|
|
/* Don't let any inherited processes keep open our port */
|
|
|
|
if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
|
|
|
|
RARCH_WARN("Cannot set Netplay port to close-on-exec. It may fail to reopen if the client disconnects.\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (server)
|
|
|
|
{
|
|
|
|
if (socket_connect(fd, (void*)res, false) < 0)
|
|
|
|
{
|
|
|
|
ret = false;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-12-10 10:27:25 +00:00
|
|
|
#if defined(HAVE_INET6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
|
2016-12-05 05:45:40 +00:00
|
|
|
/* Make sure we accept connections on both IPv6 and IPv4 */
|
|
|
|
int on = 0;
|
|
|
|
if (res->ai_family == AF_INET6)
|
|
|
|
{
|
2016-12-06 01:50:05 +00:00
|
|
|
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&on, sizeof(on)) < 0)
|
2016-12-05 05:45:40 +00:00
|
|
|
RARCH_WARN("Failed to listen on both IPv6 and IPv4\n");
|
|
|
|
}
|
|
|
|
#endif
|
2016-09-29 18:11:46 +00:00
|
|
|
if ( !socket_bind(fd, (void*)res) ||
|
2016-12-10 04:04:39 +00:00
|
|
|
listen(fd, 1024) < 0)
|
2016-09-29 18:11:46 +00:00
|
|
|
{
|
|
|
|
ret = false;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
|
|
|
if (!ret && fd >= 0)
|
|
|
|
{
|
|
|
|
socket_close(fd);
|
|
|
|
fd = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2016-12-03 02:16:15 +00:00
|
|
|
static bool init_tcp_socket(netplay_t *netplay, void *direct_host,
|
2016-12-10 04:04:39 +00:00
|
|
|
const char *server, uint16_t port)
|
2016-09-29 18:11:46 +00:00
|
|
|
{
|
2016-10-21 17:39:51 +00:00
|
|
|
char port_buf[16];
|
2016-09-29 18:11:46 +00:00
|
|
|
bool ret = false;
|
|
|
|
const struct addrinfo *tmp_info = NULL;
|
|
|
|
struct addrinfo *res = NULL;
|
|
|
|
struct addrinfo hints = {0};
|
|
|
|
|
2016-10-21 17:39:51 +00:00
|
|
|
port_buf[0] = '\0';
|
|
|
|
|
2016-12-03 02:16:15 +00:00
|
|
|
if (!direct_host)
|
|
|
|
{
|
2016-12-10 10:27:25 +00:00
|
|
|
#ifdef HAVE_INET6
|
2016-12-05 05:45:40 +00:00
|
|
|
/* Default to hosting on IPv6 and IPv4 */
|
2016-12-03 02:16:15 +00:00
|
|
|
if (!server)
|
|
|
|
hints.ai_family = AF_INET6;
|
2016-11-30 05:25:16 +00:00
|
|
|
#endif
|
2016-12-03 02:16:15 +00:00
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
if (!server)
|
|
|
|
hints.ai_flags = AI_PASSIVE;
|
2016-09-29 18:11:46 +00:00
|
|
|
|
2016-12-03 02:16:15 +00:00
|
|
|
snprintf(port_buf, sizeof(port_buf), "%hu", (unsigned short)port);
|
|
|
|
if (getaddrinfo_retro(server, port_buf, &hints, &res) < 0)
|
2016-12-05 05:45:40 +00:00
|
|
|
{
|
2016-12-10 10:27:25 +00:00
|
|
|
#ifdef HAVE_INET6
|
2016-12-05 05:45:40 +00:00
|
|
|
if (!server)
|
|
|
|
{
|
|
|
|
/* Didn't work with IPv6, try wildcard */
|
|
|
|
hints.ai_family = 0;
|
|
|
|
if (getaddrinfo_retro(server, port_buf, &hints, &res) < 0)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
2016-12-03 02:16:15 +00:00
|
|
|
return false;
|
2016-12-05 05:45:40 +00:00
|
|
|
}
|
2016-09-29 18:11:46 +00:00
|
|
|
|
2016-12-03 02:16:15 +00:00
|
|
|
if (!res)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* I'll build my own addrinfo! With blackjack and hookers! */
|
|
|
|
struct netplay_host *host = (struct netplay_host *) direct_host;
|
|
|
|
hints.ai_family = host->addr.sa_family;
|
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
2016-12-03 21:33:48 +00:00
|
|
|
hints.ai_protocol = 0;
|
2016-12-03 02:16:15 +00:00
|
|
|
hints.ai_addrlen = host->addrlen;
|
|
|
|
hints.ai_addr = &host->addr;
|
|
|
|
res = &hints;
|
|
|
|
|
|
|
|
}
|
2016-09-29 18:11:46 +00:00
|
|
|
|
2016-11-30 05:25:16 +00:00
|
|
|
/* If we're serving on IPv6, make sure we accept all connections, including
|
|
|
|
* IPv4 */
|
2016-12-10 10:27:25 +00:00
|
|
|
#ifdef HAVE_INET6
|
2016-12-03 02:16:15 +00:00
|
|
|
if (!direct_host && !server && res->ai_family == AF_INET6)
|
2016-11-30 05:25:16 +00:00
|
|
|
{
|
|
|
|
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) res->ai_addr;
|
|
|
|
sin6->sin6_addr = in6addr_any;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-09-29 18:11:46 +00:00
|
|
|
/* If "localhost" is used, it is important to check every possible
|
|
|
|
* address for IPv4/IPv6. */
|
|
|
|
tmp_info = res;
|
|
|
|
|
|
|
|
while (tmp_info)
|
|
|
|
{
|
2016-12-10 03:29:02 +00:00
|
|
|
struct sockaddr_storage sad;
|
2016-09-29 18:11:46 +00:00
|
|
|
int fd = init_tcp_connection(
|
|
|
|
tmp_info,
|
2016-12-03 02:16:15 +00:00
|
|
|
direct_host || server,
|
2016-12-10 03:29:02 +00:00
|
|
|
(struct sockaddr*)&sad,
|
|
|
|
sizeof(sad));
|
2016-09-29 18:11:46 +00:00
|
|
|
|
|
|
|
if (fd >= 0)
|
|
|
|
{
|
|
|
|
ret = true;
|
2016-12-05 05:04:01 +00:00
|
|
|
if (direct_host || server)
|
2016-12-09 19:14:54 +00:00
|
|
|
{
|
|
|
|
netplay->connections[0].active = true;
|
2016-12-05 05:04:01 +00:00
|
|
|
netplay->connections[0].fd = fd;
|
2016-12-10 03:29:02 +00:00
|
|
|
netplay->connections[0].addr = sad;
|
2016-12-09 19:14:54 +00:00
|
|
|
}
|
2016-12-05 05:04:01 +00:00
|
|
|
else
|
2016-12-09 19:14:54 +00:00
|
|
|
{
|
2016-12-05 05:04:01 +00:00
|
|
|
netplay->listen_fd = fd;
|
2016-12-09 19:14:54 +00:00
|
|
|
}
|
2016-09-29 18:11:46 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp_info = tmp_info->ai_next;
|
|
|
|
}
|
2015-12-26 07:10:37 +00:00
|
|
|
|
2016-12-03 02:16:15 +00:00
|
|
|
if (res && !direct_host)
|
2016-09-29 18:11:46 +00:00
|
|
|
freeaddrinfo_retro(res);
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
RARCH_ERR("Failed to set up netplay sockets.\n");
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-12-03 02:16:15 +00:00
|
|
|
static bool init_socket(netplay_t *netplay, void *direct_host, const char *server, uint16_t port)
|
2016-09-29 18:11:46 +00:00
|
|
|
{
|
|
|
|
if (!network_init())
|
|
|
|
return false;
|
|
|
|
|
2016-12-10 04:04:39 +00:00
|
|
|
if (!init_tcp_socket(netplay, direct_host, server, port))
|
2016-09-29 18:11:46 +00:00
|
|
|
return false;
|
|
|
|
|
2016-11-30 03:59:46 +00:00
|
|
|
if (netplay->is_server && netplay->nat_traversal)
|
2016-12-14 01:16:22 +00:00
|
|
|
netplay_init_nat_traversal(netplay);
|
2016-11-30 03:59:46 +00:00
|
|
|
|
2016-09-29 18:11:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
2016-09-22 02:36:29 +00:00
|
|
|
|
2016-12-09 19:14:54 +00:00
|
|
|
static bool netplay_init_socket_buffers(netplay_t *netplay)
|
|
|
|
{
|
|
|
|
/* Make our packet buffer big enough for a save state and frames-many frames
|
|
|
|
* of input data, plus the headers for each of them */
|
|
|
|
size_t i;
|
|
|
|
size_t packet_buffer_size = netplay->zbuffer_size +
|
|
|
|
netplay->delay_frames * WORDS_PER_FRAME + (netplay->delay_frames+1)*3;
|
|
|
|
netplay->packet_buffer_size = packet_buffer_size;
|
|
|
|
|
|
|
|
for (i = 0; i < netplay->connections_size; i++)
|
|
|
|
{
|
|
|
|
struct netplay_connection *connection = &netplay->connections[i];
|
|
|
|
if (connection->active)
|
|
|
|
{
|
|
|
|
if (connection->send_packet_buffer.data)
|
|
|
|
{
|
|
|
|
if (!netplay_resize_socket_buffer(&connection->send_packet_buffer,
|
|
|
|
packet_buffer_size) ||
|
|
|
|
!netplay_resize_socket_buffer(&connection->recv_packet_buffer,
|
|
|
|
packet_buffer_size))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!netplay_init_socket_buffer(&connection->send_packet_buffer,
|
|
|
|
packet_buffer_size) ||
|
|
|
|
!netplay_init_socket_buffer(&connection->recv_packet_buffer,
|
|
|
|
packet_buffer_size))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-12-15 13:42:03 +00:00
|
|
|
bool netplay_init_serialization(netplay_t *netplay)
|
|
|
|
{
|
|
|
|
unsigned i;
|
|
|
|
retro_ctx_size_info_t info;
|
|
|
|
|
|
|
|
if (netplay->state_size)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
core_serialize_size(&info);
|
|
|
|
|
|
|
|
if (!info.size)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
netplay->state_size = info.size;
|
|
|
|
|
|
|
|
for (i = 0; i < netplay->buffer_size; i++)
|
|
|
|
{
|
|
|
|
netplay->buffer[i].state = calloc(netplay->state_size, 1);
|
|
|
|
|
|
|
|
if (!netplay->buffer[i].state)
|
|
|
|
{
|
|
|
|
netplay->quirks |= NETPLAY_QUIRK_NO_SAVESTATES;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
netplay->zbuffer_size = netplay->state_size * 2;
|
|
|
|
netplay->zbuffer = (uint8_t *) calloc(netplay->zbuffer_size, 1);
|
|
|
|
if (!netplay->zbuffer)
|
|
|
|
{
|
|
|
|
netplay->quirks |= NETPLAY_QUIRK_NO_TRANSMISSION;
|
|
|
|
netplay->zbuffer_size = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-12-14 01:16:22 +00:00
|
|
|
/**
|
|
|
|
* netplay_try_init_serialization
|
|
|
|
*
|
|
|
|
* Try to initialize serialization. For quirky cores.
|
|
|
|
*
|
|
|
|
* Returns true if serialization is now ready, false otherwise.
|
|
|
|
*/
|
2016-09-30 23:04:58 +00:00
|
|
|
bool netplay_try_init_serialization(netplay_t *netplay)
|
|
|
|
{
|
|
|
|
retro_ctx_serialize_info_t serial_info;
|
2016-12-01 04:21:34 +00:00
|
|
|
size_t packet_buffer_size;
|
2016-09-30 23:04:58 +00:00
|
|
|
|
|
|
|
if (netplay->state_size)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!netplay_init_serialization(netplay))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Check if we can actually save */
|
|
|
|
serial_info.data_const = NULL;
|
|
|
|
serial_info.data = netplay->buffer[netplay->self_ptr].state;
|
|
|
|
serial_info.size = netplay->state_size;
|
|
|
|
|
|
|
|
if (!core_serialize(&serial_info))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Once initialized, we no longer exhibit this quirk */
|
|
|
|
netplay->quirks &= ~((uint64_t) NETPLAY_QUIRK_INITIALIZATION);
|
|
|
|
|
2016-12-09 19:14:54 +00:00
|
|
|
return netplay_init_socket_buffers(netplay);
|
2016-09-30 23:04:58 +00:00
|
|
|
}
|
|
|
|
|
2016-12-15 13:42:03 +00:00
|
|
|
/**
|
|
|
|
* netplay_wait_and_init_serialization
|
|
|
|
*
|
|
|
|
* Try very hard to initialize serialization, simulating multiple frames if
|
|
|
|
* necessary. For quirky cores.
|
|
|
|
*
|
|
|
|
* Returns true if serialization is now ready, false otherwise.
|
|
|
|
*/
|
2016-09-30 18:03:18 +00:00
|
|
|
bool netplay_wait_and_init_serialization(netplay_t *netplay)
|
|
|
|
{
|
2016-09-30 23:04:58 +00:00
|
|
|
int frame;
|
2016-09-30 18:03:18 +00:00
|
|
|
|
|
|
|
if (netplay->state_size)
|
|
|
|
return true;
|
|
|
|
|
2016-09-30 23:04:58 +00:00
|
|
|
/* Wait a maximum of 60 frames */
|
|
|
|
for (frame = 0; frame < 60; frame++) {
|
|
|
|
if (netplay_try_init_serialization(netplay))
|
|
|
|
return true;
|
|
|
|
|
2016-09-30 18:03:18 +00:00
|
|
|
#if defined(HAVE_THREADS)
|
2016-09-30 23:04:58 +00:00
|
|
|
autosave_lock();
|
2016-09-30 18:03:18 +00:00
|
|
|
#endif
|
2016-09-30 23:04:58 +00:00
|
|
|
core_run();
|
2016-09-30 18:03:18 +00:00
|
|
|
#if defined(HAVE_THREADS)
|
2016-09-30 23:04:58 +00:00
|
|
|
autosave_unlock();
|
2016-09-30 18:03:18 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-09-30 23:04:58 +00:00
|
|
|
return false;
|
2016-09-30 18:03:18 +00:00
|
|
|
}
|
|
|
|
|
2016-09-16 03:04:48 +00:00
|
|
|
static bool netplay_init_buffers(netplay_t *netplay, unsigned frames)
|
|
|
|
{
|
2016-09-25 15:05:50 +00:00
|
|
|
size_t packet_buffer_size;
|
|
|
|
|
2016-09-16 03:04:48 +00:00
|
|
|
if (!netplay)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* * 2 + 1 because:
|
|
|
|
* Self sits in the middle,
|
|
|
|
* Other is allowed to drift as much as 'frames' frames behind
|
|
|
|
* Read is allowed to drift as much as 'frames' frames ahead */
|
|
|
|
netplay->buffer_size = frames * 2 + 1;
|
|
|
|
|
|
|
|
netplay->buffer = (struct delta_frame*)calloc(netplay->buffer_size,
|
|
|
|
sizeof(*netplay->buffer));
|
|
|
|
|
|
|
|
if (!netplay->buffer)
|
|
|
|
return false;
|
|
|
|
|
2016-12-09 19:14:54 +00:00
|
|
|
if (!(netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_INITIALIZATION)))
|
2016-09-30 23:04:58 +00:00
|
|
|
netplay_init_serialization(netplay);
|
2016-09-16 03:04:48 +00:00
|
|
|
|
2016-12-09 19:14:54 +00:00
|
|
|
return netplay_init_socket_buffers(netplay);
|
2016-09-16 03:04:48 +00:00
|
|
|
}
|
|
|
|
|
2015-01-09 17:34:00 +00:00
|
|
|
/**
|
|
|
|
* netplay_new:
|
2016-12-03 02:16:15 +00:00
|
|
|
* @direct_host : Netplay host discovered from scanning.
|
2015-01-09 17:34:00 +00:00
|
|
|
* @server : IP address of server.
|
|
|
|
* @port : Port of server.
|
2016-12-15 03:18:24 +00:00
|
|
|
* @play_password : Password required to play.
|
|
|
|
* @spectate_password : Password required to connect.
|
2016-12-01 18:34:37 +00:00
|
|
|
* @delay_frames : Amount of delay frames.
|
2016-09-15 03:54:18 +00:00
|
|
|
* @check_frames : Frequency with which to check CRCs.
|
2015-01-09 17:34:00 +00:00
|
|
|
* @cb : Libretro callbacks.
|
2016-11-30 03:59:46 +00:00
|
|
|
* @nat_traversal : If true, attempt NAT traversal.
|
2015-01-09 17:34:00 +00:00
|
|
|
* @nick : Nickname of user.
|
2016-09-30 17:31:58 +00:00
|
|
|
* @quirks : Netplay quirks required for this session.
|
2015-01-09 17:34:00 +00:00
|
|
|
*
|
2016-12-15 13:42:03 +00:00
|
|
|
* Creates a new netplay handle. A NULL server means we're
|
|
|
|
* hosting.
|
2015-01-09 17:34:00 +00:00
|
|
|
*
|
2016-12-15 13:42:03 +00:00
|
|
|
* Returns: new netplay data.
|
|
|
|
*/
|
2016-12-03 02:16:15 +00:00
|
|
|
netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port,
|
2016-12-15 03:18:24 +00:00
|
|
|
const char *play_password, const char *spectate_password,
|
2016-12-15 13:42:03 +00:00
|
|
|
unsigned delay_frames, unsigned check_frames,
|
|
|
|
const struct retro_callbacks *cb, bool nat_traversal, const char *nick,
|
|
|
|
uint64_t quirks)
|
2011-02-14 15:10:53 +00:00
|
|
|
{
|
2016-09-15 19:26:10 +00:00
|
|
|
netplay_t *netplay = (netplay_t*)calloc(1, sizeof(*netplay));
|
2015-01-09 02:21:47 +00:00
|
|
|
if (!netplay)
|
|
|
|
return NULL;
|
2011-02-14 15:10:53 +00:00
|
|
|
|
2016-12-05 05:04:01 +00:00
|
|
|
netplay->listen_fd = -1;
|
2016-09-25 03:47:57 +00:00
|
|
|
netplay->tcp_port = port;
|
2015-11-18 00:45:24 +00:00
|
|
|
netplay->cbs = *cb;
|
2016-12-11 01:36:57 +00:00
|
|
|
netplay->connected_players = 0;
|
2016-12-15 03:02:01 +00:00
|
|
|
netplay->player_max = 1;
|
2015-12-23 20:25:28 +00:00
|
|
|
netplay->is_server = server == NULL;
|
2016-11-30 03:59:46 +00:00
|
|
|
netplay->nat_traversal = netplay->is_server ? nat_traversal : false;
|
2016-12-01 18:34:37 +00:00
|
|
|
netplay->delay_frames = delay_frames;
|
2016-10-22 03:23:45 +00:00
|
|
|
netplay->check_frames = check_frames;
|
|
|
|
netplay->quirks = quirks;
|
2016-12-04 04:08:31 +00:00
|
|
|
netplay->self_mode = netplay->is_server ?
|
|
|
|
NETPLAY_CONNECTION_PLAYING :
|
|
|
|
NETPLAY_CONNECTION_NONE;
|
2016-10-22 03:23:45 +00:00
|
|
|
|
2016-12-05 05:04:01 +00:00
|
|
|
if (netplay->is_server)
|
|
|
|
{
|
|
|
|
netplay->connections = NULL;
|
|
|
|
netplay->connections_size = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
netplay->connections = &netplay->one_connection;
|
|
|
|
netplay->connections_size = 1;
|
|
|
|
netplay->connections[0].fd = -1;
|
|
|
|
}
|
|
|
|
|
2016-12-03 03:40:26 +00:00
|
|
|
strlcpy(netplay->nick, nick[0] ? nick : RARCH_DEFAULT_NICK, sizeof(netplay->nick));
|
2016-12-15 03:18:24 +00:00
|
|
|
strlcpy(netplay->play_password, play_password ? play_password : "", sizeof(netplay->play_password));
|
|
|
|
strlcpy(netplay->spectate_password, spectate_password ? spectate_password : "", sizeof(netplay->spectate_password));
|
2011-02-18 01:01:47 +00:00
|
|
|
|
2016-12-03 02:16:15 +00:00
|
|
|
if (!init_socket(netplay, direct_host, server, port))
|
2011-02-18 01:01:47 +00:00
|
|
|
{
|
2015-01-09 02:21:47 +00:00
|
|
|
free(netplay);
|
|
|
|
return NULL;
|
2011-02-18 01:01:47 +00:00
|
|
|
}
|
|
|
|
|
2016-12-09 19:14:54 +00:00
|
|
|
if (!netplay_init_buffers(netplay, delay_frames))
|
|
|
|
{
|
|
|
|
free(netplay);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!netplay->is_server)
|
|
|
|
{
|
2016-12-10 04:11:18 +00:00
|
|
|
netplay_handshake_init_send(netplay, &netplay->connections[0]);
|
|
|
|
netplay->connections[0].mode = netplay->self_mode = NETPLAY_CONNECTION_INIT;
|
2016-12-09 19:14:54 +00:00
|
|
|
}
|
|
|
|
|
2016-12-05 05:04:01 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
2016-09-25 15:05:50 +00:00
|
|
|
|
2015-01-09 02:21:47 +00:00
|
|
|
return netplay;
|
2012-01-21 13:00:11 +00:00
|
|
|
|
2015-01-09 02:21:47 +00:00
|
|
|
error:
|
2016-12-05 05:04:01 +00:00
|
|
|
if (netplay->listen_fd >= 0)
|
|
|
|
socket_close(netplay->listen_fd);
|
|
|
|
|
|
|
|
if (netplay->connections && netplay->connections[0].fd >= 0)
|
|
|
|
socket_close(netplay->connections[0].fd);
|
2012-01-21 13:00:11 +00:00
|
|
|
|
2015-04-11 00:49:30 +00:00
|
|
|
free(netplay);
|
2015-01-09 02:21:47 +00:00
|
|
|
return NULL;
|
2012-01-21 13:00:11 +00:00
|
|
|
}
|
|
|
|
|
2015-01-09 17:34:00 +00:00
|
|
|
/**
|
2016-12-15 13:42:03 +00:00
|
|
|
* netplay_free
|
2015-01-09 17:34:00 +00:00
|
|
|
* @netplay : pointer to netplay object
|
|
|
|
*
|
2016-12-15 13:42:03 +00:00
|
|
|
* Frees netplay data/
|
|
|
|
*/
|
2014-10-01 21:00:05 +00:00
|
|
|
void netplay_free(netplay_t *netplay)
|
2011-02-13 15:40:24 +00:00
|
|
|
{
|
2016-12-05 05:04:01 +00:00
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (netplay->listen_fd >= 0)
|
|
|
|
socket_close(netplay->listen_fd);
|
2015-01-09 17:34:00 +00:00
|
|
|
|
2016-12-05 05:04:01 +00:00
|
|
|
for (i = 0; i < netplay->connections_size; i++)
|
|
|
|
{
|
|
|
|
struct netplay_connection *connection = &netplay->connections[i];
|
|
|
|
if (connection->active)
|
2016-12-09 19:14:54 +00:00
|
|
|
{
|
2016-12-05 05:04:01 +00:00
|
|
|
socket_close(connection->fd);
|
2016-12-09 19:14:54 +00:00
|
|
|
netplay_deinit_socket_buffer(&connection->send_packet_buffer);
|
|
|
|
netplay_deinit_socket_buffer(&connection->recv_packet_buffer);
|
|
|
|
}
|
2016-12-05 05:04:01 +00:00
|
|
|
}
|
2011-02-14 15:10:53 +00:00
|
|
|
|
2016-12-05 05:04:01 +00:00
|
|
|
if (netplay->connections && netplay->connections != &netplay->one_connection)
|
|
|
|
free(netplay->connections);
|
|
|
|
|
2016-11-30 03:59:46 +00:00
|
|
|
if (netplay->nat_traversal)
|
|
|
|
natt_free(&netplay->nat_traversal_state);
|
|
|
|
|
2016-10-30 18:27:43 +00:00
|
|
|
if (netplay->buffer)
|
2012-01-11 18:22:18 +00:00
|
|
|
{
|
2014-10-01 21:00:05 +00:00
|
|
|
for (i = 0; i < netplay->buffer_size; i++)
|
2016-09-28 00:49:16 +00:00
|
|
|
if (netplay->buffer[i].state)
|
|
|
|
free(netplay->buffer[i].state);
|
2012-01-11 18:22:18 +00:00
|
|
|
|
2014-10-01 21:00:05 +00:00
|
|
|
free(netplay->buffer);
|
2012-01-11 18:22:18 +00:00
|
|
|
}
|
|
|
|
|
2016-10-30 18:27:43 +00:00
|
|
|
if (netplay->zbuffer)
|
|
|
|
free(netplay->zbuffer);
|
|
|
|
|
2016-11-26 21:06:52 +00:00
|
|
|
if (netplay->compression_stream)
|
|
|
|
netplay->compression_backend->stream_free(netplay->compression_stream);
|
|
|
|
|
2014-10-01 21:00:05 +00:00
|
|
|
if (netplay->addr)
|
2015-10-26 02:18:13 +00:00
|
|
|
freeaddrinfo_retro(netplay->addr);
|
2012-01-11 18:22:18 +00:00
|
|
|
|
2014-10-01 21:00:05 +00:00
|
|
|
free(netplay);
|
2011-02-13 15:40:24 +00:00
|
|
|
}
|