Move code out of retroarch.c - move it into

network/netplay/netplay_frontend.c
This commit is contained in:
twinaphex 2021-09-18 06:14:34 +02:00
parent 7c74e035b0
commit db3f0a8468
8 changed files with 2496 additions and 2537 deletions

View File

@ -2099,10 +2099,9 @@ ifeq ($(HAVE_NETWORKING), 1)
# Netplay
DEFINES += -DHAVE_NETWORK_CMD
OBJ += network/netplay/netplay_handshake.o \
network/netplay/netplay_io.o \
network/netplay/netplay_discovery.o \
network/netplay/netplay_room_parse.o
OBJ += \
network/netplay/netplay_frontend.o \
network/netplay/netplay_room_parse.o
# RetroAchievements
ifeq ($(HAVE_CHEEVOS), 1)

View File

@ -1290,9 +1290,7 @@ THREAD
NETPLAY
============================================================ */
#ifdef HAVE_NETWORKING
#include "../network/netplay/netplay_handshake.c"
#include "../network/netplay/netplay_io.c"
#include "../network/netplay/netplay_discovery.c"
#include "../network/netplay/netplay_frontend.c"
#include "../network/netplay/netplay_room_parse.c"
#include "../libretro-common/net/net_compat.c"
#include "../libretro-common/net/net_socket.c"

View File

@ -96,4 +96,82 @@ int netplay_rooms_get_count(void);
void netplay_rooms_free(void);
/**
* netplay_frontend_paused
* @netplay : pointer to netplay object
* @paused : true if frontend is paused
*
* Inform Netplay of the frontend's pause state (paused or otherwise)
*/
void netplay_frontend_paused(netplay_t *netplay, bool paused);
/**
* netplay_toggle_play_spectate
*
* Toggle between play mode and spectate mode
*/
void netplay_toggle_play_spectate(netplay_t *netplay);
/**
* netplay_load_savestate
* @netplay : pointer to netplay object
* @serial_info : the savestate being loaded, NULL means
* "load it yourself"
* @save : Whether to save the provided serial_info
* into the frame buffer
*
* Inform Netplay of a savestate load and send it to the other side
**/
void netplay_load_savestate(netplay_t *netplay,
retro_ctx_serialize_info_t *serial_info, bool save);
/**
* netplay_core_reset
* @netplay : pointer to netplay object
*
* Indicate that the core has been reset to netplay peers
**/
void netplay_core_reset(netplay_t *netplay);
int16_t netplay_input_state(netplay_t *netplay,
unsigned port, unsigned device,
unsigned idx, unsigned id);
/**
* netplay_poll:
* @netplay : pointer to netplay object
*
* Polls network to see if we have anything new. If our
* network buffer is full, we simply have to block
* for new input data.
*
* Returns: true (1) if successful, otherwise false (0).
**/
bool netplay_poll(
bool block_libretro_input,
void *settings_data,
netplay_t *netplay);
/**
* netplay_is_alive:
* @netplay : pointer to netplay object
*
* Checks if input port/index is controlled by netplay or not.
*
* Returns: true (1) if alive, otherwise false (0).
**/
bool netplay_is_alive(netplay_t *netplay);
/**
* netplay_should_skip:
* @netplay : pointer to netplay object
*
* If we're fast-forward replaying to resync, check if we
* should actually show frame.
*
* Returns: bool (1) if we should skip this frame, otherwise
* false (0).
**/
bool netplay_should_skip(netplay_t *netplay);
#endif

View File

@ -1,556 +0,0 @@
/* RetroArch - A frontend for libretro.
* 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/>.
*/
/*
* AD PACKET FORMAT:
*
* Request:
* 1 word: RANQ (RetroArch Netplay Query)
* 1 word: Netplay protocol version
*
* Reply:
* 1 word : RANS (RetroArch Netplay Server)
* 1 word : Netplay protocol version
* 1 word : Port
* 8 words: RetroArch version
* 8 words: Nick
* 8 words: Core name
* 8 words: Core version
* 8 words: Content name (currently always blank)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string/stdstring.h>
#include <file/file_path.h>
#include "../../file_path_special.h"
#include "../../paths.h"
#include "../../content.h"
#include <compat/strl.h>
#include <net/net_compat.h>
#include "../../retroarch.h"
#include "../../version.h"
#include "../../verbosity.h"
#include "netplay.h"
#include "netplay_discovery.h"
#include "netplay_private.h"
#if defined(AF_INET6) && !defined(HAVE_SOCKET_LEGACY) && !defined(_3DS)
#define HAVE_INET6 1
#endif
/* TODO/FIXME - globals referenced outside */
struct netplay_room *netplay_room_list = NULL;
int netplay_room_count = 0;
#ifdef HAVE_NETPLAYDISCOVERY
struct ad_packet
{
uint32_t header;
uint32_t protocol_version;
uint32_t port;
char address[NETPLAY_HOST_STR_LEN];
char retroarch_version[NETPLAY_HOST_STR_LEN];
char nick[NETPLAY_HOST_STR_LEN];
char frontend[NETPLAY_HOST_STR_LEN];
char core[NETPLAY_HOST_STR_LEN];
char core_version[NETPLAY_HOST_STR_LEN];
char content[NETPLAY_HOST_LONGSTR_LEN];
char content_crc[NETPLAY_HOST_STR_LEN];
char subsystem_name[NETPLAY_HOST_STR_LEN];
};
/* TODO/FIXME - static globals */
/* LAN discovery sockets */
static int lan_ad_server_fd = -1;
static int lan_ad_client_fd = -1;
/* Packet buffer for advertisement and responses */
static struct ad_packet ad_packet_buffer;
/* List of discovered hosts */
static struct netplay_host_list discovered_hosts;
static size_t discovered_hosts_allocated;
#ifdef HAVE_SOCKET_LEGACY
#ifndef htons
/* The fact that I need to write this is deeply depressing */
static int16_t htons_for_morons(int16_t value)
{
union {
int32_t l;
int16_t s[2];
} val;
val.l = htonl(value);
return val.s[1];
}
#define htons htons_for_morons
#endif
#endif
static bool netplay_lan_ad_client(void)
{
unsigned i;
fd_set fds;
socklen_t addr_size;
struct sockaddr their_addr;
struct timeval tmp_tv = {0};
if (lan_ad_client_fd < 0)
return false;
their_addr.sa_family = 0;
for (i = 0; i < 14; i++)
their_addr.sa_data[i] = 0;
/* Check for any ad queries */
for (;;)
{
FD_ZERO(&fds);
FD_SET(lan_ad_client_fd, &fds);
if (socket_select(lan_ad_client_fd + 1,
&fds, NULL, NULL, &tmp_tv) <= 0)
break;
if (!FD_ISSET(lan_ad_client_fd, &fds))
break;
/* Somebody queried, so check that it's valid */
addr_size = sizeof(their_addr);
if (recvfrom(lan_ad_client_fd, (char*)&ad_packet_buffer,
sizeof(struct ad_packet), 0, &their_addr, &addr_size) >=
(ssize_t) sizeof(struct ad_packet))
{
struct netplay_host *host = NULL;
/* Make sure it's a valid response */
if (memcmp((void *) &ad_packet_buffer, "RANS", 4))
continue;
/* For this version */
if (ntohl(ad_packet_buffer.protocol_version)
!= NETPLAY_PROTOCOL_VERSION)
continue;
/* And that we know how to handle it */
if (their_addr.sa_family == AF_INET)
{
struct sockaddr_in *sin = NULL;
RARCH_WARN ("[Discovery] Using IPv4 for discovery\n");
sin = (struct sockaddr_in *) &their_addr;
sin->sin_port = htons(ntohl(ad_packet_buffer.port));
}
#ifdef HAVE_INET6
else if (their_addr.sa_family == AF_INET6)
{
struct sockaddr_in6 *sin6 = NULL;
RARCH_WARN ("[Discovery] Using IPv6 for discovery\n");
sin6 = (struct sockaddr_in6 *) &their_addr;
sin6->sin6_port = htons(ad_packet_buffer.port);
}
#endif
else
continue;
/* Allocate space for it */
if (discovered_hosts.size >= discovered_hosts_allocated)
{
size_t allocated = discovered_hosts_allocated;
struct netplay_host *new_hosts = NULL;
if (allocated == 0)
allocated = 2;
else
allocated *= 2;
if (discovered_hosts.hosts)
new_hosts = (struct netplay_host *)
realloc(discovered_hosts.hosts, allocated * sizeof(struct
netplay_host));
else
/* Should be equivalent to realloc,
* but I don't trust screwy libcs */
new_hosts = (struct netplay_host *)
malloc(allocated * sizeof(struct netplay_host));
if (!new_hosts)
return false;
discovered_hosts.hosts = new_hosts;
discovered_hosts_allocated = allocated;
}
/* Get our host structure */
host = &discovered_hosts.hosts[discovered_hosts.size++];
/* Copy in the response */
memset(host, 0, sizeof(struct netplay_host));
host->addr = their_addr;
host->addrlen = addr_size;
host->port = ntohl(ad_packet_buffer.port);
strlcpy(host->address, ad_packet_buffer.address, NETPLAY_HOST_STR_LEN);
strlcpy(host->nick, ad_packet_buffer.nick, NETPLAY_HOST_STR_LEN);
strlcpy(host->core, ad_packet_buffer.core, NETPLAY_HOST_STR_LEN);
strlcpy(host->retroarch_version, ad_packet_buffer.retroarch_version,
NETPLAY_HOST_STR_LEN);
strlcpy(host->core_version, ad_packet_buffer.core_version,
NETPLAY_HOST_STR_LEN);
strlcpy(host->content, ad_packet_buffer.content,
NETPLAY_HOST_LONGSTR_LEN);
strlcpy(host->subsystem_name, ad_packet_buffer.subsystem_name,
NETPLAY_HOST_LONGSTR_LEN);
strlcpy(host->frontend, ad_packet_buffer.frontend,
NETPLAY_HOST_STR_LEN);
host->content_crc =
atoi(ad_packet_buffer.content_crc);
host->nick[NETPLAY_HOST_STR_LEN-1] =
host->core[NETPLAY_HOST_STR_LEN-1] =
host->core_version[NETPLAY_HOST_STR_LEN-1] =
host->content[NETPLAY_HOST_LONGSTR_LEN-1] = '\0';
}
}
return true;
}
/** Initialize Netplay discovery (client) */
bool init_netplay_discovery(void)
{
struct addrinfo *addr = NULL;
int fd = socket_init((void **)&addr, 0, NULL, SOCKET_TYPE_DATAGRAM);
if (fd < 0)
goto error;
if (!socket_bind(fd, (void*)addr))
{
socket_close(fd);
goto error;
}
lan_ad_client_fd = fd;
freeaddrinfo_retro(addr);
return true;
error:
if (addr)
freeaddrinfo_retro(addr);
RARCH_ERR("[Discovery] Failed to initialize netplay advertisement client socket.\n");
return false;
}
/** Deinitialize and free Netplay discovery */
/* TODO/FIXME - this is apparently never called? */
void deinit_netplay_discovery(void)
{
if (lan_ad_client_fd >= 0)
{
socket_close(lan_ad_client_fd);
lan_ad_client_fd = -1;
}
}
/** Discovery control */
/* TODO/FIXME: implement net_ifinfo and ntohs for consoles */
bool netplay_discovery_driver_ctl(
enum rarch_netplay_discovery_ctl_state state, void *data)
{
int ret;
char port_str[6];
unsigned k = 0;
if (lan_ad_client_fd < 0)
return false;
switch (state)
{
case RARCH_NETPLAY_DISCOVERY_CTL_LAN_SEND_QUERY:
{
net_ifinfo_t interfaces;
struct addrinfo hints = {0}, *addr;
if (!net_ifinfo_new(&interfaces))
return false;
/* Get the broadcast address (IPv4 only for now) */
snprintf(port_str, 6, "%hu", (unsigned short) RARCH_DEFAULT_PORT);
if (getaddrinfo_retro("255.255.255.255", port_str, &hints, &addr) < 0)
return false;
/* Make it broadcastable */
#if defined(SOL_SOCKET) && defined(SO_BROADCAST)
{
int can_broadcast = 1;
if (setsockopt(lan_ad_client_fd, SOL_SOCKET, SO_BROADCAST,
(const char *)&can_broadcast, sizeof(can_broadcast)) < 0)
RARCH_WARN("[Discovery] Failed to set netplay discovery port to broadcast\n");
}
#endif
/* Put together the request */
memcpy((void *) &ad_packet_buffer, "RANQ", 4);
ad_packet_buffer.protocol_version = htonl(NETPLAY_PROTOCOL_VERSION);
for (k = 0; k < (unsigned)interfaces.size; k++)
{
strlcpy(ad_packet_buffer.address, interfaces.entries[k].host,
NETPLAY_HOST_STR_LEN);
/* And send it off */
ret = (int)sendto(lan_ad_client_fd, (const char *) &ad_packet_buffer,
sizeof(struct ad_packet), 0, addr->ai_addr, addr->ai_addrlen);
if (ret < (ssize_t) (2*sizeof(uint32_t)))
RARCH_WARN("[Discovery] Failed to send netplay discovery query (error: %d)\n", errno);
}
freeaddrinfo_retro(addr);
net_ifinfo_free(&interfaces);
break;
}
case RARCH_NETPLAY_DISCOVERY_CTL_LAN_GET_RESPONSES:
if (!netplay_lan_ad_client())
return false;
*((struct netplay_host_list **) data) = &discovered_hosts;
break;
case RARCH_NETPLAY_DISCOVERY_CTL_LAN_CLEAR_RESPONSES:
discovered_hosts.size = 0;
break;
default:
return false;
}
return true;
}
static bool init_lan_ad_server_socket(netplay_t *netplay, uint16_t port)
{
struct addrinfo *addr = NULL;
int fd = socket_init((void **) &addr, port, NULL, SOCKET_TYPE_DATAGRAM);
if (fd < 0)
goto error;
if (!socket_bind(fd, (void*)addr))
{
socket_close(fd);
goto error;
}
lan_ad_server_fd = fd;
freeaddrinfo_retro(addr);
return true;
error:
if (addr)
freeaddrinfo_retro(addr);
return false;
}
/**
* netplay_lan_ad_server
*
* Respond to any LAN ad queries that the netplay server has received.
*/
bool netplay_lan_ad_server(netplay_t *netplay)
{
/* TODO/FIXME: implement net_ifinfo and ntohs for consoles */
fd_set fds;
int ret;
unsigned i;
char buf[4096];
net_ifinfo_t interfaces;
socklen_t addr_size;
char reply_addr[NETPLAY_HOST_STR_LEN], port_str[6];
struct sockaddr their_addr;
struct timeval tmp_tv = {0};
unsigned k = 0;
struct addrinfo *our_addr, hints = {0};
struct string_list *subsystem = path_get_subsystem_list();
interfaces.entries = NULL;
interfaces.size = 0;
their_addr.sa_family = 0;
for (i = 0; i < 14; i++)
their_addr.sa_data[i] = 0;
if (!net_ifinfo_new(&interfaces))
return false;
if ( (lan_ad_server_fd < 0)
&& !init_lan_ad_server_socket(netplay, RARCH_DEFAULT_PORT))
{
RARCH_ERR("[Discovery] Failed to initialize netplay advertisement socket\n");
return false;
}
/* Check for any ad queries */
for (;;)
{
FD_ZERO(&fds);
FD_SET(lan_ad_server_fd, &fds);
if (socket_select(lan_ad_server_fd + 1, &fds, NULL, NULL, &tmp_tv) <= 0)
break;
if (!FD_ISSET(lan_ad_server_fd, &fds))
break;
/* Somebody queried, so check that it's valid */
addr_size = sizeof(their_addr);
ret = (int)recvfrom(lan_ad_server_fd, (char*)&ad_packet_buffer,
sizeof(struct ad_packet), 0, &their_addr, &addr_size);
if (ret >= (ssize_t) (2 * sizeof(uint32_t)))
{
char s[NETPLAY_HOST_STR_LEN];
uint32_t content_crc = 0;
/* Make sure it's a valid query */
if (memcmp((void *) &ad_packet_buffer, "RANQ", 4))
{
RARCH_LOG("[Discovery] Invalid query\n");
continue;
}
/* For this version */
if (ntohl(ad_packet_buffer.protocol_version) !=
NETPLAY_PROTOCOL_VERSION)
{
RARCH_LOG("[Discovery] Invalid protocol version\n");
continue;
}
if (!string_is_empty(ad_packet_buffer.address))
strlcpy(reply_addr, ad_packet_buffer.address, NETPLAY_HOST_STR_LEN);
for (k = 0; k < interfaces.size; k++)
{
char *p;
char sub[NETPLAY_HOST_STR_LEN];
char frontend_architecture_tmp[32];
char frontend[256];
const frontend_ctx_driver_t *frontend_drv =
(const frontend_ctx_driver_t*)
frontend_driver_get_cpu_architecture_str(
frontend_architecture_tmp, sizeof(frontend_architecture_tmp));
snprintf(frontend, sizeof(frontend), "%s %s",
frontend_drv->ident, frontend_architecture_tmp);
p=strrchr(reply_addr,'.');
if (p)
{
strlcpy(sub, reply_addr, p - reply_addr + 1);
if (strstr(interfaces.entries[k].host, sub) &&
!strstr(interfaces.entries[k].host, "127.0.0.1"))
{
struct retro_system_info *info = runloop_get_libretro_system_info();
RARCH_LOG ("[Discovery] Query received on common interface: %s/%s (theirs / ours) \n",
reply_addr, interfaces.entries[k].host);
/* Now build our response */
buf[0] = '\0';
content_crc = content_get_crc();
memset(&ad_packet_buffer, 0, sizeof(struct ad_packet));
memcpy(&ad_packet_buffer, "RANS", 4);
if (subsystem)
{
unsigned i;
for (i = 0; i < subsystem->size; i++)
{
strlcat(buf, path_basename(subsystem->elems[i].data), NETPLAY_HOST_LONGSTR_LEN);
if (i < subsystem->size - 1)
strlcat(buf, "|", NETPLAY_HOST_LONGSTR_LEN);
}
strlcpy(ad_packet_buffer.content, buf,
NETPLAY_HOST_LONGSTR_LEN);
strlcpy(ad_packet_buffer.subsystem_name, path_get(RARCH_PATH_SUBSYSTEM),
NETPLAY_HOST_STR_LEN);
}
else
{
strlcpy(ad_packet_buffer.content, !string_is_empty(
path_basename(path_get(RARCH_PATH_BASENAME)))
? path_basename(path_get(RARCH_PATH_BASENAME)) : "N/A",
NETPLAY_HOST_LONGSTR_LEN);
strlcpy(ad_packet_buffer.subsystem_name, "N/A", NETPLAY_HOST_STR_LEN);
}
strlcpy(ad_packet_buffer.address, interfaces.entries[k].host,
NETPLAY_HOST_STR_LEN);
ad_packet_buffer.protocol_version =
htonl(NETPLAY_PROTOCOL_VERSION);
ad_packet_buffer.port = htonl(netplay->tcp_port);
strlcpy(ad_packet_buffer.retroarch_version, PACKAGE_VERSION,
NETPLAY_HOST_STR_LEN);
strlcpy(ad_packet_buffer.nick, netplay->nick, NETPLAY_HOST_STR_LEN);
strlcpy(ad_packet_buffer.frontend, frontend, NETPLAY_HOST_STR_LEN);
if (info)
{
strlcpy(ad_packet_buffer.core, info->library_name,
NETPLAY_HOST_STR_LEN);
strlcpy(ad_packet_buffer.core_version, info->library_version,
NETPLAY_HOST_STR_LEN);
}
snprintf(s, sizeof(s), "%d", content_crc);
strlcpy(ad_packet_buffer.content_crc, s,
NETPLAY_HOST_STR_LEN);
/* Build up the destination address*/
snprintf(port_str, 6, "%hu", ntohs(((struct sockaddr_in*)(&their_addr))->sin_port));
if (getaddrinfo_retro(reply_addr, port_str, &hints, &our_addr) < 0)
continue;
RARCH_LOG ("[Discovery] Sending reply to %s \n", reply_addr);
/* And send it */
sendto(lan_ad_server_fd, (const char*)&ad_packet_buffer,
sizeof(struct ad_packet), 0, our_addr->ai_addr, our_addr->ai_addrlen);
freeaddrinfo_retro(our_addr);
}
else
continue;
}
else
continue;
}
}
}
net_ifinfo_free(&interfaces);
return true;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -674,19 +674,6 @@ bool netplay_lan_ad_server(netplay_t *netplay);
* NETPLAY-FRONTEND.C
**************************************************************/
/**
* netplay_load_savestate
* @netplay : pointer to netplay object
* @serial_info : the savestate being loaded, NULL means
* "load it yourself"
* @save : Whether to save the provided serial_info
* into the frame buffer
*
* Inform Netplay of a savestate load and send it to the other side
**/
void netplay_load_savestate(netplay_t *netplay,
retro_ctx_serialize_info_t *serial_info, bool save);
/**
* input_poll_net
*

View File

@ -4641,192 +4641,6 @@ static void discord_init(
#endif
#ifdef HAVE_NETWORKING
/**
* netplay_is_alive:
* @netplay : pointer to netplay object
*
* Checks if input port/index is controlled by netplay or not.
*
* Returns: true (1) if alive, otherwise false (0).
**/
static bool netplay_is_alive(netplay_t *netplay)
{
return (netplay->is_server) ||
(!netplay->is_server &&
netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED);
}
/**
* netplay_should_skip:
* @netplay : pointer to netplay object
*
* If we're fast-forward replaying to resync, check if we
* should actually show frame.
*
* Returns: bool (1) if we should skip this frame, otherwise
* false (0).
**/
static bool netplay_should_skip(netplay_t *netplay)
{
if (!netplay)
return false;
return netplay->is_replay
&& (netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED);
}
/**
* get_self_input_state:
* @netplay : pointer to netplay object
*
* Grab our own input state and send this frame's input state (self and remote)
* over the network
*
* Returns: true (1) if successful, otherwise false (0).
*/
static bool get_self_input_state(
bool block_libretro_input,
netplay_t *netplay)
{
unsigned i;
struct delta_frame *ptr = &netplay->buffer[netplay->self_ptr];
netplay_input_state_t istate = NULL;
uint32_t devices, used_devices = 0, devi, dev_type, local_device;
if (!netplay_delta_frame_ready(netplay, ptr, netplay->self_frame_count))
return false;
/* We've already read this frame! */
if (ptr->have_local)
return true;
devices = netplay->self_devices;
used_devices = 0;
for (devi = 0; devi < MAX_INPUT_DEVICES; devi++)
{
if (!(devices & (1 << devi)))
continue;
/* Find an appropriate local device */
dev_type = netplay->config_devices[devi]&RETRO_DEVICE_MASK;
for (local_device = 0; local_device < MAX_INPUT_DEVICES; local_device++)
{
if (used_devices & (1 << local_device))
continue;
if ((netplay->config_devices[local_device]&RETRO_DEVICE_MASK) == dev_type)
break;
}
if (local_device == MAX_INPUT_DEVICES)
local_device = 0;
used_devices |= (1 << local_device);
istate = netplay_input_state_for(&ptr->real_input[devi],
/* If we're a slave, we write our own input to MAX_CLIENTS to keep it separate */
(netplay->self_mode==NETPLAY_CONNECTION_SLAVE)?MAX_CLIENTS:netplay->self_client_num,
netplay_expected_input_size(netplay, 1 << devi),
true, false);
if (!istate)
continue; /* FIXME: More severe? */
/* First frame we always give zero input since relying on
* input from first frame screws up when we use -F 0. */
if ( !block_libretro_input
&& netplay->self_frame_count > 0)
{
uint32_t *state = istate->data;
retro_input_state_t cb = netplay->cbs.state_cb;
unsigned dtype = netplay->config_devices[devi]&RETRO_DEVICE_MASK;
switch (dtype)
{
case RETRO_DEVICE_ANALOG:
for (i = 0; i < 2; i++)
{
int16_t tmp_x = cb(local_device,
RETRO_DEVICE_ANALOG, (unsigned)i, 0);
int16_t tmp_y = cb(local_device,
RETRO_DEVICE_ANALOG, (unsigned)i, 1);
state[1 + i] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16);
}
/* no break */
case RETRO_DEVICE_JOYPAD:
for (i = 0; i <= RETRO_DEVICE_ID_JOYPAD_R3; i++)
{
int16_t tmp = cb(local_device,
RETRO_DEVICE_JOYPAD, 0, (unsigned)i);
state[0] |= tmp ? 1 << i : 0;
}
break;
case RETRO_DEVICE_MOUSE:
case RETRO_DEVICE_LIGHTGUN:
{
int16_t tmp_x = cb(local_device, dtype, 0, 0);
int16_t tmp_y = cb(local_device, dtype, 0, 1);
state[1] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16);
for (i = 2;
i <= (unsigned)((dtype == RETRO_DEVICE_MOUSE) ?
RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN :
RETRO_DEVICE_ID_LIGHTGUN_START);
i++)
{
int16_t tmp = cb(local_device, dtype, 0,
(unsigned) i);
state[0] |= tmp ? 1 << i : 0;
}
break;
}
case RETRO_DEVICE_KEYBOARD:
{
unsigned key, word = 0, bit = 1;
for (key = 1; key < NETPLAY_KEY_LAST; key++)
{
state[word] |=
cb(local_device, RETRO_DEVICE_KEYBOARD, 0,
NETPLAY_KEY_NTOH(key)) ?
(UINT32_C(1) << bit) : 0;
bit++;
if (bit >= 32)
{
bit = 0;
word++;
if (word >= istate->size)
break;
}
}
break;
}
}
}
}
ptr->have_local = true;
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING)
{
ptr->have_real[netplay->self_client_num] = true;
netplay->read_ptr[netplay->self_client_num] = NEXT_PTR(netplay->self_ptr);
netplay->read_frame_count[netplay->self_client_num] = netplay->self_frame_count + 1;
}
/* And send this input to our peers */
for (i = 0; i < netplay->connections_size; i++)
{
struct netplay_connection *connection = &netplay->connections[i];
if (connection->active && connection->mode >= NETPLAY_CONNECTION_CONNECTED)
netplay_send_cur_input(netplay, &netplay->connections[i]);
}
/* Handle any delayed state changes */
if (netplay->is_server)
netplay_delayed_state_change(netplay);
return true;
}
static bool init_netplay_deferred(
struct rarch_state *p_rarch,
const char* server, unsigned port)
@ -4844,252 +4658,6 @@ static bool init_netplay_deferred(
return p_rarch->netplay_client_deferred;
}
/**
* netplay_poll:
* @netplay : pointer to netplay object
*
* Polls network to see if we have anything new. If our
* network buffer is full, we simply have to block
* for new input data.
*
* Returns: true (1) if successful, otherwise false (0).
**/
static bool netplay_poll(
bool block_libretro_input,
settings_t *settings,
netplay_t *netplay)
{
int res;
uint32_t client;
size_t i;
if (!get_self_input_state(block_libretro_input, netplay))
goto catastrophe;
/* If we're not connected, we're done */
if (netplay->self_mode == NETPLAY_CONNECTION_NONE)
return true;
/* Read Netplay input, block if we're configured to stall for input every
* frame */
netplay_update_unread_ptr(netplay);
if (netplay->stateless_mode &&
(netplay->connected_players>1) &&
netplay->unread_frame_count <= netplay->run_frame_count)
res = netplay_poll_net_input(netplay, true);
else
res = netplay_poll_net_input(netplay, false);
if (res == -1)
goto catastrophe;
/* Resolve and/or simulate the input if we don't have real input */
netplay_resolve_input(netplay, netplay->run_ptr, false);
/* Handle any slaves */
if (netplay->is_server && netplay->connected_slaves)
netplay_handle_slaves(netplay);
netplay_update_unread_ptr(netplay);
/* Figure out how many frames of input latency we should be using to hide
* network latency */
if (netplay->frame_run_time_avg || netplay->stateless_mode)
{
/* FIXME: Using fixed 60fps for this calculation */
unsigned frames_per_frame = netplay->frame_run_time_avg ?
(16666 / netplay->frame_run_time_avg) :
0;
unsigned frames_ahead = (netplay->run_frame_count > netplay->unread_frame_count) ?
(netplay->run_frame_count - netplay->unread_frame_count) :
0;
int input_latency_frames_min = settings->uints.netplay_input_latency_frames_min -
(settings->bools.run_ahead_enabled ? settings->uints.run_ahead_frames : 0);
int input_latency_frames_max = input_latency_frames_min + settings->uints.netplay_input_latency_frames_range;
/* Assume we need a couple frames worth of time to actually run the
* current frame */
if (frames_per_frame > 2)
frames_per_frame -= 2;
else
frames_per_frame = 0;
/* Shall we adjust our latency? */
if (netplay->stateless_mode)
{
/* In stateless mode, we adjust up if we're "close" and down if we
* have a lot of slack */
if (netplay->input_latency_frames < input_latency_frames_min ||
(netplay->unread_frame_count == netplay->run_frame_count + 1 &&
netplay->input_latency_frames < input_latency_frames_max))
netplay->input_latency_frames++;
else if (netplay->input_latency_frames > input_latency_frames_max ||
(netplay->unread_frame_count > netplay->run_frame_count + 2 &&
netplay->input_latency_frames > input_latency_frames_min))
netplay->input_latency_frames--;
}
else if (netplay->input_latency_frames < input_latency_frames_min ||
(frames_per_frame < frames_ahead &&
netplay->input_latency_frames < input_latency_frames_max))
{
/* We can't hide this much network latency with replay, so hide some
* with input latency */
netplay->input_latency_frames++;
}
else if (netplay->input_latency_frames > input_latency_frames_max ||
(frames_per_frame > frames_ahead + 2 &&
netplay->input_latency_frames > input_latency_frames_min))
{
/* We don't need this much latency (any more) */
netplay->input_latency_frames--;
}
}
/* If we're stalled, consider unstalling */
switch (netplay->stall)
{
case NETPLAY_STALL_RUNNING_FAST:
if (netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES - 2
> netplay->self_frame_count)
{
netplay->stall = NETPLAY_STALL_NONE;
for (i = 0; i < netplay->connections_size; i++)
{
struct netplay_connection *connection = &netplay->connections[i];
if (connection->active && connection->stall)
connection->stall = NETPLAY_STALL_NONE;
}
}
break;
case NETPLAY_STALL_SPECTATOR_WAIT:
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING || netplay->unread_frame_count > netplay->self_frame_count)
netplay->stall = NETPLAY_STALL_NONE;
break;
case NETPLAY_STALL_INPUT_LATENCY:
/* Just let it recalculate momentarily */
netplay->stall = NETPLAY_STALL_NONE;
break;
case NETPLAY_STALL_SERVER_REQUESTED:
/* See if the stall is done */
if (netplay->connections[0].stall_frame == 0)
{
/* Stop stalling! */
netplay->connections[0].stall = NETPLAY_STALL_NONE;
netplay->stall = NETPLAY_STALL_NONE;
}
else
netplay->connections[0].stall_frame--;
break;
case NETPLAY_STALL_NO_CONNECTION:
/* We certainly haven't fixed this */
break;
default: /* not stalling */
break;
}
/* If we're not stalled, consider stalling */
if (!netplay->stall)
{
/* Have we not read enough latency frames? */
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING &&
netplay->connected_players &&
netplay->run_frame_count + netplay->input_latency_frames > netplay->self_frame_count)
{
netplay->stall = NETPLAY_STALL_INPUT_LATENCY;
netplay->stall_time = 0;
}
/* Are we too far ahead? */
if (netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES
<= netplay->self_frame_count)
{
netplay->stall = NETPLAY_STALL_RUNNING_FAST;
netplay->stall_time = cpu_features_get_time_usec();
/* Figure out who to blame */
if (netplay->is_server)
{
for (client = 1; client < MAX_CLIENTS; client++)
{
struct netplay_connection *connection;
if (!(netplay->connected_players & (1 << client)))
continue;
if (netplay->read_frame_count[client] > netplay->unread_frame_count)
continue;
connection = &netplay->connections[client-1];
if (connection->active &&
connection->mode == NETPLAY_CONNECTION_PLAYING)
{
connection->stall = NETPLAY_STALL_RUNNING_FAST;
connection->stall_time = netplay->stall_time;
}
}
}
}
/* If we're a spectator, are we ahead at all? */
if (!netplay->is_server &&
(netplay->self_mode == NETPLAY_CONNECTION_SPECTATING ||
netplay->self_mode == NETPLAY_CONNECTION_SLAVE) &&
netplay->unread_frame_count <= netplay->self_frame_count)
{
netplay->stall = NETPLAY_STALL_SPECTATOR_WAIT;
netplay->stall_time = cpu_features_get_time_usec();
}
}
/* If we're stalling, consider disconnection */
if (netplay->stall && netplay->stall_time)
{
retro_time_t now = cpu_features_get_time_usec();
/* Don't stall out while they're paused */
if (netplay->remote_paused)
netplay->stall_time = now;
else if (now - netplay->stall_time >=
(netplay->is_server ? MAX_SERVER_STALL_TIME_USEC :
MAX_CLIENT_STALL_TIME_USEC))
{
/* Stalled out! */
if (netplay->is_server)
{
bool fixed = false;
for (i = 0; i < netplay->connections_size; i++)
{
struct netplay_connection *connection = &netplay->connections[i];
if (connection->active &&
connection->mode == NETPLAY_CONNECTION_PLAYING &&
connection->stall)
{
netplay_hangup(netplay, connection);
fixed = true;
}
}
if (fixed)
{
/* Not stalled now :) */
netplay->stall = NETPLAY_STALL_NONE;
return true;
}
}
else
goto catastrophe;
return false;
}
}
return true;
catastrophe:
for (i = 0; i < netplay->connections_size; i++)
netplay_hangup(netplay, &netplay->connections[i]);
return false;
}
/**
* input_poll_net
*
@ -5136,85 +4704,6 @@ static size_t audio_sample_batch_net(const int16_t *data, size_t frames)
return frames;
}
static int16_t netplay_input_state(netplay_t *netplay,
unsigned port, unsigned device,
unsigned idx, unsigned id)
{
struct delta_frame *delta;
netplay_input_state_t istate;
const uint32_t *curr_input_state = NULL;
size_t ptr =
netplay->is_replay
? netplay->replay_ptr
: netplay->run_ptr;
if (port >= MAX_INPUT_DEVICES)
return 0;
/* If the port doesn't seem to correspond to the device, "correct" it. This
* is common with devices that typically only have one instance, such as
* keyboards, mice and lightguns. */
if (device != RETRO_DEVICE_JOYPAD &&
(netplay->config_devices[port]&RETRO_DEVICE_MASK) != device)
{
for (port = 0; port < MAX_INPUT_DEVICES; port++)
{
if ((netplay->config_devices[port]&RETRO_DEVICE_MASK) == device)
break;
}
if (port == MAX_INPUT_DEVICES)
return 0;
}
delta = &netplay->buffer[ptr];
istate = delta->resolved_input[port];
if (!istate || !istate->used || istate->size == 0)
return 0;
curr_input_state = istate->data;
switch (device)
{
case RETRO_DEVICE_JOYPAD:
if (id == RETRO_DEVICE_ID_JOYPAD_MASK)
return curr_input_state[0];
return ((1 << id) & curr_input_state[0]) ? 1 : 0;
case RETRO_DEVICE_ANALOG:
if (istate->size == 3)
{
uint32_t state = curr_input_state[1 + idx];
return (int16_t)(uint16_t)(state >> (id * 16));
}
break;
case RETRO_DEVICE_MOUSE:
case RETRO_DEVICE_LIGHTGUN:
if (istate->size == 2)
{
if (id <= RETRO_DEVICE_ID_MOUSE_Y)
return (int16_t)(uint16_t)(curr_input_state[1] >> (id * 16));
return ((1 << id) & curr_input_state[0]) ? 1 : 0;
}
break;
case RETRO_DEVICE_KEYBOARD:
{
unsigned key = netplay_key_hton(id);
if (key != NETPLAY_KEY_UNKNOWN)
{
unsigned word = key / 32;
unsigned bit = key % 32;
if (word <= istate->size)
return ((UINT32_C(1) << bit) & curr_input_state[word]) ? 1 : 0;
}
}
break;
default:
break;
}
return 0;
}
static void netplay_announce_cb(retro_task_t *task,
void *task_data, void *user_data, const char *error)
{
@ -5486,57 +4975,6 @@ static int16_t input_state_net(unsigned port, unsigned device,
/* ^^^ Netplay polling callbacks */
/**
* netplay_frontend_paused
* @netplay : pointer to netplay object
* @paused : true if frontend is paused
*
* Inform Netplay of the frontend's pause state (paused or otherwise)
*/
static void netplay_frontend_paused(netplay_t *netplay, bool paused)
{
size_t i;
uint32_t paused_ct = 0;
netplay->local_paused = paused;
/* Communicating this is a bit odd: If exactly one other connection is
* paused, then we must tell them that we're unpaused, as from their
* perspective we are. If more than one other connection is paused, then our
* status as proxy means we are NOT unpaused to either of them. */
for (i = 0; i < netplay->connections_size; i++)
{
struct netplay_connection *connection = &netplay->connections[i];
if (connection->active && connection->paused)
paused_ct++;
}
if (paused_ct > 1)
return;
/* Send our unpaused status. Must send manually because we must immediately
* flush the buffer: If we're paused, we won't be polled. */
for (i = 0; i < netplay->connections_size; i++)
{
struct netplay_connection *connection = &netplay->connections[i];
if ( connection->active
&& connection->mode >= NETPLAY_CONNECTION_CONNECTED)
{
if (paused)
netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_PAUSE,
netplay->nick, NETPLAY_NICK_LEN);
else
netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_RESUME,
NULL, 0);
/* We're not going to be polled, so we need to
* flush this command now */
netplay_send_flush(&connection->send_packet_buffer,
connection->fd, true);
}
}
}
/**
* netplay_disconnect
* @netplay : pointer to netplay object
@ -5685,219 +5123,6 @@ static void netplay_post_frame(
netplay_disconnect(p_rarch, netplay);
}
/**
* netplay_force_future
* @netplay : pointer to netplay object
*
* Force netplay to ignore all past input, typically because we've just loaded
* a state or reset.
*/
static void netplay_force_future(netplay_t *netplay)
{
/* Wherever we're inputting, that's where we consider our state to be loaded */
netplay->run_ptr = netplay->self_ptr;
netplay->run_frame_count = netplay->self_frame_count;
/* We need to ignore any intervening data from the other side,
* and never rewind past this */
netplay_update_unread_ptr(netplay);
if (netplay->unread_frame_count < netplay->run_frame_count)
{
uint32_t client;
for (client = 0; client < MAX_CLIENTS; client++)
{
if (!(netplay->connected_players & (1 << client)))
continue;
if (netplay->read_frame_count[client] < netplay->run_frame_count)
{
netplay->read_ptr[client] = netplay->run_ptr;
netplay->read_frame_count[client] = netplay->run_frame_count;
}
}
if (netplay->server_frame_count < netplay->run_frame_count)
{
netplay->server_ptr = netplay->run_ptr;
netplay->server_frame_count = netplay->run_frame_count;
}
netplay_update_unread_ptr(netplay);
}
if (netplay->other_frame_count < netplay->run_frame_count)
{
netplay->other_ptr = netplay->run_ptr;
netplay->other_frame_count = netplay->run_frame_count;
}
}
/**
* netplay_send_savestate
* @netplay : pointer to netplay object
* @serial_info : the savestate being loaded
* @cx : compression type
* @z : compression backend to use
*
* Send a loaded savestate to those connected peers using the given compression
* scheme.
*/
static void netplay_send_savestate(netplay_t *netplay,
retro_ctx_serialize_info_t *serial_info, uint32_t cx,
struct compression_transcoder *z)
{
uint32_t header[4];
uint32_t rd, wn;
size_t i;
/* Compress it */
z->compression_backend->set_in(z->compression_stream,
(const uint8_t*)serial_info->data_const, (uint32_t)serial_info->size);
z->compression_backend->set_out(z->compression_stream,
netplay->zbuffer, (uint32_t)netplay->zbuffer_size);
if (!z->compression_backend->trans(z->compression_stream, true, &rd,
&wn, NULL))
{
/* Catastrophe! */
for (i = 0; i < netplay->connections_size; i++)
netplay_hangup(netplay, &netplay->connections[i]);
return;
}
/* Send it to relevant peers */
header[0] = htonl(NETPLAY_CMD_LOAD_SAVESTATE);
header[1] = htonl(wn + 2*sizeof(uint32_t));
header[2] = htonl(netplay->run_frame_count);
header[3] = htonl(serial_info->size);
for (i = 0; i < netplay->connections_size; i++)
{
struct netplay_connection *connection = &netplay->connections[i];
if (!connection->active ||
connection->mode < NETPLAY_CONNECTION_CONNECTED ||
connection->compression_supported != cx) continue;
if (!netplay_send(&connection->send_packet_buffer, connection->fd, header,
sizeof(header)) ||
!netplay_send(&connection->send_packet_buffer, connection->fd,
netplay->zbuffer, wn))
netplay_hangup(netplay, connection);
}
}
/**
* netplay_load_savestate
* @netplay : pointer to netplay object
* @serial_info : the savestate being loaded, NULL means
* "load it yourself"
* @save : Whether to save the provided serial_info
* into the frame buffer
*
* Inform Netplay of a savestate load and send it to the other side
**/
void netplay_load_savestate(netplay_t *netplay,
retro_ctx_serialize_info_t *serial_info, bool save)
{
retro_ctx_serialize_info_t tmp_serial_info;
netplay_force_future(netplay);
/* Record it in our own buffer */
if (save || !serial_info)
{
/* TODO/FIXME: This is a critical failure! */
if (!netplay_delta_frame_ready(netplay,
&netplay->buffer[netplay->run_ptr], netplay->run_frame_count))
return;
if (!serial_info)
{
tmp_serial_info.size = netplay->state_size;
tmp_serial_info.data = netplay->buffer[netplay->run_ptr].state;
if (!core_serialize(&tmp_serial_info))
return;
tmp_serial_info.data_const = tmp_serial_info.data;
serial_info = &tmp_serial_info;
}
else
{
if (serial_info->size <= netplay->state_size)
memcpy(netplay->buffer[netplay->run_ptr].state,
serial_info->data_const, serial_info->size);
}
}
/* Don't send it if we're expected to be desynced */
if (netplay->desync)
return;
/* If we can't send it to the peer, loading a state was a bad idea */
if (netplay->quirks & (
NETPLAY_QUIRK_NO_SAVESTATES
| NETPLAY_QUIRK_NO_TRANSMISSION))
return;
/* Send this to every peer */
if (netplay->compress_nil.compression_backend)
netplay_send_savestate(netplay, serial_info, 0, &netplay->compress_nil);
if (netplay->compress_zlib.compression_backend)
netplay_send_savestate(netplay, serial_info, NETPLAY_COMPRESSION_ZLIB,
&netplay->compress_zlib);
}
/**
* netplay_core_reset
* @netplay : pointer to netplay object
*
* Indicate that the core has been reset to netplay peers
**/
static void netplay_core_reset(netplay_t *netplay)
{
size_t i;
uint32_t cmd[3];
/* Ignore past input */
netplay_force_future(netplay);
/* Request that our peers reset */
cmd[0] = htonl(NETPLAY_CMD_RESET);
cmd[1] = htonl(sizeof(uint32_t));
cmd[2] = htonl(netplay->self_frame_count);
for (i = 0; i < netplay->connections_size; i++)
{
struct netplay_connection *connection = &netplay->connections[i];
if (!connection->active ||
connection->mode < NETPLAY_CONNECTION_CONNECTED) continue;
if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd,
sizeof(cmd)))
netplay_hangup(netplay, connection);
}
}
/**
* netplay_toggle_play_spectate
*
* Toggle between play mode and spectate mode
*/
static void netplay_toggle_play_spectate(netplay_t *netplay)
{
switch (netplay->self_mode)
{
case NETPLAY_CONNECTION_PLAYING:
case NETPLAY_CONNECTION_SLAVE:
/* Switch to spectator mode immediately */
netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
netplay_cmd_mode(netplay, NETPLAY_CONNECTION_SPECTATING);
break;
case NETPLAY_CONNECTION_SPECTATING:
/* Switch only after getting permission */
netplay_cmd_mode(netplay, NETPLAY_CONNECTION_PLAYING);
break;
default:
break;
}
}
static void deinit_netplay(struct rarch_state *p_rarch)
{
if (p_rarch->netplay_data)