mirror of
https://github.com/libretro/RetroArch
synced 2025-03-29 22:20:21 +00:00
Merge netplay_init.c into netplay_io.c
This commit is contained in:
parent
2a2f77f0dc
commit
0d549fd42b
@ -2080,7 +2080,6 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||
# Netplay
|
||||
DEFINES += -DHAVE_NETWORK_CMD
|
||||
OBJ += network/netplay/netplay_handshake.o \
|
||||
network/netplay/netplay_init.o \
|
||||
network/netplay/netplay_io.o \
|
||||
network/netplay/netplay_discovery.o \
|
||||
network/netplay/netplay_room_parse.o
|
||||
|
@ -1293,7 +1293,6 @@ NETPLAY
|
||||
============================================================ */
|
||||
#ifdef HAVE_NETWORKING
|
||||
#include "../network/netplay/netplay_handshake.c"
|
||||
#include "../network/netplay/netplay_init.c"
|
||||
#include "../network/netplay/netplay_io.c"
|
||||
#include "../network/netplay/netplay_discovery.c"
|
||||
#include "../network/netplay/netplay_room_parse.c"
|
||||
|
@ -1,585 +0,0 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2017 - Daniel De Matteis
|
||||
* Copyright (C) 2016-2017 - Gregor Richards
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* 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.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_XBOX)
|
||||
#pragma comment(lib, "ws2_32")
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <boolean.h>
|
||||
#include <compat/strl.h>
|
||||
|
||||
#include "netplay_private.h"
|
||||
|
||||
#include "netplay_discovery.h"
|
||||
|
||||
#include "../../autosave.h"
|
||||
#include "../../retroarch.h"
|
||||
|
||||
#if defined(AF_INET6) && !defined(HAVE_SOCKET_LEGACY) && !defined(_3DS)
|
||||
#define HAVE_INET6 1
|
||||
#endif
|
||||
|
||||
static int init_tcp_connection(const struct addrinfo *res,
|
||||
bool server,
|
||||
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;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
|
||||
#ifdef _WIN32
|
||||
(const char*)
|
||||
#else
|
||||
(const void*)
|
||||
#endif
|
||||
&flag,
|
||||
sizeof(int)) < 0)
|
||||
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
|
||||
{
|
||||
#if defined(HAVE_INET6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
|
||||
/* Make sure we accept connections on both IPv6 and IPv4 */
|
||||
int on = 0;
|
||||
if (res->ai_family == AF_INET6)
|
||||
{
|
||||
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(on)) < 0)
|
||||
RARCH_WARN("Failed to listen on both IPv6 and IPv4\n");
|
||||
}
|
||||
#endif
|
||||
if ( !socket_bind(fd, (void*)res) ||
|
||||
listen(fd, 1024) < 0)
|
||||
{
|
||||
ret = false;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
if (!ret && fd >= 0)
|
||||
{
|
||||
socket_close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static bool init_tcp_socket(netplay_t *netplay, void *direct_host,
|
||||
const char *server, uint16_t port)
|
||||
{
|
||||
char port_buf[16];
|
||||
bool ret = false;
|
||||
const struct addrinfo *tmp_info = NULL;
|
||||
struct addrinfo *res = NULL;
|
||||
struct addrinfo hints = {0};
|
||||
|
||||
port_buf[0] = '\0';
|
||||
|
||||
if (!direct_host)
|
||||
{
|
||||
#ifdef HAVE_INET6
|
||||
/* Default to hosting on IPv6 and IPv4 */
|
||||
if (!server)
|
||||
hints.ai_family = AF_INET6;
|
||||
#endif
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
if (!server)
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
snprintf(port_buf, sizeof(port_buf), "%hu", (unsigned short)port);
|
||||
if (getaddrinfo_retro(server, port_buf, &hints, &res) != 0)
|
||||
{
|
||||
#ifdef HAVE_INET6
|
||||
try_wildcard:
|
||||
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
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/* I'll build my own addrinfo! */
|
||||
struct netplay_host *host = (struct netplay_host *)direct_host;
|
||||
hints.ai_family = host->addr.sa_family;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = 0;
|
||||
hints.ai_addrlen = host->addrlen;
|
||||
hints.ai_addr = &host->addr;
|
||||
res = &hints;
|
||||
|
||||
}
|
||||
|
||||
/* If we're serving on IPv6, make sure we accept all connections, including
|
||||
* IPv4 */
|
||||
#ifdef HAVE_INET6
|
||||
if (!direct_host && !server && res->ai_family == AF_INET6)
|
||||
{
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) res->ai_addr;
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1200
|
||||
IN6ADDR_SETANY(sin6);
|
||||
#else
|
||||
sin6->sin6_addr = in6addr_any;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If "localhost" is used, it is important to check every possible
|
||||
* address for IPv4/IPv6. */
|
||||
tmp_info = res;
|
||||
|
||||
while (tmp_info)
|
||||
{
|
||||
struct sockaddr_storage sad = {0};
|
||||
int fd = init_tcp_connection(
|
||||
tmp_info,
|
||||
direct_host || server,
|
||||
(struct sockaddr*)&sad,
|
||||
sizeof(sad));
|
||||
|
||||
if (fd >= 0)
|
||||
{
|
||||
ret = true;
|
||||
if (direct_host || server)
|
||||
{
|
||||
netplay->connections[0].active = true;
|
||||
netplay->connections[0].fd = fd;
|
||||
netplay->connections[0].addr = sad;
|
||||
}
|
||||
else
|
||||
{
|
||||
netplay->listen_fd = fd;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
tmp_info = tmp_info->ai_next;
|
||||
}
|
||||
|
||||
if (res && !direct_host)
|
||||
freeaddrinfo_retro(res);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
#ifdef HAVE_INET6
|
||||
if (!direct_host && (hints.ai_family == AF_INET6))
|
||||
goto try_wildcard;
|
||||
#endif
|
||||
RARCH_ERR("Failed to set up netplay sockets.\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool init_socket(netplay_t *netplay, void *direct_host,
|
||||
const char *server, uint16_t port)
|
||||
{
|
||||
if (!network_init())
|
||||
return false;
|
||||
|
||||
if (!init_tcp_socket(netplay, direct_host, server, port))
|
||||
return false;
|
||||
|
||||
if (netplay->is_server && netplay->nat_traversal)
|
||||
netplay_init_nat_traversal(netplay);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool netplay_init_socket_buffers(netplay_t *netplay)
|
||||
{
|
||||
/* Make our packet buffer big enough for a save state and stall-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_MAX_STALL_FRAMES * 16;
|
||||
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;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_try_init_serialization
|
||||
*
|
||||
* Try to initialize serialization. For quirky cores.
|
||||
*
|
||||
* Returns true if serialization is now ready, false otherwise.
|
||||
*/
|
||||
bool netplay_try_init_serialization(netplay_t *netplay)
|
||||
{
|
||||
retro_ctx_serialize_info_t serial_info;
|
||||
|
||||
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->run_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);
|
||||
|
||||
return netplay_init_socket_buffers(netplay);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
bool netplay_wait_and_init_serialization(netplay_t *netplay)
|
||||
{
|
||||
int frame;
|
||||
|
||||
if (netplay->state_size)
|
||||
return true;
|
||||
|
||||
/* Wait a maximum of 60 frames */
|
||||
for (frame = 0; frame < 60; frame++)
|
||||
{
|
||||
if (netplay_try_init_serialization(netplay))
|
||||
return true;
|
||||
|
||||
#if defined(HAVE_THREADS)
|
||||
autosave_lock();
|
||||
#endif
|
||||
core_run();
|
||||
#if defined(HAVE_THREADS)
|
||||
autosave_unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool netplay_init_buffers(netplay_t *netplay)
|
||||
{
|
||||
struct delta_frame *delta_frames = NULL;
|
||||
|
||||
/* Enough to get ahead or behind by MAX_STALL_FRAMES frames, plus one for
|
||||
* other remote clients, plus one to send the stall message */
|
||||
netplay->buffer_size = NETPLAY_MAX_STALL_FRAMES + 2;
|
||||
|
||||
/* If we're the server, we need enough to get ahead AND behind by
|
||||
* MAX_STALL_FRAMES frame */
|
||||
if (netplay->is_server)
|
||||
netplay->buffer_size *= 2;
|
||||
|
||||
delta_frames = (struct delta_frame*)calloc(netplay->buffer_size,
|
||||
sizeof(*delta_frames));
|
||||
|
||||
if (!delta_frames)
|
||||
return false;
|
||||
|
||||
netplay->buffer = delta_frames;
|
||||
|
||||
if (!(netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_INITIALIZATION)))
|
||||
netplay_init_serialization(netplay);
|
||||
|
||||
return netplay_init_socket_buffers(netplay);
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_new:
|
||||
* @direct_host : Netplay host discovered from scanning.
|
||||
* @server : IP address of server.
|
||||
* @port : Port of server.
|
||||
* @stateless_mode : Shall we use stateless mode?
|
||||
* @check_frames : Frequency with which to check CRCs.
|
||||
* @cb : Libretro callbacks.
|
||||
* @nat_traversal : If true, attempt NAT traversal.
|
||||
* @nick : Nickname of user.
|
||||
* @quirks : Netplay quirks required for this session.
|
||||
*
|
||||
* Creates a new netplay handle. A NULL server means we're
|
||||
* hosting.
|
||||
*
|
||||
* Returns: new netplay data.
|
||||
*/
|
||||
netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port,
|
||||
bool stateless_mode, int check_frames,
|
||||
const struct retro_callbacks *cb, bool nat_traversal, const char *nick,
|
||||
uint64_t quirks)
|
||||
{
|
||||
netplay_t *netplay = (netplay_t*)calloc(1, sizeof(*netplay));
|
||||
if (!netplay)
|
||||
return NULL;
|
||||
|
||||
netplay->listen_fd = -1;
|
||||
netplay->tcp_port = port;
|
||||
netplay->cbs = *cb;
|
||||
netplay->is_server = (direct_host == NULL && server == NULL);
|
||||
netplay->is_connected = false;
|
||||
netplay->nat_traversal = netplay->is_server ? nat_traversal : false;
|
||||
netplay->stateless_mode = stateless_mode;
|
||||
netplay->check_frames = check_frames;
|
||||
netplay->crc_validity_checked = false;
|
||||
netplay->crcs_valid = true;
|
||||
netplay->quirks = quirks;
|
||||
netplay->self_mode = netplay->is_server ?
|
||||
NETPLAY_CONNECTION_SPECTATING :
|
||||
NETPLAY_CONNECTION_NONE;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
strlcpy(netplay->nick, nick[0]
|
||||
? nick : RARCH_DEFAULT_NICK,
|
||||
sizeof(netplay->nick));
|
||||
|
||||
if (!init_socket(netplay, direct_host, server, port))
|
||||
{
|
||||
free(netplay);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!netplay_init_buffers(netplay))
|
||||
{
|
||||
free(netplay);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (netplay->is_server)
|
||||
{
|
||||
/* Clients get device info from the server */
|
||||
unsigned i;
|
||||
for (i = 0; i < MAX_INPUT_DEVICES; i++)
|
||||
{
|
||||
uint32_t dtype = input_config_get_device(i);
|
||||
netplay->config_devices[i] = dtype;
|
||||
if ((dtype&RETRO_DEVICE_MASK) == RETRO_DEVICE_KEYBOARD)
|
||||
{
|
||||
netplay->have_updown_device = true;
|
||||
netplay_key_hton_init();
|
||||
}
|
||||
if (dtype != RETRO_DEVICE_NONE && !netplay_expected_input_size(netplay, 1<<i))
|
||||
RARCH_WARN("Netplay does not support input device %u\n", i+1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Start our handshake */
|
||||
netplay_handshake_init_send(netplay, &netplay->connections[0]);
|
||||
|
||||
netplay->connections[0].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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_free
|
||||
* @netplay : pointer to netplay object
|
||||
*
|
||||
* Frees netplay data/
|
||||
*/
|
||||
void netplay_free(netplay_t *netplay)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (netplay->listen_fd >= 0)
|
||||
socket_close(netplay->listen_fd);
|
||||
|
||||
for (i = 0; i < netplay->connections_size; i++)
|
||||
{
|
||||
struct netplay_connection *connection = &netplay->connections[i];
|
||||
if (connection->active)
|
||||
{
|
||||
socket_close(connection->fd);
|
||||
netplay_deinit_socket_buffer(&connection->send_packet_buffer);
|
||||
netplay_deinit_socket_buffer(&connection->recv_packet_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (netplay->connections && netplay->connections != &netplay->one_connection)
|
||||
free(netplay->connections);
|
||||
|
||||
if (netplay->nat_traversal)
|
||||
natt_free(&netplay->nat_traversal_state);
|
||||
|
||||
if (netplay->buffer)
|
||||
{
|
||||
for (i = 0; i < netplay->buffer_size; i++)
|
||||
netplay_delta_frame_free(&netplay->buffer[i]);
|
||||
|
||||
free(netplay->buffer);
|
||||
}
|
||||
|
||||
if (netplay->zbuffer)
|
||||
free(netplay->zbuffer);
|
||||
|
||||
if (netplay->compress_nil.compression_stream)
|
||||
{
|
||||
netplay->compress_nil.compression_backend->stream_free(netplay->compress_nil.compression_stream);
|
||||
netplay->compress_nil.decompression_backend->stream_free(netplay->compress_nil.decompression_stream);
|
||||
}
|
||||
if (netplay->compress_zlib.compression_stream)
|
||||
{
|
||||
netplay->compress_zlib.compression_backend->stream_free(netplay->compress_zlib.compression_stream);
|
||||
netplay->compress_zlib.decompression_backend->stream_free(netplay->compress_zlib.decompression_stream);
|
||||
}
|
||||
|
||||
if (netplay->addr)
|
||||
freeaddrinfo_retro(netplay->addr);
|
||||
|
||||
free(netplay);
|
||||
}
|
@ -15,6 +15,10 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_XBOX)
|
||||
#pragma comment(lib, "ws2_32")
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
@ -27,6 +31,7 @@
|
||||
#include <net/net_socket.h>
|
||||
|
||||
#include "netplay_private.h"
|
||||
#include "netplay_discovery.h"
|
||||
|
||||
#include "../../autosave.h"
|
||||
#include "../../configuration.h"
|
||||
@ -37,6 +42,10 @@
|
||||
|
||||
#include "../../input/input_driver.h"
|
||||
|
||||
#if defined(AF_INET6) && !defined(HAVE_SOCKET_LEGACY) && !defined(_3DS)
|
||||
#define HAVE_INET6 1
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DISCORD
|
||||
#include "../discord.h"
|
||||
|
||||
@ -139,11 +148,9 @@ bool netplay_delta_frame_ready(netplay_t *netplay, struct delta_frame *delta,
|
||||
*
|
||||
* Get the CRC for the serialization of this frame.
|
||||
*/
|
||||
uint32_t netplay_delta_frame_crc(netplay_t *netplay,
|
||||
static uint32_t netplay_delta_frame_crc(netplay_t *netplay,
|
||||
struct delta_frame *delta)
|
||||
{
|
||||
if (!netplay->state_size)
|
||||
return 0;
|
||||
return encoding_crc32(0L, (const unsigned char*)delta->state,
|
||||
netplay->state_size);
|
||||
}
|
||||
@ -204,7 +211,7 @@ netplay_input_state_t netplay_input_state_for(
|
||||
if (!ret->used && !must_not_create && ret->size == size)
|
||||
{
|
||||
ret->client_num = client_num;
|
||||
ret->used = true;
|
||||
ret->used = true;
|
||||
memset(ret->data, 0, size*sizeof(uint32_t));
|
||||
return ret;
|
||||
}
|
||||
@ -378,10 +385,12 @@ void netplay_deinit_socket_buffer(struct socket_buffer *sbuf)
|
||||
free(sbuf->data);
|
||||
}
|
||||
|
||||
void netplay_clear_socket_buffer(struct socket_buffer *sbuf)
|
||||
#if 0
|
||||
static void netplay_clear_socket_buffer(struct socket_buffer *sbuf)
|
||||
{
|
||||
sbuf->start = sbuf->read = sbuf->end = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* netplay_send
|
||||
@ -1117,14 +1126,20 @@ static void netplay_handle_frame_hash(netplay_t *netplay,
|
||||
if (netplay->check_frames &&
|
||||
delta->frame % abs(netplay->check_frames) == 0)
|
||||
{
|
||||
delta->crc = netplay_delta_frame_crc(netplay, delta);
|
||||
if (netplay->state_size)
|
||||
delta->crc = netplay_delta_frame_crc(netplay, delta);
|
||||
else
|
||||
delta->crc = 0;
|
||||
netplay_cmd_crc(netplay, delta);
|
||||
}
|
||||
}
|
||||
else if (delta->crc && netplay->crcs_valid)
|
||||
{
|
||||
/* We have a remote CRC, so check it */
|
||||
uint32_t local_crc = netplay_delta_frame_crc(netplay, delta);
|
||||
uint32_t local_crc = 0;
|
||||
if (netplay->state_size)
|
||||
local_crc = netplay_delta_frame_crc(netplay, delta);
|
||||
|
||||
if (local_crc != delta->crc)
|
||||
{
|
||||
/* If the very first check frame is wrong,
|
||||
@ -1508,7 +1523,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
||||
#ifdef DEBUG_NONDETERMINISTIC_CORES
|
||||
if (ptr->have_remote && netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->replay_ptr], netplay->replay_frame_count))
|
||||
{
|
||||
RARCH_LOG("PRE %u: %X\n", netplay->replay_frame_count-1, netplay_delta_frame_crc(netplay, ptr));
|
||||
RARCH_LOG("PRE %u: %X\n", netplay->replay_frame_count-1, netplay->state_size ? netplay_delta_frame_crc(netplay, ptr) : 0);
|
||||
if (netplay->is_server)
|
||||
RARCH_LOG("INP %X %X\n", ptr->real_input_state[0], ptr->self_state[0]);
|
||||
else
|
||||
@ -1517,7 +1532,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
||||
serial_info.data = ptr->state;
|
||||
memset(serial_info.data, 0, serial_info.size);
|
||||
core_serialize(&serial_info);
|
||||
RARCH_LOG("POST %u: %X\n", netplay->replay_frame_count-1, netplay_delta_frame_crc(netplay, ptr));
|
||||
RARCH_LOG("POST %u: %X\n", netplay->replay_frame_count-1, netplay->state_size ? netplay_delta_frame_crc(netplay, ptr) : 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -3158,8 +3173,10 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
{
|
||||
/* We've already replayed up to this frame, so we can check it
|
||||
* directly */
|
||||
uint32_t local_crc = netplay_delta_frame_crc(
|
||||
netplay, &netplay->buffer[tmp_ptr]);
|
||||
uint32_t local_crc = 0;
|
||||
if (netplay->state_size)
|
||||
local_crc = netplay_delta_frame_crc(
|
||||
netplay, &netplay->buffer[tmp_ptr]);
|
||||
|
||||
/* Problem! */
|
||||
if (buffer[1] != local_crc)
|
||||
@ -3670,3 +3687,550 @@ void netplay_init_nat_traversal(netplay_t *netplay)
|
||||
netplay->nat_traversal_task_oustanding = true;
|
||||
task_push_netplay_nat_traversal(&netplay->nat_traversal_state, netplay->tcp_port);
|
||||
}
|
||||
|
||||
static int init_tcp_connection(const struct addrinfo *res,
|
||||
bool server,
|
||||
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;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
|
||||
#ifdef _WIN32
|
||||
(const char*)
|
||||
#else
|
||||
(const void*)
|
||||
#endif
|
||||
&flag,
|
||||
sizeof(int)) < 0)
|
||||
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
|
||||
{
|
||||
#if defined(HAVE_INET6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
|
||||
/* Make sure we accept connections on both IPv6 and IPv4 */
|
||||
int on = 0;
|
||||
if (res->ai_family == AF_INET6)
|
||||
{
|
||||
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(on)) < 0)
|
||||
RARCH_WARN("Failed to listen on both IPv6 and IPv4\n");
|
||||
}
|
||||
#endif
|
||||
if ( !socket_bind(fd, (void*)res) ||
|
||||
listen(fd, 1024) < 0)
|
||||
{
|
||||
ret = false;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
if (!ret && fd >= 0)
|
||||
{
|
||||
socket_close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static bool init_tcp_socket(netplay_t *netplay, void *direct_host,
|
||||
const char *server, uint16_t port)
|
||||
{
|
||||
char port_buf[16];
|
||||
bool ret = false;
|
||||
const struct addrinfo *tmp_info = NULL;
|
||||
struct addrinfo *res = NULL;
|
||||
struct addrinfo hints = {0};
|
||||
|
||||
port_buf[0] = '\0';
|
||||
|
||||
if (!direct_host)
|
||||
{
|
||||
#ifdef HAVE_INET6
|
||||
/* Default to hosting on IPv6 and IPv4 */
|
||||
if (!server)
|
||||
hints.ai_family = AF_INET6;
|
||||
#endif
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
if (!server)
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
snprintf(port_buf, sizeof(port_buf), "%hu", (unsigned short)port);
|
||||
if (getaddrinfo_retro(server, port_buf, &hints, &res) != 0)
|
||||
{
|
||||
#ifdef HAVE_INET6
|
||||
try_wildcard:
|
||||
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
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/* I'll build my own addrinfo! */
|
||||
struct netplay_host *host = (struct netplay_host *)direct_host;
|
||||
hints.ai_family = host->addr.sa_family;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = 0;
|
||||
hints.ai_addrlen = host->addrlen;
|
||||
hints.ai_addr = &host->addr;
|
||||
res = &hints;
|
||||
|
||||
}
|
||||
|
||||
/* If we're serving on IPv6, make sure we accept all connections, including
|
||||
* IPv4 */
|
||||
#ifdef HAVE_INET6
|
||||
if (!direct_host && !server && res->ai_family == AF_INET6)
|
||||
{
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) res->ai_addr;
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1200
|
||||
IN6ADDR_SETANY(sin6);
|
||||
#else
|
||||
sin6->sin6_addr = in6addr_any;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If "localhost" is used, it is important to check every possible
|
||||
* address for IPv4/IPv6. */
|
||||
tmp_info = res;
|
||||
|
||||
while (tmp_info)
|
||||
{
|
||||
struct sockaddr_storage sad = {0};
|
||||
int fd = init_tcp_connection(
|
||||
tmp_info,
|
||||
direct_host || server,
|
||||
(struct sockaddr*)&sad,
|
||||
sizeof(sad));
|
||||
|
||||
if (fd >= 0)
|
||||
{
|
||||
ret = true;
|
||||
if (direct_host || server)
|
||||
{
|
||||
netplay->connections[0].active = true;
|
||||
netplay->connections[0].fd = fd;
|
||||
netplay->connections[0].addr = sad;
|
||||
}
|
||||
else
|
||||
{
|
||||
netplay->listen_fd = fd;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
tmp_info = tmp_info->ai_next;
|
||||
}
|
||||
|
||||
if (res && !direct_host)
|
||||
freeaddrinfo_retro(res);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
#ifdef HAVE_INET6
|
||||
if (!direct_host && (hints.ai_family == AF_INET6))
|
||||
goto try_wildcard;
|
||||
#endif
|
||||
RARCH_ERR("Failed to set up netplay sockets.\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool init_socket(netplay_t *netplay, void *direct_host,
|
||||
const char *server, uint16_t port)
|
||||
{
|
||||
if (!network_init())
|
||||
return false;
|
||||
|
||||
if (!init_tcp_socket(netplay, direct_host, server, port))
|
||||
return false;
|
||||
|
||||
if (netplay->is_server && netplay->nat_traversal)
|
||||
netplay_init_nat_traversal(netplay);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool netplay_init_socket_buffers(netplay_t *netplay)
|
||||
{
|
||||
/* Make our packet buffer big enough for a save state and stall-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_MAX_STALL_FRAMES * 16;
|
||||
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;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_try_init_serialization
|
||||
*
|
||||
* Try to initialize serialization. For quirky cores.
|
||||
*
|
||||
* Returns true if serialization is now ready, false otherwise.
|
||||
*/
|
||||
bool netplay_try_init_serialization(netplay_t *netplay)
|
||||
{
|
||||
retro_ctx_serialize_info_t serial_info;
|
||||
|
||||
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->run_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);
|
||||
|
||||
return netplay_init_socket_buffers(netplay);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
bool netplay_wait_and_init_serialization(netplay_t *netplay)
|
||||
{
|
||||
int frame;
|
||||
|
||||
if (netplay->state_size)
|
||||
return true;
|
||||
|
||||
/* Wait a maximum of 60 frames */
|
||||
for (frame = 0; frame < 60; frame++)
|
||||
{
|
||||
if (netplay_try_init_serialization(netplay))
|
||||
return true;
|
||||
|
||||
#if defined(HAVE_THREADS)
|
||||
autosave_lock();
|
||||
#endif
|
||||
core_run();
|
||||
#if defined(HAVE_THREADS)
|
||||
autosave_unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool netplay_init_buffers(netplay_t *netplay)
|
||||
{
|
||||
struct delta_frame *delta_frames = NULL;
|
||||
|
||||
/* Enough to get ahead or behind by MAX_STALL_FRAMES frames, plus one for
|
||||
* other remote clients, plus one to send the stall message */
|
||||
netplay->buffer_size = NETPLAY_MAX_STALL_FRAMES + 2;
|
||||
|
||||
/* If we're the server, we need enough to get ahead AND behind by
|
||||
* MAX_STALL_FRAMES frame */
|
||||
if (netplay->is_server)
|
||||
netplay->buffer_size *= 2;
|
||||
|
||||
delta_frames = (struct delta_frame*)calloc(netplay->buffer_size,
|
||||
sizeof(*delta_frames));
|
||||
|
||||
if (!delta_frames)
|
||||
return false;
|
||||
|
||||
netplay->buffer = delta_frames;
|
||||
|
||||
if (!(netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_INITIALIZATION)))
|
||||
netplay_init_serialization(netplay);
|
||||
|
||||
return netplay_init_socket_buffers(netplay);
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_new:
|
||||
* @direct_host : Netplay host discovered from scanning.
|
||||
* @server : IP address of server.
|
||||
* @port : Port of server.
|
||||
* @stateless_mode : Shall we use stateless mode?
|
||||
* @check_frames : Frequency with which to check CRCs.
|
||||
* @cb : Libretro callbacks.
|
||||
* @nat_traversal : If true, attempt NAT traversal.
|
||||
* @nick : Nickname of user.
|
||||
* @quirks : Netplay quirks required for this session.
|
||||
*
|
||||
* Creates a new netplay handle. A NULL server means we're
|
||||
* hosting.
|
||||
*
|
||||
* Returns: new netplay data.
|
||||
*/
|
||||
netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port,
|
||||
bool stateless_mode, int check_frames,
|
||||
const struct retro_callbacks *cb, bool nat_traversal, const char *nick,
|
||||
uint64_t quirks)
|
||||
{
|
||||
netplay_t *netplay = (netplay_t*)calloc(1, sizeof(*netplay));
|
||||
if (!netplay)
|
||||
return NULL;
|
||||
|
||||
netplay->listen_fd = -1;
|
||||
netplay->tcp_port = port;
|
||||
netplay->cbs = *cb;
|
||||
netplay->is_server = (direct_host == NULL && server == NULL);
|
||||
netplay->is_connected = false;
|
||||
netplay->nat_traversal = netplay->is_server ? nat_traversal : false;
|
||||
netplay->stateless_mode = stateless_mode;
|
||||
netplay->check_frames = check_frames;
|
||||
netplay->crc_validity_checked = false;
|
||||
netplay->crcs_valid = true;
|
||||
netplay->quirks = quirks;
|
||||
netplay->self_mode = netplay->is_server ?
|
||||
NETPLAY_CONNECTION_SPECTATING :
|
||||
NETPLAY_CONNECTION_NONE;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
strlcpy(netplay->nick, nick[0]
|
||||
? nick : RARCH_DEFAULT_NICK,
|
||||
sizeof(netplay->nick));
|
||||
|
||||
if (!init_socket(netplay, direct_host, server, port))
|
||||
{
|
||||
free(netplay);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!netplay_init_buffers(netplay))
|
||||
{
|
||||
free(netplay);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (netplay->is_server)
|
||||
{
|
||||
/* Clients get device info from the server */
|
||||
unsigned i;
|
||||
for (i = 0; i < MAX_INPUT_DEVICES; i++)
|
||||
{
|
||||
uint32_t dtype = input_config_get_device(i);
|
||||
netplay->config_devices[i] = dtype;
|
||||
if ((dtype&RETRO_DEVICE_MASK) == RETRO_DEVICE_KEYBOARD)
|
||||
{
|
||||
netplay->have_updown_device = true;
|
||||
netplay_key_hton_init();
|
||||
}
|
||||
if (dtype != RETRO_DEVICE_NONE && !netplay_expected_input_size(netplay, 1<<i))
|
||||
RARCH_WARN("Netplay does not support input device %u\n", i+1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Start our handshake */
|
||||
netplay_handshake_init_send(netplay, &netplay->connections[0]);
|
||||
|
||||
netplay->connections[0].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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_free
|
||||
* @netplay : pointer to netplay object
|
||||
*
|
||||
* Frees netplay data/
|
||||
*/
|
||||
void netplay_free(netplay_t *netplay)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (netplay->listen_fd >= 0)
|
||||
socket_close(netplay->listen_fd);
|
||||
|
||||
for (i = 0; i < netplay->connections_size; i++)
|
||||
{
|
||||
struct netplay_connection *connection = &netplay->connections[i];
|
||||
if (connection->active)
|
||||
{
|
||||
socket_close(connection->fd);
|
||||
netplay_deinit_socket_buffer(&connection->send_packet_buffer);
|
||||
netplay_deinit_socket_buffer(&connection->recv_packet_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (netplay->connections && netplay->connections != &netplay->one_connection)
|
||||
free(netplay->connections);
|
||||
|
||||
if (netplay->nat_traversal)
|
||||
natt_free(&netplay->nat_traversal_state);
|
||||
|
||||
if (netplay->buffer)
|
||||
{
|
||||
for (i = 0; i < netplay->buffer_size; i++)
|
||||
netplay_delta_frame_free(&netplay->buffer[i]);
|
||||
|
||||
free(netplay->buffer);
|
||||
}
|
||||
|
||||
if (netplay->zbuffer)
|
||||
free(netplay->zbuffer);
|
||||
|
||||
if (netplay->compress_nil.compression_stream)
|
||||
{
|
||||
netplay->compress_nil.compression_backend->stream_free(netplay->compress_nil.compression_stream);
|
||||
netplay->compress_nil.decompression_backend->stream_free(netplay->compress_nil.decompression_stream);
|
||||
}
|
||||
if (netplay->compress_zlib.compression_stream)
|
||||
{
|
||||
netplay->compress_zlib.compression_backend->stream_free(netplay->compress_zlib.compression_stream);
|
||||
netplay->compress_zlib.decompression_backend->stream_free(netplay->compress_zlib.decompression_stream);
|
||||
}
|
||||
|
||||
if (netplay->addr)
|
||||
freeaddrinfo_retro(netplay->addr);
|
||||
|
||||
free(netplay);
|
||||
}
|
||||
|
@ -665,13 +665,6 @@ void netplay_recv_flush(struct socket_buffer *sbuf);
|
||||
bool netplay_delta_frame_ready(netplay_t *netplay, struct delta_frame *delta,
|
||||
uint32_t frame);
|
||||
|
||||
/**
|
||||
* netplay_delta_frame_crc
|
||||
*
|
||||
* Get the CRC for the serialization of this frame.
|
||||
*/
|
||||
uint32_t netplay_delta_frame_crc(netplay_t *netplay, struct delta_frame *delta);
|
||||
|
||||
/**
|
||||
* netplay_delta_frame_free
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user