diff --git a/network/netplay/netplay.c b/network/netplay/netplay.c index b9acb1bf5f..2c8cb5358f 100644 --- a/network/netplay/netplay.c +++ b/network/netplay/netplay.c @@ -54,6 +54,9 @@ enum static bool netplay_enabled = false; static bool netplay_is_client = false; +/* Used to advertise or request advertisement of Netplay */ +static int netplay_ad_fd = -1; + /* Used while Netplay is running */ static netplay_t *netplay_data = NULL; @@ -167,6 +170,28 @@ static bool init_tcp_socket(netplay_t *netplay, const char *server, return ret; } +static bool init_ad_socket(netplay_t *netplay, uint16_t port) +{ + int fd = socket_init((void**)&netplay->addr, port, NULL, SOCKET_TYPE_DATAGRAM); + + if (fd < 0) + goto error; + + if (!socket_bind(fd, (void*)netplay->addr)) + { + socket_close(fd); + goto error; + } + + netplay_ad_fd = fd; + + return true; + +error: + RARCH_ERR("Failed to initialize netplay advertisement socket.\n"); + return false; +} + static bool init_socket(netplay_t *netplay, const char *server, uint16_t port) { if (!network_init()) @@ -1291,6 +1316,13 @@ bool netplay_pre_frame(netplay_t *netplay) netplay_try_init_serialization(netplay); } + /* Advertise our server if applicable */ + if (netplay->is_server) + { + if (netplay_ad_fd >= 0 || init_ad_socket(netplay, RARCH_DEFAULT_PORT)) + netplay_ad_server(netplay, netplay_ad_fd); + } + if (!netplay->net_cbs->pre_frame(netplay)) return false; @@ -1427,7 +1459,6 @@ void deinit_netplay(void) if (netplay_data) netplay_free(netplay_data); netplay_data = NULL; - netplay_enabled = false; } /** diff --git a/network/netplay/netplay_common.c b/network/netplay/netplay_common.c index 782f8b9b42..42a8a279f7 100644 --- a/network/netplay/netplay_common.c +++ b/network/netplay/netplay_common.c @@ -386,3 +386,103 @@ uint32_t netplay_delta_frame_crc(netplay_t *netplay, struct delta_frame *delta) return 0; return encoding_crc32(0L, (const unsigned char*)delta->state, netplay->state_size); } + +/* + * AD PACKET FORMAT: + * + * Request: + * 1 word: RANQ (RetroArch Netplay Query) + * 1 word: Netplay protocol version + * + * Reply: + * 1 word : RANS (RetroArch Netplay Server) + * 1 word : Netplay protocol version + * 1 word : Port + * 8 words: RetroArch version + * 8 words: Nick + * 8 words: Core name + * 8 words: Core version + * 8 words: Content name (currently always blank) + */ + +#define AD_PACKET_MAX_SIZE 512 +#define AD_PACKET_STRING_SIZE 32 +#define AD_PACKET_STRING_WORDS (AD_PACKET_STRING_SIZE/sizeof(uint32_t)) +static uint32_t *ad_packet_buffer = NULL; + +bool netplay_ad_server(netplay_t *netplay, int ad_fd) +{ + fd_set fds; + struct timeval tmp_tv = {0}; + struct sockaddr their_addr; + socklen_t addr_size; + rarch_system_info_t *info = NULL; + size_t bufloc; + + if (!ad_packet_buffer) + { + ad_packet_buffer = (uint32_t *) malloc(AD_PACKET_MAX_SIZE); + if (!ad_packet_buffer) + return false; + } + + /* Check for any ad queries */ + while (1) + { + FD_ZERO(&fds); + FD_SET(ad_fd, &fds); + if (socket_select(ad_fd + 1, &fds, NULL, NULL, &tmp_tv) <= 0) + break; + if (!FD_ISSET(ad_fd, &fds)) + break; + + /* Somebody queried, so check that it's valid */ + if (recvfrom(ad_fd, ad_packet_buffer, AD_PACKET_MAX_SIZE, 0, + &their_addr, &addr_size) >= (ssize_t) (2*sizeof(uint32_t))) + { + /* Make sure it's a valid query */ + if (memcmp(ad_packet_buffer, "RANQ", 4)) + continue; + + /* For this version */ + if (ntohl(ad_packet_buffer[1]) != NETPLAY_PROTOCOL_VERSION) + continue; + + runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &info); + + /* Now build our response */ + memset(ad_packet_buffer, 0, AD_PACKET_MAX_SIZE); + memcpy(ad_packet_buffer, "RANS", 4); + ad_packet_buffer[1] = htonl(NETPLAY_PROTOCOL_VERSION); + ad_packet_buffer[2] = htonl(netplay->tcp_port); + bufloc = 3; + strncpy((char *) (ad_packet_buffer + bufloc), + PACKAGE_VERSION, AD_PACKET_STRING_SIZE); + bufloc += AD_PACKET_STRING_WORDS; + strncpy((char *) (ad_packet_buffer + bufloc), + netplay->nick, AD_PACKET_STRING_SIZE); + bufloc += AD_PACKET_STRING_WORDS; + if (info) + { + strncpy((char *) (ad_packet_buffer + bufloc), + info->info.library_name, AD_PACKET_STRING_SIZE); + bufloc += AD_PACKET_STRING_WORDS; + strncpy((char *) (ad_packet_buffer + bufloc), + info->info.library_version, AD_PACKET_STRING_SIZE); + bufloc += AD_PACKET_STRING_WORDS; + /* Blank content */ + bufloc += AD_PACKET_STRING_WORDS; + } + else + { + bufloc += 3*AD_PACKET_STRING_WORDS; + } + + /* And send it */ + sendto(ad_fd, ad_packet_buffer, bufloc*sizeof(uint32_t), 0, + &their_addr, addr_size); + } + } + + return true; +} diff --git a/network/netplay/netplay_private.h b/network/netplay/netplay_private.h index 0a7e38254d..4cfbe9273e 100644 --- a/network/netplay/netplay_private.h +++ b/network/netplay/netplay_private.h @@ -234,4 +234,6 @@ bool netplay_cmd_crc(netplay_t *netplay, struct delta_frame *delta); bool netplay_cmd_request_savestate(netplay_t *netplay); +bool netplay_ad_server(netplay_t *netplay, int ad_fd); + #endif