mirror of
https://github.com/libretro/RetroArch
synced 2025-02-06 00:39:53 +00:00
Move code out of retroarch.c - move it into
network/netplay/netplay_frontend.c
This commit is contained in:
parent
7c74e035b0
commit
db3f0a8468
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
*
|
||||
|
775
retroarch.c
775
retroarch.c
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user