netplay discovery through bonjour/mdns

This commit is contained in:
Eric Warmenhoven 2023-07-12 01:50:28 -04:00 committed by LibretroAdmin
parent a33ec20c21
commit b1fdbb9a37
8 changed files with 353 additions and 2 deletions

View File

@ -63,3 +63,7 @@
#import "../gfx/common/metal/metal_renderer.m"
#import "../gfx/drivers/metal.m"
#endif
#if defined(HAVE_NETWORKING) && defined(HAVE_NETPLAYDISCOVERY) && defined(HAVE_NETPLAYDISCOVERY_NSNET)
#import "../network/netplay/netplay_nsnetservice.m"
#endif

View File

@ -256,7 +256,12 @@ bool init_netplay_discovery(void)
if (addr)
freeaddrinfo_retro(addr);
#ifdef HAVE_NETPLAYDISCOVERY_NSNET
netplay_mdns_start_discovery();
return true;
#else
return ret;
#endif
}
/** Deinitialize Netplay discovery (client) */
@ -269,6 +274,10 @@ void deinit_netplay_discovery(void)
socket_close(net_st->lan_ad_client_fd);
net_st->lan_ad_client_fd = -1;
}
#ifdef HAVE_NETPLAYDISCOVERY_NSNET
netplay_mdns_finish_discovery(net_st);
#endif
}
static bool netplay_lan_ad_client_query(void)
@ -426,8 +435,15 @@ bool netplay_discovery_driver_ctl(
switch (state)
{
case RARCH_NETPLAY_DISCOVERY_CTL_LAN_SEND_QUERY:
return net_st->lan_ad_client_fd >= 0 && netplay_lan_ad_client_query();
case RARCH_NETPLAY_DISCOVERY_CTL_LAN_SEND_QUERY: {
bool rv;
if (net_st->lan_ad_client_fd >= 0)
rv = netplay_lan_ad_client_query();
#if HAVE_NETPLAYDISCOVERY_NSNET
rv = true;
#endif
return rv;
}
case RARCH_NETPLAY_DISCOVERY_CTL_LAN_GET_RESPONSES:
return net_st->lan_ad_client_fd >= 0 &&
@ -6850,7 +6866,12 @@ try_ipv4:
netplay->connections[0].fd = fd;
}
else
{
netplay->listen_fd = fd;
#ifdef HAVE_NETPLAYDISCOVERY_NSNET
netplay_mdns_publish(netplay);
#endif
}
return true;
}
@ -8627,6 +8648,9 @@ void deinit_netplay(void)
#ifdef HAVE_NETPLAYDISCOVERY
deinit_lan_ad_server_socket();
#ifdef HAVE_NETPLAYDISCOVERY_NSNET
netplay_mdns_unpublish();
#endif
#endif
net_st->data = NULL;

View File

@ -0,0 +1,307 @@
#import <Foundation/Foundation.h>
#import <Foundation/NSNetServices.h>
#import "netplay_private.h"
#import "content.h"
#import "../../frontend/frontend_driver.h"
#import "paths.h"
#import "version.h"
#define NETPLAY_MDNS_TYPE "_ra_netplay._tcp"
@interface NetplayBonjourMan : NSObject<NSNetServiceDelegate, NSNetServiceBrowserDelegate>
@property (strong, nonatomic) NSNetService *service;
@property (strong, nonatomic) NSNetServiceBrowser *browser;
@property (strong, atomic) NSMutableArray<NSNetService*> *services;
+ (NetplayBonjourMan*)shared;
- (void)publish:(netplay_t *)netplay;
- (void)unpublish;
- (void)browse;
- (void)finishBrowsing:(net_driver_state_t *)net_st;
@end
static NetplayBonjourMan *nbm_instance;
@implementation NetplayBonjourMan
#pragma mark - (Semi-)Public API
+ (NetplayBonjourMan*)shared
{
if (!nbm_instance)
nbm_instance = [[NetplayBonjourMan alloc] init];
return nbm_instance;
}
- (void)publish:(netplay_t *)netplay
{
self.service = [[NSNetService alloc] initWithDomain:@"" type:@NETPLAY_MDNS_TYPE name:@"" port:netplay->tcp_port];
[self.service setTXTRecordData:[self TXTdataFromNetplay:netplay]];
[self.service setDelegate:self];
[self.service publish];
}
- (void)unpublish
{
[self.service stop];
self.service = nil;
}
- (void)browse
{
self.services = [NSMutableArray arrayWithCapacity: 0];
dispatch_async(dispatch_get_main_queue(), ^{
self.browser = [[NSNetServiceBrowser alloc] init];
[self.browser setDelegate:self];
[self.browser searchForServicesOfType:@NETPLAY_MDNS_TYPE inDomain:@""];
});
}
- (void)finishBrowsing:(net_driver_state_t *)net_st
{
[self.browser stop];
for (NSNetService *srv in self.services)
{
if (![srv.addresses count] || [srv port] <= 0 || ![srv TXTRecordData])
continue;
NSDictionary<NSString*,NSData*> *txt = [NSNetService dictionaryFromTXTRecordData:[srv TXTRecordData]];
if (!txt)
continue;
char address[16] = {0};
for (NSData *addr_data in [srv addresses])
{
struct sockaddr_storage *their_addr = (struct sockaddr_storage *)[addr_data bytes];
if (!addr_6to4(their_addr))
continue;
if (!getnameinfo_retro((struct sockaddr*)their_addr, sizeof(struct sockaddr_storage),
address, sizeof(address), NULL, 0, NI_NUMERICHOST))
break;
}
if (!address[0])
continue;
/* Make sure we don't already know about it */
long port = [srv port];
size_t iter;
for (iter = 0; iter < net_st->discovered_hosts.size; iter++)
if (port != net_st->discovered_hosts.hosts[iter].port ||
!string_is_equal(address, net_st->discovered_hosts.hosts[iter].address))
continue;
/* Allocate space for it */
if (net_st->discovered_hosts.size >= net_st->discovered_hosts.allocated)
{
if (!net_st->discovered_hosts.size)
{
net_st->discovered_hosts.hosts = (struct netplay_host*)
malloc(sizeof(*net_st->discovered_hosts.hosts));
if (!net_st->discovered_hosts.hosts)
return;
net_st->discovered_hosts.allocated = 1;
}
else
{
size_t new_allocated = net_st->discovered_hosts.allocated + 4;
struct netplay_host *new_hosts = (struct netplay_host*)realloc(
net_st->discovered_hosts.hosts,
new_allocated * sizeof(*new_hosts));
if (!new_hosts)
{
free(net_st->discovered_hosts.hosts);
memset(&net_st->discovered_hosts, 0,
sizeof(net_st->discovered_hosts));
return;
}
net_st->discovered_hosts.allocated = new_allocated;
net_st->discovered_hosts.hosts = new_hosts;
}
}
struct netplay_host *host = &net_st->discovered_hosts.hosts[net_st->discovered_hosts.size++];
NSString *crc = [[NSString alloc] initWithData:txt[@"content_crc"] encoding:NSUTF8StringEncoding];
NSScanner *scanner = [NSScanner scannerWithString:crc];
unsigned int content_crc;
[scanner scanHexInt:&content_crc];
host->content_crc = (int)ntohl(content_crc);
host->port = (int)port;
strlcpy(host->address, address, sizeof(host->address));
strlcpy(host->nick, [txt[@"nick"] bytes], [txt[@"nick"] length] + 1);
strlcpy(host->frontend, [txt[@"frontend"] bytes], [txt[@"frontend"] length] + 1);
strlcpy(host->core, [txt[@"core"] bytes], [txt[@"core"] length] + 1);
strlcpy(host->core_version, [txt[@"core_version"] bytes], [txt[@"core_version"] length] + 1);
strlcpy(host->retroarch_version, [txt[@"retroarch_version"] bytes], [txt[@"retroarch_version"] length] + 1);
strlcpy(host->content, [txt[@"content"] bytes], [txt[@"content"] length] + 1);
strlcpy(host->subsystem_name, [txt[@"subsystem_name"] bytes], [txt[@"subsystem_name"] length] + 1);
host->has_password = [[[NSString alloc] initWithData:txt[@"has_password"] encoding:NSUTF8StringEncoding] isEqualToString:@"true"];
host->has_spectate_password = [[[NSString alloc] initWithData:txt[@"has_spectate_password"] encoding:NSUTF8StringEncoding] isEqualToString:@"true"];
}
}
#pragma mark - Browse helper functions
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser
didFindService:(NSNetService *)service
moreComing:(BOOL)moreComing
{
[service resolveWithTimeout:0.9f];
[self.services addObject:service];
}
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser
didRemoveService:(NSNetService *)service
moreComing:(BOOL)moreComing
{
[self.services removeObject:service];
}
#pragma mark - Publish helper functions
- (NSData *)TXTdataFromNetplay:(netplay_t *)netplay
{
return [NSNetService dataFromTXTRecordDictionary:@{
@"content_crc": [self content_crc],
@"nick": [self nick:netplay],
@"frontend": [self frontend],
@"core": [self core],
@"core_version": [self core_version],
@"retroarch_version": [self retroarch_version],
@"content": [self content],
@"subsystem_name": [self subsystem_name],
@"has_password": [self has_password],
@"has_spectate_password": [self has_spectate_password]
}];
}
- (NSData *)content_crc
{
uint32_t crc = 0;
struct string_list *subsystem = path_get_subsystem_list();
if (!subsystem || subsystem->size <= 0)
crc = content_get_crc();
return [[NSString stringWithFormat:@"%08x", (uint32_t)htonl(crc)] dataUsingEncoding:NSUTF8StringEncoding];
}
- (NSData *)nick:(netplay_t *)netplay
{
return [[NSData alloc] initWithBytes:netplay->nick length:strlen(netplay->nick)];
}
- (NSData *)frontend
{
char frontend_architecture_tmp[24];
const frontend_ctx_driver_t *frontend_drv;
frontend_drv = (const frontend_ctx_driver_t*)
frontend_driver_get_cpu_architecture_str(frontend_architecture_tmp,
sizeof(frontend_architecture_tmp));
NSString *frontend;
if (frontend_drv)
frontend = [NSString stringWithFormat:@"%s %s", frontend_drv->ident, frontend_architecture_tmp];
else
frontend = @"N/A";
return [frontend dataUsingEncoding:NSUTF8StringEncoding];
}
- (NSData *)core
{
struct retro_system_info *system = &runloop_state_get_ptr()->system.info;
return [[NSData alloc] initWithBytes:system->library_name length:strlen(system->library_name)];
}
- (NSData *)core_version
{
struct retro_system_info *system = &runloop_state_get_ptr()->system.info;
return [[NSData alloc] initWithBytes:system->library_version length:strlen(system->library_version)];
}
- (NSData *)retroarch_version
{
return [[NSData alloc] initWithBytes:PACKAGE_VERSION length:strlen(PACKAGE_VERSION)];
}
- (NSData *)content
{
struct string_list *subsystem = path_get_subsystem_list();
if (subsystem && subsystem->size > 0)
{
unsigned i;
NSMutableData *data = [[NSMutableData alloc] init];
for (i = 0;;)
{
const char *pb = path_basename(subsystem->elems[i].data);
[data appendBytes:pb length:strlen(pb)];
if (++i >= subsystem->size)
break;
[data appendBytes:"|" length:strlen("|")];
}
return data;
}
else
{
const char *basename = path_basename(path_get(RARCH_PATH_BASENAME));
if (string_is_empty(basename))
basename = "N/A";
return [[NSData alloc] initWithBytes:basename length:strlen(basename)];
}
}
- (NSData *)subsystem_name
{
struct string_list *subsystem = path_get_subsystem_list();
if (subsystem && subsystem->size > 0)
{
const char *path = path_get(RARCH_PATH_SUBSYSTEM);
return [[NSData alloc] initWithBytes:path length:strlen(path)];
}
else
return [[NSData alloc] initWithBytes:"N/A" length:3];
}
- (NSData *)has_password
{
settings_t *settings = config_get_ptr();
const char *has_password = string_is_empty(settings->paths.netplay_password) ? "false" : "true";
return [[NSData alloc] initWithBytes:has_password length:strlen(has_password)];
}
- (NSData *)has_spectate_password
{
settings_t *settings = config_get_ptr();
const char *has_password = string_is_empty(settings->paths.netplay_spectate_password) ? "false" : "true";
return [[NSData alloc] initWithBytes:has_password length:strlen(has_password)];
}
@end
void netplay_mdns_publish(netplay_t *netplay)
{
[[NetplayBonjourMan shared] publish:netplay];
}
void netplay_mdns_unpublish(void)
{
[[NetplayBonjourMan shared] unpublish];
}
void netplay_mdns_start_discovery(void)
{
[[NetplayBonjourMan shared] browse];
}
void netplay_mdns_finish_discovery(net_driver_state_t *net_st)
{
[[NetplayBonjourMan shared] finishBrowsing:net_st];
}

View File

@ -848,4 +848,10 @@ bool netplay_cmd_mode(netplay_t *netplay,
**/
void netplay_load_savestate(netplay_t *netplay,
retro_ctx_serialize_info_t *serial_info, bool save);
void netplay_mdns_publish(netplay_t *netplay);
void netplay_mdns_unpublish(void);
void netplay_mdns_start_discovery(void);
void netplay_mdns_finish_discovery(net_driver_state_t *net_st);
#endif

View File

@ -51,6 +51,7 @@ OTHER_CFLAGS = $(inherited) -DHAVE_METAL
OTHER_CFLAGS = $(inherited) -DHAVE_MFI
OTHER_CFLAGS = $(inherited) -DHAVE_MMAP
OTHER_CFLAGS = $(inherited) -DHAVE_NETPLAYDISCOVERY
OTHER_CFLAGS = $(inherited) -DHAVE_NETPLAYDISCOVERY_NSNET
OTHER_CFLAGS = $(inherited) -DHAVE_NETWORKGAMEPAD
OTHER_CFLAGS = $(inherited) -DHAVE_NETWORKING
OTHER_CFLAGS = $(inherited) -DHAVE_NETWORK_CMD

View File

@ -39,6 +39,10 @@
<string>public.app-category.games</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSBonjourServices</key>
<array>
<string>_ra_netplay._tcp</string>
</array>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSHumanReadableCopyright</key>

View File

@ -1624,6 +1624,7 @@
"-DHAVE_MFI",
"-DHAVE_MINIUPNPC",
"-DHAVE_NETPLAYDISCOVERY",
"-DHAVE_NETPLAYDISCOVERY_NSNET",
"-DHAVE_NETWORKGAMEPAD",
"-DHAVE_NETWORKING",
"-DHAVE_ONLINE_UPDATER",
@ -1770,6 +1771,7 @@
"-DHAVE_MFI",
"-DHAVE_MINIUPNPC",
"-DHAVE_NETPLAYDISCOVERY",
"-DHAVE_NETPLAYDISCOVERY_NSNET",
"-DHAVE_NETWORKGAMEPAD",
"-DHAVE_NETWORKING",
"-DHAVE_ONLINE_UPDATER",
@ -1939,6 +1941,7 @@
"-DHAVE_MFI",
"-DHAVE_MINIUPNPC",
"-DHAVE_NETPLAYDISCOVERY",
"-DHAVE_NETPLAYDISCOVERY_NSNET",
"-DHAVE_NETWORKGAMEPAD",
"-DHAVE_NETWORKING",
"-DHAVE_ONLINE_UPDATER",
@ -2102,6 +2105,7 @@
"-DHAVE_MFI",
"-DHAVE_MINIUPNPC",
"-DHAVE_NETPLAYDISCOVERY",
"-DHAVE_NETPLAYDISCOVERY_NSNET",
"-DHAVE_NETWORKGAMEPAD",
"-DHAVE_NETWORKING",
"-DHAVE_ONLINE_UPDATER",

View File

@ -49,6 +49,7 @@
<key>NSBonjourServices</key>
<array>
<string>_altserver._tcp</string>
<string>_ra_netplay._tcp</string>
</array>
<key>NSLocalNetworkUsageDescription</key>
<string>RetroArch uses the local network to find and communicate with AltServer to enable JIT.</string>