commit
bb499b70be
|
@ -1086,6 +1086,7 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||||
OBJ += $(LIBRETRO_COMM_DIR)/net/net_compat.o \
|
OBJ += $(LIBRETRO_COMM_DIR)/net/net_compat.o \
|
||||||
$(LIBRETRO_COMM_DIR)/net/net_http.o \
|
$(LIBRETRO_COMM_DIR)/net/net_http.o \
|
||||||
$(LIBRETRO_COMM_DIR)/net/net_socket.o \
|
$(LIBRETRO_COMM_DIR)/net/net_socket.o \
|
||||||
|
$(LIBRETRO_COMM_DIR)/net/net_natt.o \
|
||||||
network/net_http_special.o \
|
network/net_http_special.o \
|
||||||
tasks/task_http.o
|
tasks/task_http.o
|
||||||
|
|
||||||
|
@ -1123,6 +1124,10 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||||
OBJ += input/input_remote.o \
|
OBJ += input/input_remote.o \
|
||||||
cores/libretro-net-retropad/net_retropad_core.o
|
cores/libretro-net-retropad/net_retropad_core.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(HAVE_MINIUPNPC), 1)
|
||||||
|
LIBS += -lminiupnpc
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(findstring Win32,$(OS)),)
|
ifneq ($(findstring Win32,$(OS)),)
|
||||||
|
|
|
@ -800,6 +800,7 @@ static int populate_settings_bool(settings_t *settings, struct config_bool_setti
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_NETWORKING
|
#ifdef HAVE_NETWORKING
|
||||||
SETTING_BOOL("netplay_spectator_mode_enable",&settings->netplay.is_spectate, false, false /* TODO */, false);
|
SETTING_BOOL("netplay_spectator_mode_enable",&settings->netplay.is_spectate, false, false /* TODO */, false);
|
||||||
|
SETTING_BOOL("netplay_nat_traversal", &settings->netplay.nat_traversal, true, true, false);
|
||||||
#endif
|
#endif
|
||||||
SETTING_BOOL("block_sram_overwrite", &settings->block_sram_overwrite, true, block_sram_overwrite, false);
|
SETTING_BOOL("block_sram_overwrite", &settings->block_sram_overwrite, true, block_sram_overwrite, false);
|
||||||
SETTING_BOOL("savestate_auto_index", &settings->savestate_auto_index, true, savestate_auto_index, false);
|
SETTING_BOOL("savestate_auto_index", &settings->savestate_auto_index, true, savestate_auto_index, false);
|
||||||
|
@ -922,7 +923,7 @@ static int populate_settings_int(settings_t *settings, struct config_int_setting
|
||||||
#ifdef HAVE_NETWORKING
|
#ifdef HAVE_NETWORKING
|
||||||
SETTING_INT("netplay_ip_port", &settings->netplay.port, false, 0 /* TODO */, false);
|
SETTING_INT("netplay_ip_port", &settings->netplay.port, false, 0 /* TODO */, false);
|
||||||
SETTING_INT("netplay_delay_frames", &settings->netplay.sync_frames, true, 16, false);
|
SETTING_INT("netplay_delay_frames", &settings->netplay.sync_frames, true, 16, false);
|
||||||
SETTING_INT("netplay_check_frames", &settings->netplay.check_frames, false, 30, false);
|
SETTING_INT("netplay_check_frames", &settings->netplay.check_frames, true, 30, false);
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_LANGEXTRA
|
#ifdef HAVE_LANGEXTRA
|
||||||
SETTING_INT("user_language", &settings->user_language, true, RETRO_LANGUAGE_ENGLISH, false);
|
SETTING_INT("user_language", &settings->user_language, true, RETRO_LANGUAGE_ENGLISH, false);
|
||||||
|
|
|
@ -403,6 +403,7 @@ typedef struct settings
|
||||||
unsigned check_frames;
|
unsigned check_frames;
|
||||||
bool is_spectate;
|
bool is_spectate;
|
||||||
bool swap_input;
|
bool swap_input;
|
||||||
|
bool nat_traversal;
|
||||||
} netplay;
|
} netplay;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -879,6 +879,7 @@ NETPLAY
|
||||||
#include "../libretro-common/net/net_compat.c"
|
#include "../libretro-common/net/net_compat.c"
|
||||||
#include "../libretro-common/net/net_socket.c"
|
#include "../libretro-common/net/net_socket.c"
|
||||||
#include "../libretro-common/net/net_http.c"
|
#include "../libretro-common/net/net_http.c"
|
||||||
|
#include "../libretro-common/net/net_natt.c"
|
||||||
#ifndef HAVE_SOCKET_LEGACY
|
#ifndef HAVE_SOCKET_LEGACY
|
||||||
#include "../libretro-common/net/net_ifinfo.c"
|
#include "../libretro-common/net/net_ifinfo.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1558,6 +1558,12 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len)
|
||||||
"no checks. This value is only used on the \n"
|
"no checks. This value is only used on the \n"
|
||||||
"netplay host. \n");
|
"netplay host. \n");
|
||||||
break;
|
break;
|
||||||
|
case MENU_ENUM_LABEL_NETPLAY_NAT_TRAVERSAL:
|
||||||
|
snprintf(s, len,
|
||||||
|
"When hosting, attempt to listen for\n"
|
||||||
|
"connections from the public internet, using\n"
|
||||||
|
"UPnP or similar technologies to escape LANs. \n");
|
||||||
|
break;
|
||||||
case MENU_ENUM_LABEL_VIDEO_MAX_SWAPCHAIN_IMAGES:
|
case MENU_ENUM_LABEL_VIDEO_MAX_SWAPCHAIN_IMAGES:
|
||||||
snprintf(s, len,
|
snprintf(s, len,
|
||||||
"Maximum amount of swapchain images. This \n"
|
"Maximum amount of swapchain images. This \n"
|
||||||
|
|
|
@ -22,6 +22,10 @@ MSG_HASH(
|
||||||
MSG_GOT_CONNECTION_FROM,
|
MSG_GOT_CONNECTION_FROM,
|
||||||
"Got connection from"
|
"Got connection from"
|
||||||
)
|
)
|
||||||
|
MSG_HASH(
|
||||||
|
MSG_PUBLIC_ADDRESS,
|
||||||
|
"Public address"
|
||||||
|
)
|
||||||
MSG_HASH(
|
MSG_HASH(
|
||||||
MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN,
|
MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN,
|
||||||
"No arguments supplied and no menu builtin, displaying help..."
|
"No arguments supplied and no menu builtin, displaying help..."
|
||||||
|
@ -922,6 +926,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SPECTATOR_MODE_ENABLE,
|
||||||
"Netplay Spectator Enable")
|
"Netplay Spectator Enable")
|
||||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_TCP_UDP_PORT,
|
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_TCP_UDP_PORT,
|
||||||
"Netplay TCP/UDP Port")
|
"Netplay TCP/UDP Port")
|
||||||
|
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_NAT_TRAVERSAL,
|
||||||
|
"Netplay NAT Traversal")
|
||||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_CMD_ENABLE,
|
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_CMD_ENABLE,
|
||||||
"Network Commands")
|
"Network Commands")
|
||||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_CMD_PORT,
|
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_CMD_PORT,
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
/* Copyright (C) 2016 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (net_natt.h).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge,
|
||||||
|
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LIBRETRO_SDK_NET_NATT_H
|
||||||
|
#define _LIBRETRO_SDK_NET_NATT_H
|
||||||
|
|
||||||
|
#include <net/net_compat.h>
|
||||||
|
#include <net/net_socket.h>
|
||||||
|
|
||||||
|
struct natt_status {
|
||||||
|
/** nfds for select when checking for input */
|
||||||
|
int nfds;
|
||||||
|
|
||||||
|
/** The fdset to be selected upon to check for responses */
|
||||||
|
fd_set fds;
|
||||||
|
|
||||||
|
/** True if there might be a request outstanding */
|
||||||
|
bool request_outstanding;
|
||||||
|
|
||||||
|
/** True if we've resolved an external IPv4 address */
|
||||||
|
bool have_inet4;
|
||||||
|
|
||||||
|
/** External IPv4 address */
|
||||||
|
struct sockaddr_in ext_inet4_addr;
|
||||||
|
|
||||||
|
/** True if we've resolved an external IPv6 address */
|
||||||
|
bool have_inet6;
|
||||||
|
|
||||||
|
#ifdef AF_INET6
|
||||||
|
/** External IPv6 address */
|
||||||
|
struct sockaddr_in6 ext_inet6_addr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Internal status (currently unused) */
|
||||||
|
void *internal;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize global NAT traversal structures (must be called once to use other
|
||||||
|
* functions) */
|
||||||
|
void natt_init(void);
|
||||||
|
|
||||||
|
/** Initialize a NAT traversal status object */
|
||||||
|
bool natt_new(struct natt_status *status);
|
||||||
|
|
||||||
|
/** Free a NAT traversal status object */
|
||||||
|
void natt_free(struct natt_status *status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a port forwarding request. This may finish immediately or just send a
|
||||||
|
* request to the network. */
|
||||||
|
bool natt_open_port(struct natt_status *status, struct sockaddr *addr,
|
||||||
|
socklen_t addrlen, enum socket_protocol proto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a port forwarding request when only the port is known. Forwards any
|
||||||
|
* address it can find. */
|
||||||
|
bool natt_open_port_any(struct natt_status *status, uint16_t port,
|
||||||
|
enum socket_protocol proto);
|
||||||
|
|
||||||
|
/** Check for port forwarding responses */
|
||||||
|
bool natt_read(struct natt_status *status);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,221 @@
|
||||||
|
/* Copyright (C) 2016 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (net_natt.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge,
|
||||||
|
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <net/net_compat.h>
|
||||||
|
#include <net/net_ifinfo.h>
|
||||||
|
#include <retro_miscellaneous.h>
|
||||||
|
|
||||||
|
#include <net/net_natt.h>
|
||||||
|
|
||||||
|
#if HAVE_MINIUPNPC
|
||||||
|
#include <miniupnpc/miniwget.h>
|
||||||
|
#include <miniupnpc/miniupnpc.h>
|
||||||
|
#include <miniupnpc/upnpcommands.h>
|
||||||
|
|
||||||
|
static struct UPNPUrls urls;
|
||||||
|
static struct IGDdatas data;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void natt_init(void)
|
||||||
|
{
|
||||||
|
#if HAVE_MINIUPNPC
|
||||||
|
struct UPNPDev * devlist;
|
||||||
|
struct UPNPDev * dev;
|
||||||
|
char * descXML;
|
||||||
|
int descXMLsize = 0;
|
||||||
|
int upnperror = 0;
|
||||||
|
memset(&urls, 0, sizeof(struct UPNPUrls));
|
||||||
|
memset(&data, 0, sizeof(struct IGDdatas));
|
||||||
|
devlist = upnpDiscover(2000, NULL, NULL, 0, 0, &upnperror);
|
||||||
|
if (devlist)
|
||||||
|
{
|
||||||
|
dev = devlist;
|
||||||
|
while (dev)
|
||||||
|
{
|
||||||
|
if (strstr (dev->st, "InternetGatewayDevice"))
|
||||||
|
break;
|
||||||
|
dev = dev->pNext;
|
||||||
|
}
|
||||||
|
if (!dev)
|
||||||
|
dev = devlist;
|
||||||
|
|
||||||
|
descXML = (char *) miniwget(dev->descURL, &descXMLsize, 0);
|
||||||
|
if (descXML)
|
||||||
|
{
|
||||||
|
parserootdesc (descXML, descXMLsize, &data);
|
||||||
|
free (descXML); descXML = 0;
|
||||||
|
GetUPNPUrls (&urls, &data, dev->descURL, 0);
|
||||||
|
}
|
||||||
|
freeUPNPDevlist(devlist);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool natt_new(struct natt_status *status)
|
||||||
|
{
|
||||||
|
memset(status, 0, sizeof(struct natt_status));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void natt_free(struct natt_status *status)
|
||||||
|
{
|
||||||
|
/* Nothing */
|
||||||
|
}
|
||||||
|
|
||||||
|
bool natt_open_port(struct natt_status *status, struct sockaddr *addr, socklen_t addrlen, enum socket_protocol proto)
|
||||||
|
{
|
||||||
|
#if HAVE_MINIUPNPC
|
||||||
|
char host[PATH_MAX_LENGTH], ext_host[PATH_MAX_LENGTH],
|
||||||
|
port_str[6], ext_port_str[6];
|
||||||
|
const char *proto_str;
|
||||||
|
struct addrinfo hints = {0}, *ext_addrinfo;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* if NAT traversal is uninitialized or unavailable, oh well */
|
||||||
|
if (!urls.controlURL[0])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* figure out the internal info */
|
||||||
|
if (getnameinfo(addr, addrlen, host, PATH_MAX_LENGTH, port_str, 6, NI_NUMERICHOST|NI_NUMERICSERV) != 0)
|
||||||
|
return false;
|
||||||
|
proto_str = (proto == SOCKET_PROTOCOL_UDP) ? "UDP" : "TCP";
|
||||||
|
|
||||||
|
/* add the port mapping */
|
||||||
|
r = UPNP_AddAnyPortMapping(urls.controlURL, data.first.servicetype, port_str,
|
||||||
|
port_str, host, "retroarch", proto_str, NULL, "3600", ext_port_str);
|
||||||
|
if (r == 501 /* Action Failed */)
|
||||||
|
{
|
||||||
|
/* try the older AddPortMapping */
|
||||||
|
memcpy(ext_port_str, port_str, 6);
|
||||||
|
r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port_str,
|
||||||
|
port_str, host, "retroarch", proto_str, NULL, "3600");
|
||||||
|
}
|
||||||
|
if (r != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* get the external IP */
|
||||||
|
r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, ext_host);
|
||||||
|
if (r != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* update the status */
|
||||||
|
if (getaddrinfo_retro(ext_host, ext_port_str, &hints, &ext_addrinfo) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ext_addrinfo->ai_family == AF_INET &&
|
||||||
|
ext_addrinfo->ai_addrlen >= sizeof(struct sockaddr_in))
|
||||||
|
{
|
||||||
|
status->have_inet4 = true;
|
||||||
|
status->ext_inet4_addr = *((struct sockaddr_in *) ext_addrinfo->ai_addr);
|
||||||
|
}
|
||||||
|
#ifdef AF_INET6
|
||||||
|
else if (ext_addrinfo->ai_family == AF_INET6 &&
|
||||||
|
ext_addrinfo->ai_addrlen >= sizeof(struct sockaddr_in6))
|
||||||
|
{
|
||||||
|
status->have_inet6 = true;
|
||||||
|
status->ext_inet6_addr = *((struct sockaddr_in6 *) ext_addrinfo->ai_addr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
freeaddrinfo_retro(ext_addrinfo);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool natt_open_port_any(struct natt_status *status, uint16_t port, enum socket_protocol proto)
|
||||||
|
{
|
||||||
|
struct net_ifinfo *list;
|
||||||
|
bool ret = false;
|
||||||
|
size_t i;
|
||||||
|
struct addrinfo hints = {0}, *addr;
|
||||||
|
char port_str[6];
|
||||||
|
|
||||||
|
sprintf(port_str, "%hu", port);
|
||||||
|
|
||||||
|
/* get our interfaces */
|
||||||
|
if ((list = (struct net_ifinfo *) calloc(1, sizeof(struct net_ifinfo))) == NULL)
|
||||||
|
return false;
|
||||||
|
if (!net_ifinfo_new(list))
|
||||||
|
{
|
||||||
|
free(list);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loop through them */
|
||||||
|
for (i = 0; i < list->size; i++)
|
||||||
|
{
|
||||||
|
struct net_ifinfo_entry *entry = list->entries + i;
|
||||||
|
|
||||||
|
/* ignore localhost */
|
||||||
|
if (!strcmp(entry->host, "127.0.0.1") || !strcmp(entry->host, "::1"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* make a request for this host */
|
||||||
|
if (getaddrinfo_retro(entry->host, port_str, &hints, &addr) == 0)
|
||||||
|
{
|
||||||
|
ret = natt_open_port(status, addr->ai_addr, addr->ai_addrlen, proto) || ret;
|
||||||
|
freeaddrinfo_retro(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This really shouldn't free list, but does */
|
||||||
|
net_ifinfo_free(list);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool natt_read(struct natt_status *status)
|
||||||
|
{
|
||||||
|
/* MiniUPNPC is always synchronous, so there's nothing to read here.
|
||||||
|
* Reserved for future backends. */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* If we want to remove redirects in the future, this is a sample of how to do
|
||||||
|
* that */
|
||||||
|
void upnp_rem_redir (int port)
|
||||||
|
{
|
||||||
|
char port_str[16];
|
||||||
|
int t;
|
||||||
|
printf("TB : upnp_rem_redir (%d)\n", port);
|
||||||
|
if(urls.controlURL[0] == '\0')
|
||||||
|
{
|
||||||
|
printf("TB : the init was not done !\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sprintf(port_str, "%d", port);
|
||||||
|
UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port_str, "TCP", NULL);
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -4942,6 +4942,10 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data)
|
||||||
MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT,
|
MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT,
|
||||||
PARSE_ONLY_BOOL, false) != -1)
|
PARSE_ONLY_BOOL, false) != -1)
|
||||||
count++;
|
count++;
|
||||||
|
if (menu_displaylist_parse_settings_enum(menu, info,
|
||||||
|
MENU_ENUM_LABEL_NETPLAY_NAT_TRAVERSAL,
|
||||||
|
PARSE_ONLY_BOOL, false) != -1)
|
||||||
|
count++;
|
||||||
if (menu_displaylist_parse_settings_enum(menu, info,
|
if (menu_displaylist_parse_settings_enum(menu, info,
|
||||||
MENU_ENUM_LABEL_NETWORK_CMD_ENABLE,
|
MENU_ENUM_LABEL_NETWORK_CMD_ENABLE,
|
||||||
PARSE_ONLY_BOOL, false) != -1)
|
PARSE_ONLY_BOOL, false) != -1)
|
||||||
|
|
|
@ -5589,6 +5589,21 @@ static bool setting_append_list(
|
||||||
general_read_handler,
|
general_read_handler,
|
||||||
SD_FLAG_NONE);
|
SD_FLAG_NONE);
|
||||||
|
|
||||||
|
CONFIG_BOOL(
|
||||||
|
list, list_info,
|
||||||
|
&settings->netplay.is_spectate,
|
||||||
|
MENU_ENUM_LABEL_NETPLAY_NAT_TRAVERSAL,
|
||||||
|
MENU_ENUM_LABEL_VALUE_NETPLAY_NAT_TRAVERSAL,
|
||||||
|
false,
|
||||||
|
MENU_ENUM_LABEL_VALUE_OFF,
|
||||||
|
MENU_ENUM_LABEL_VALUE_ON,
|
||||||
|
&group_info,
|
||||||
|
&subgroup_info,
|
||||||
|
parent_group,
|
||||||
|
general_write_handler,
|
||||||
|
general_read_handler,
|
||||||
|
SD_FLAG_NONE);
|
||||||
|
|
||||||
CONFIG_BOOL(
|
CONFIG_BOOL(
|
||||||
list, list_info,
|
list, list_info,
|
||||||
&settings->netplay.swap_input,
|
&settings->netplay.swap_input,
|
||||||
|
|
|
@ -173,6 +173,7 @@ enum msg_hash_enums
|
||||||
MSG_NO_STATE_HAS_BEEN_LOADED_YET,
|
MSG_NO_STATE_HAS_BEEN_LOADED_YET,
|
||||||
MSG_GOT_CONNECTION_FROM,
|
MSG_GOT_CONNECTION_FROM,
|
||||||
MSG_CONNECTION_SLOT,
|
MSG_CONNECTION_SLOT,
|
||||||
|
MSG_PUBLIC_ADDRESS,
|
||||||
MSG_NO_SAVE_STATE_HAS_BEEN_OVERWRITTEN_YET,
|
MSG_NO_SAVE_STATE_HAS_BEEN_OVERWRITTEN_YET,
|
||||||
MSG_CANNOT_INFER_NEW_CONFIG_PATH,
|
MSG_CANNOT_INFER_NEW_CONFIG_PATH,
|
||||||
MSG_UNDID_LOAD_STATE,
|
MSG_UNDID_LOAD_STATE,
|
||||||
|
@ -975,6 +976,7 @@ enum msg_hash_enums
|
||||||
MENU_LABEL(NETPLAY_CHECK_FRAMES),
|
MENU_LABEL(NETPLAY_CHECK_FRAMES),
|
||||||
MENU_LABEL(NETPLAY_SPECTATOR_MODE_ENABLE),
|
MENU_LABEL(NETPLAY_SPECTATOR_MODE_ENABLE),
|
||||||
MENU_LABEL(NETPLAY_TCP_UDP_PORT),
|
MENU_LABEL(NETPLAY_TCP_UDP_PORT),
|
||||||
|
MENU_LABEL(NETPLAY_NAT_TRAVERSAL),
|
||||||
MENU_LABEL(SORT_SAVEFILES_ENABLE),
|
MENU_LABEL(SORT_SAVEFILES_ENABLE),
|
||||||
MENU_LABEL(SORT_SAVESTATES_ENABLE),
|
MENU_LABEL(SORT_SAVESTATES_ENABLE),
|
||||||
MENU_LABEL(NETPLAY_IP_ADDRESS),
|
MENU_LABEL(NETPLAY_IP_ADDRESS),
|
||||||
|
|
|
@ -63,6 +63,8 @@ static netplay_t *netplay_data = NULL;
|
||||||
/* Used to avoid recursive netplay calls */
|
/* Used to avoid recursive netplay calls */
|
||||||
static bool in_netplay = false;
|
static bool in_netplay = false;
|
||||||
|
|
||||||
|
static void announce_nat_traversal(netplay_t *netplay);
|
||||||
|
|
||||||
static int init_tcp_connection(const struct addrinfo *res,
|
static int init_tcp_connection(const struct addrinfo *res,
|
||||||
bool server, bool spectate,
|
bool server, bool spectate,
|
||||||
struct sockaddr *other_addr, socklen_t addr_size)
|
struct sockaddr *other_addr, socklen_t addr_size)
|
||||||
|
@ -186,6 +188,22 @@ static bool init_tcp_socket(netplay_t *netplay, const char *server,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void init_nat_traversal(netplay_t *netplay)
|
||||||
|
{
|
||||||
|
natt_init();
|
||||||
|
|
||||||
|
if (!natt_new(&netplay->nat_traversal_state))
|
||||||
|
{
|
||||||
|
netplay->nat_traversal = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
natt_open_port_any(&netplay->nat_traversal_state, netplay->tcp_port, SOCKET_PROTOCOL_TCP);
|
||||||
|
|
||||||
|
if (!netplay->nat_traversal_state.request_outstanding)
|
||||||
|
announce_nat_traversal(netplay);
|
||||||
|
}
|
||||||
|
|
||||||
static bool init_ad_socket(netplay_t *netplay, uint16_t port)
|
static bool init_ad_socket(netplay_t *netplay, uint16_t port)
|
||||||
{
|
{
|
||||||
int fd = socket_init((void**)&netplay->addr, port, NULL, SOCKET_TYPE_DATAGRAM);
|
int fd = socket_init((void**)&netplay->addr, port, NULL, SOCKET_TYPE_DATAGRAM);
|
||||||
|
@ -216,6 +234,9 @@ static bool init_socket(netplay_t *netplay, const char *server, uint16_t port)
|
||||||
if (!init_tcp_socket(netplay, server, port, netplay->spectate.enabled))
|
if (!init_tcp_socket(netplay, server, port, netplay->spectate.enabled))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (netplay->is_server && netplay->nat_traversal)
|
||||||
|
init_nat_traversal(netplay);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1061,6 +1082,36 @@ void netplay_log_connection(const struct sockaddr_storage *their_addr,
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void announce_nat_traversal(netplay_t *netplay)
|
||||||
|
{
|
||||||
|
char msg[512], host[PATH_MAX_LENGTH], port[6];
|
||||||
|
|
||||||
|
if (netplay->nat_traversal_state.have_inet4)
|
||||||
|
{
|
||||||
|
if (getnameinfo((const struct sockaddr *) &netplay->nat_traversal_state.ext_inet4_addr,
|
||||||
|
sizeof(struct sockaddr_in),
|
||||||
|
host, PATH_MAX_LENGTH, port, 6, NI_NUMERICHOST|NI_NUMERICSERV) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
#ifdef AF_INET6
|
||||||
|
else if (netplay->nat_traversal_state.have_inet6)
|
||||||
|
{
|
||||||
|
if (getnameinfo((const struct sockaddr *) &netplay->nat_traversal_state.ext_inet6_addr,
|
||||||
|
sizeof(struct sockaddr_in6),
|
||||||
|
host, PATH_MAX_LENGTH, port, 6, NI_NUMERICHOST|NI_NUMERICSERV) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else return;
|
||||||
|
|
||||||
|
snprintf(msg, sizeof(msg), "%s: %s:%s\n",
|
||||||
|
msg_hash_to_str(MSG_PUBLIC_ADDRESS),
|
||||||
|
host, port);
|
||||||
|
runloop_msg_queue_push(msg, 1, 180, false);
|
||||||
|
RARCH_LOG("%s\n", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool netplay_try_init_serialization(netplay_t *netplay)
|
bool netplay_try_init_serialization(netplay_t *netplay)
|
||||||
|
@ -1180,6 +1231,7 @@ static bool netplay_init_buffers(netplay_t *netplay, unsigned frames)
|
||||||
* @check_frames : Frequency with which to check CRCs.
|
* @check_frames : Frequency with which to check CRCs.
|
||||||
* @cb : Libretro callbacks.
|
* @cb : Libretro callbacks.
|
||||||
* @spectate : If true, enable spectator mode.
|
* @spectate : If true, enable spectator mode.
|
||||||
|
* @nat_traversal : If true, attempt NAT traversal.
|
||||||
* @nick : Nickname of user.
|
* @nick : Nickname of user.
|
||||||
* @quirks : Netplay quirks required for this session.
|
* @quirks : Netplay quirks required for this session.
|
||||||
*
|
*
|
||||||
|
@ -1188,10 +1240,9 @@ static bool netplay_init_buffers(netplay_t *netplay, unsigned frames)
|
||||||
*
|
*
|
||||||
* Returns: new netplay handle.
|
* Returns: new netplay handle.
|
||||||
**/
|
**/
|
||||||
netplay_t *netplay_new(const char *server, uint16_t port,
|
netplay_t *netplay_new(const char *server, uint16_t port, unsigned frames,
|
||||||
unsigned frames, unsigned check_frames,
|
unsigned check_frames, const struct retro_callbacks *cb, bool spectate,
|
||||||
const struct retro_callbacks *cb,
|
bool nat_traversal, const char *nick, uint64_t quirks)
|
||||||
bool spectate, const char *nick, uint64_t quirks)
|
|
||||||
{
|
{
|
||||||
netplay_t *netplay = (netplay_t*)calloc(1, sizeof(*netplay));
|
netplay_t *netplay = (netplay_t*)calloc(1, sizeof(*netplay));
|
||||||
if (!netplay)
|
if (!netplay)
|
||||||
|
@ -1203,6 +1254,7 @@ netplay_t *netplay_new(const char *server, uint16_t port,
|
||||||
netplay->port = server ? 0 : 1;
|
netplay->port = server ? 0 : 1;
|
||||||
netplay->spectate.enabled = spectate;
|
netplay->spectate.enabled = spectate;
|
||||||
netplay->is_server = server == NULL;
|
netplay->is_server = server == NULL;
|
||||||
|
netplay->nat_traversal = netplay->is_server ? nat_traversal : false;
|
||||||
netplay->stall_frames = frames;
|
netplay->stall_frames = frames;
|
||||||
netplay->check_frames = check_frames;
|
netplay->check_frames = check_frames;
|
||||||
netplay->quirks = quirks;
|
netplay->quirks = quirks;
|
||||||
|
@ -1338,6 +1390,9 @@ void netplay_free(netplay_t *netplay)
|
||||||
free(netplay->spectate.input);
|
free(netplay->spectate.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (netplay->nat_traversal)
|
||||||
|
natt_free(&netplay->nat_traversal_state);
|
||||||
|
|
||||||
if (netplay->buffer)
|
if (netplay->buffer)
|
||||||
{
|
{
|
||||||
for (i = 0; i < netplay->buffer_size; i++)
|
for (i = 0; i < netplay->buffer_size; i++)
|
||||||
|
@ -1383,11 +1438,26 @@ bool netplay_pre_frame(netplay_t *netplay)
|
||||||
netplay_try_init_serialization(netplay);
|
netplay_try_init_serialization(netplay);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Advertise our server if applicable */
|
|
||||||
if (netplay->is_server)
|
if (netplay->is_server)
|
||||||
{
|
{
|
||||||
|
/* Advertise our server if applicable */
|
||||||
if (netplay_ad_fd >= 0 || init_ad_socket(netplay, RARCH_DEFAULT_PORT))
|
if (netplay_ad_fd >= 0 || init_ad_socket(netplay, RARCH_DEFAULT_PORT))
|
||||||
netplay_ad_server(netplay, netplay_ad_fd);
|
netplay_ad_server(netplay, netplay_ad_fd);
|
||||||
|
|
||||||
|
/* NAT traversal if applicable */
|
||||||
|
if (netplay->nat_traversal &&
|
||||||
|
netplay->nat_traversal_state.request_outstanding &&
|
||||||
|
!netplay->nat_traversal_state.have_inet4)
|
||||||
|
{
|
||||||
|
struct timeval tmptv = {0};
|
||||||
|
fd_set fds = netplay->nat_traversal_state.fds;
|
||||||
|
if (socket_select(netplay->nat_traversal_state.nfds, &fds, NULL, NULL, &tmptv) > 0)
|
||||||
|
natt_read(&netplay->nat_traversal_state);
|
||||||
|
|
||||||
|
if (!netplay->nat_traversal_state.request_outstanding ||
|
||||||
|
netplay->nat_traversal_state.have_inet4)
|
||||||
|
announce_nat_traversal(netplay);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!netplay->net_cbs->pre_frame(netplay))
|
if (!netplay->net_cbs->pre_frame(netplay))
|
||||||
|
@ -1613,7 +1683,8 @@ bool init_netplay(bool is_spectate, const char *server, unsigned port)
|
||||||
netplay_is_client ? server : NULL,
|
netplay_is_client ? server : NULL,
|
||||||
port ? port : RARCH_DEFAULT_PORT,
|
port ? port : RARCH_DEFAULT_PORT,
|
||||||
settings->netplay.sync_frames, settings->netplay.check_frames, &cbs,
|
settings->netplay.sync_frames, settings->netplay.check_frames, &cbs,
|
||||||
is_spectate, settings->username, quirks);
|
is_spectate, settings->netplay.nat_traversal, settings->username,
|
||||||
|
quirks);
|
||||||
|
|
||||||
if (netplay_data)
|
if (netplay_data)
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -137,6 +137,7 @@ size_t audio_sample_batch_net(const int16_t *data, size_t frames);
|
||||||
* @check_frames : Frequency with which to check CRCs.
|
* @check_frames : Frequency with which to check CRCs.
|
||||||
* @cb : Libretro callbacks.
|
* @cb : Libretro callbacks.
|
||||||
* @spectate : If true, enable spectator mode.
|
* @spectate : If true, enable spectator mode.
|
||||||
|
* @nat_traversal : If true, attempt NAT traversal.
|
||||||
* @nick : Nickname of user.
|
* @nick : Nickname of user.
|
||||||
* @quirks : Netplay quirks.
|
* @quirks : Netplay quirks.
|
||||||
*
|
*
|
||||||
|
@ -147,7 +148,7 @@ size_t audio_sample_batch_net(const int16_t *data, size_t frames);
|
||||||
**/
|
**/
|
||||||
netplay_t *netplay_new(const char *server,
|
netplay_t *netplay_new(const char *server,
|
||||||
uint16_t port, unsigned frames, unsigned check_frames,
|
uint16_t port, unsigned frames, unsigned check_frames,
|
||||||
const struct retro_callbacks *cb, bool spectate,
|
const struct retro_callbacks *cb, bool spectate, bool nat_traversal,
|
||||||
const char *nick, uint64_t quirks);
|
const char *nick, uint64_t quirks);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include <net/net_compat.h>
|
#include <net/net_compat.h>
|
||||||
#include <net/net_socket.h>
|
#include <net/net_socket.h>
|
||||||
|
#include <net/net_natt.h>
|
||||||
|
|
||||||
#include "netplay_private.h"
|
#include "netplay_private.h"
|
||||||
|
|
||||||
|
@ -337,6 +338,19 @@ static bool netplay_net_info_cb(netplay_t* netplay, unsigned frames)
|
||||||
netplay->has_connection = true;
|
netplay->has_connection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
struct natt_status status;
|
||||||
|
natt_init();
|
||||||
|
if (natt_new(&status) && natt_open_port_any(&status, netplay->tcp_port, SOCKET_PROTOCOL_TCP))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Forwarded to %d!\n", status.ext_inet4_addr.sin_port);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Forwarding failed :(\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "netplay.h"
|
#include "netplay.h"
|
||||||
|
|
||||||
#include <net/net_compat.h>
|
#include <net/net_compat.h>
|
||||||
|
#include <net/net_natt.h>
|
||||||
#include <features/features_cpu.h>
|
#include <features/features_cpu.h>
|
||||||
#include <streams/trans_stream.h>
|
#include <streams/trans_stream.h>
|
||||||
#include <retro_endianness.h>
|
#include <retro_endianness.h>
|
||||||
|
@ -124,6 +125,9 @@ struct netplay
|
||||||
int fd;
|
int fd;
|
||||||
/* TCP port (if serving) */
|
/* TCP port (if serving) */
|
||||||
uint16_t tcp_port;
|
uint16_t tcp_port;
|
||||||
|
/* NAT traversal info (if NAT traversal is used and serving) */
|
||||||
|
bool nat_traversal;
|
||||||
|
struct natt_status nat_traversal_state;
|
||||||
/* Which port is governed by netplay (other user)? */
|
/* Which port is governed by netplay (other user)? */
|
||||||
unsigned port;
|
unsigned port;
|
||||||
bool has_connection;
|
bool has_connection;
|
||||||
|
|
|
@ -182,6 +182,10 @@ if [ "$HAVE_NETWORKING" = 'yes' ]; then
|
||||||
fi
|
fi
|
||||||
HAVE_NETWORK_CMD=yes
|
HAVE_NETWORK_CMD=yes
|
||||||
HAVE_NETWORKGAMEPAD=yes
|
HAVE_NETWORKGAMEPAD=yes
|
||||||
|
|
||||||
|
if [ "$HAVE_MINIUPNPC" != "no" ]; then
|
||||||
|
check_lib MINIUPNPC "-lminiupnpc"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "Warning: All networking features have been disabled."
|
echo "Warning: All networking features have been disabled."
|
||||||
HAVE_NETWORK_CMD='no'
|
HAVE_NETWORK_CMD='no'
|
||||||
|
|
|
@ -28,6 +28,7 @@ HAVE_DYLIB=auto # Dynamic loading support
|
||||||
HAVE_NETWORKING=auto # Networking features (recommended)
|
HAVE_NETWORKING=auto # Networking features (recommended)
|
||||||
HAVE_NETWORKGAMEPAD=auto # Networked game pad (plus baked-in core)
|
HAVE_NETWORKGAMEPAD=auto # Networked game pad (plus baked-in core)
|
||||||
C89_NETWORKGAMEPAD=no
|
C89_NETWORKGAMEPAD=no
|
||||||
|
HAVE_MINIUPNPC=auto # Mini UPnP client library (for NAT traversal)
|
||||||
HAVE_D3D9=yes # Direct3D 9 support
|
HAVE_D3D9=yes # Direct3D 9 support
|
||||||
HAVE_OPENGL=auto # OpenGL support
|
HAVE_OPENGL=auto # OpenGL support
|
||||||
HAVE_MALI_FBDEV=no # Mali fbdev context support
|
HAVE_MALI_FBDEV=no # Mali fbdev context support
|
||||||
|
|
Loading…
Reference in New Issue