mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-23 07:59:42 +00:00
netplay discovery through bonjour/mdns
This commit is contained in:
parent
a33ec20c21
commit
b1fdbb9a37
@ -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
|
||||
|
@ -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;
|
||||
|
307
network/netplay/netplay_nsnetservice.m
Normal file
307
network/netplay/netplay_nsnetservice.m
Normal 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];
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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",
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user