Merge pull request #4104 from GregorR/natt

NAT traversal
This commit is contained in:
Twinaphex 2016-11-30 16:09:53 +01:00 committed by GitHub
commit bb499b70be
17 changed files with 448 additions and 8 deletions

View File

@ -1086,6 +1086,7 @@ ifeq ($(HAVE_NETWORKING), 1)
OBJ += $(LIBRETRO_COMM_DIR)/net/net_compat.o \
$(LIBRETRO_COMM_DIR)/net/net_http.o \
$(LIBRETRO_COMM_DIR)/net/net_socket.o \
$(LIBRETRO_COMM_DIR)/net/net_natt.o \
network/net_http_special.o \
tasks/task_http.o
@ -1123,6 +1124,10 @@ ifeq ($(HAVE_NETWORKING), 1)
OBJ += input/input_remote.o \
cores/libretro-net-retropad/net_retropad_core.o
endif
ifeq ($(HAVE_MINIUPNPC), 1)
LIBS += -lminiupnpc
endif
endif
ifneq ($(findstring Win32,$(OS)),)

View File

@ -800,6 +800,7 @@ static int populate_settings_bool(settings_t *settings, struct config_bool_setti
#endif
#ifdef HAVE_NETWORKING
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
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);
@ -922,7 +923,7 @@ static int populate_settings_int(settings_t *settings, struct config_int_setting
#ifdef HAVE_NETWORKING
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_check_frames", &settings->netplay.check_frames, false, 30, false);
SETTING_INT("netplay_check_frames", &settings->netplay.check_frames, true, 30, false);
#endif
#ifdef HAVE_LANGEXTRA
SETTING_INT("user_language", &settings->user_language, true, RETRO_LANGUAGE_ENGLISH, false);

View File

@ -403,6 +403,7 @@ typedef struct settings
unsigned check_frames;
bool is_spectate;
bool swap_input;
bool nat_traversal;
} netplay;
#endif

View File

@ -879,6 +879,7 @@ NETPLAY
#include "../libretro-common/net/net_compat.c"
#include "../libretro-common/net/net_socket.c"
#include "../libretro-common/net/net_http.c"
#include "../libretro-common/net/net_natt.c"
#ifndef HAVE_SOCKET_LEGACY
#include "../libretro-common/net/net_ifinfo.c"
#endif

View File

@ -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"
"netplay host. \n");
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:
snprintf(s, len,
"Maximum amount of swapchain images. This \n"

View File

@ -22,6 +22,10 @@ MSG_HASH(
MSG_GOT_CONNECTION_FROM,
"Got connection from"
)
MSG_HASH(
MSG_PUBLIC_ADDRESS,
"Public address"
)
MSG_HASH(
MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN,
"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")
MSG_HASH(MENU_ENUM_LABEL_VALUE_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,
"Network Commands")
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_CMD_PORT,

View File

@ -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

View File

@ -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

View File

@ -4942,6 +4942,10 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data)
MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT,
PARSE_ONLY_BOOL, false) != -1)
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,
MENU_ENUM_LABEL_NETWORK_CMD_ENABLE,
PARSE_ONLY_BOOL, false) != -1)

View File

@ -5589,6 +5589,21 @@ static bool setting_append_list(
general_read_handler,
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(
list, list_info,
&settings->netplay.swap_input,

View File

@ -173,6 +173,7 @@ enum msg_hash_enums
MSG_NO_STATE_HAS_BEEN_LOADED_YET,
MSG_GOT_CONNECTION_FROM,
MSG_CONNECTION_SLOT,
MSG_PUBLIC_ADDRESS,
MSG_NO_SAVE_STATE_HAS_BEEN_OVERWRITTEN_YET,
MSG_CANNOT_INFER_NEW_CONFIG_PATH,
MSG_UNDID_LOAD_STATE,
@ -975,6 +976,7 @@ enum msg_hash_enums
MENU_LABEL(NETPLAY_CHECK_FRAMES),
MENU_LABEL(NETPLAY_SPECTATOR_MODE_ENABLE),
MENU_LABEL(NETPLAY_TCP_UDP_PORT),
MENU_LABEL(NETPLAY_NAT_TRAVERSAL),
MENU_LABEL(SORT_SAVEFILES_ENABLE),
MENU_LABEL(SORT_SAVESTATES_ENABLE),
MENU_LABEL(NETPLAY_IP_ADDRESS),

View File

@ -63,6 +63,8 @@ static netplay_t *netplay_data = NULL;
/* Used to avoid recursive netplay calls */
static bool in_netplay = false;
static void announce_nat_traversal(netplay_t *netplay);
static int init_tcp_connection(const struct addrinfo *res,
bool server, bool spectate,
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;
}
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)
{
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))
return false;
if (netplay->is_server && netplay->nat_traversal)
init_nat_traversal(netplay);
return true;
}
@ -1061,6 +1082,36 @@ void netplay_log_connection(const struct sockaddr_storage *their_addr,
#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)
@ -1180,6 +1231,7 @@ static bool netplay_init_buffers(netplay_t *netplay, unsigned frames)
* @check_frames : Frequency with which to check CRCs.
* @cb : Libretro callbacks.
* @spectate : If true, enable spectator mode.
* @nat_traversal : If true, attempt NAT traversal.
* @nick : Nickname of user.
* @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.
**/
netplay_t *netplay_new(const char *server, uint16_t port,
unsigned frames, unsigned check_frames,
const struct retro_callbacks *cb,
bool spectate, const char *nick, uint64_t quirks)
netplay_t *netplay_new(const char *server, uint16_t port, unsigned frames,
unsigned check_frames, const struct retro_callbacks *cb, bool spectate,
bool nat_traversal, const char *nick, uint64_t quirks)
{
netplay_t *netplay = (netplay_t*)calloc(1, sizeof(*netplay));
if (!netplay)
@ -1203,6 +1254,7 @@ netplay_t *netplay_new(const char *server, uint16_t port,
netplay->port = server ? 0 : 1;
netplay->spectate.enabled = spectate;
netplay->is_server = server == NULL;
netplay->nat_traversal = netplay->is_server ? nat_traversal : false;
netplay->stall_frames = frames;
netplay->check_frames = check_frames;
netplay->quirks = quirks;
@ -1338,6 +1390,9 @@ void netplay_free(netplay_t *netplay)
free(netplay->spectate.input);
}
if (netplay->nat_traversal)
natt_free(&netplay->nat_traversal_state);
if (netplay->buffer)
{
for (i = 0; i < netplay->buffer_size; i++)
@ -1383,11 +1438,26 @@ bool netplay_pre_frame(netplay_t *netplay)
netplay_try_init_serialization(netplay);
}
/* Advertise our server if applicable */
if (netplay->is_server)
{
/* Advertise our server if applicable */
if (netplay_ad_fd >= 0 || init_ad_socket(netplay, RARCH_DEFAULT_PORT))
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))
@ -1613,7 +1683,8 @@ bool init_netplay(bool is_spectate, const char *server, unsigned port)
netplay_is_client ? server : NULL,
port ? port : RARCH_DEFAULT_PORT,
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)
return true;

View File

@ -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.
* @cb : Libretro callbacks.
* @spectate : If true, enable spectator mode.
* @nat_traversal : If true, attempt NAT traversal.
* @nick : Nickname of user.
* @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,
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);
/**

View File

@ -20,6 +20,7 @@
#include <net/net_compat.h>
#include <net/net_socket.h>
#include <net/net_natt.h>
#include "netplay_private.h"
@ -337,6 +338,19 @@ static bool netplay_net_info_cb(netplay_t* netplay, unsigned frames)
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;
}

View File

@ -20,6 +20,7 @@
#include "netplay.h"
#include <net/net_compat.h>
#include <net/net_natt.h>
#include <features/features_cpu.h>
#include <streams/trans_stream.h>
#include <retro_endianness.h>
@ -124,6 +125,9 @@ struct netplay
int fd;
/* TCP port (if serving) */
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)? */
unsigned port;
bool has_connection;

View File

@ -182,6 +182,10 @@ if [ "$HAVE_NETWORKING" = 'yes' ]; then
fi
HAVE_NETWORK_CMD=yes
HAVE_NETWORKGAMEPAD=yes
if [ "$HAVE_MINIUPNPC" != "no" ]; then
check_lib MINIUPNPC "-lminiupnpc"
fi
else
echo "Warning: All networking features have been disabled."
HAVE_NETWORK_CMD='no'

View File

@ -28,6 +28,7 @@ HAVE_DYLIB=auto # Dynamic loading support
HAVE_NETWORKING=auto # Networking features (recommended)
HAVE_NETWORKGAMEPAD=auto # Networked game pad (plus baked-in core)
C89_NETWORKGAMEPAD=no
HAVE_MINIUPNPC=auto # Mini UPnP client library (for NAT traversal)
HAVE_D3D9=yes # Direct3D 9 support
HAVE_OPENGL=auto # OpenGL support
HAVE_MALI_FBDEV=no # Mali fbdev context support