Skip to content

Commit

Permalink
netplay discovery through bonjour/mdns
Browse files Browse the repository at this point in the history
  • Loading branch information
warmenhoven authored and LibretroAdmin committed Jul 15, 2023
1 parent a33ec20 commit b1fdbb9
Show file tree
Hide file tree
Showing 8 changed files with 353 additions and 2 deletions.
4 changes: 4 additions & 0 deletions griffin/griffin_objc.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
28 changes: 26 additions & 2 deletions network/netplay/netplay_frontend.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) */
Expand All @@ -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)
Expand Down Expand Up @@ -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 &&
Expand Down Expand Up @@ -6850,7 +6866,12 @@ static bool init_tcp_socket(netplay_t *netplay,
netplay->connections[0].fd = fd;
}
else
{
netplay->listen_fd = fd;
#ifdef HAVE_NETPLAYDISCOVERY_NSNET
netplay_mdns_publish(netplay);
#endif
}

return true;
}
Expand Down Expand Up @@ -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;
Expand Down
307 changes: 307 additions & 0 deletions network/netplay/netplay_nsnetservice.m
Original file line number Diff line number Diff line change
@@ -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];
}
6 changes: 6 additions & 0 deletions network/netplay/netplay_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions pkg/apple/BaseConfig.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit b1fdbb9

Please sign in to comment.