mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-24 00:20:01 +00:00
Allow sending UDP commands from RetroArch.
This commit is contained in:
parent
02c77d3685
commit
a0ec6da2a8
@ -177,6 +177,16 @@ Clients can connect and disconnect at any time.
|
||||
Clients thus cannot interact as player 2.
|
||||
For spectating mode to work, both host and clients will need to use this flag.
|
||||
|
||||
.TP
|
||||
\fB--command CMD\fR
|
||||
Sends a command over UDP to an already running RetroArch application, and exit.
|
||||
The command is formatted as "COMMAND:HOST:PORT".
|
||||
HOST and PORT are both optional. "COMMAND:HOST" will set PORT to
|
||||
"network_cmd_port" default setting.
|
||||
If only "COMMAND" is used, HOST and PORT will be assumed to be "localhost" and "network_cmd_port" respectively.
|
||||
|
||||
The available commands are listed if "COMMAND" is invalid.
|
||||
|
||||
.TP
|
||||
\fB--nick NICK\fR
|
||||
Pick a nickname for use with netplay.
|
||||
|
115
network_cmd.c
115
network_cmd.c
@ -17,9 +17,13 @@
|
||||
#include "network_cmd.h"
|
||||
#include "driver.h"
|
||||
#include "general.h"
|
||||
#include "compat/strl.h"
|
||||
#include "compat/posix_string.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DEFAULT_NETWORK_CMD_PORT 55355
|
||||
|
||||
struct network_cmd
|
||||
{
|
||||
int fd;
|
||||
@ -42,6 +46,8 @@ network_cmd_t *network_cmd_new(uint16_t port)
|
||||
if (!handle)
|
||||
return NULL;
|
||||
|
||||
RARCH_LOG("Bringing up command interface on port %hu.\n", (unsigned short)port);
|
||||
|
||||
handle->fd = -1;
|
||||
|
||||
struct addrinfo hints, *res = NULL;
|
||||
@ -76,7 +82,6 @@ network_cmd_t *network_cmd_new(uint16_t port)
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
|
||||
return handle;
|
||||
|
||||
error:
|
||||
@ -190,3 +195,111 @@ void network_cmd_pre_frame(network_cmd_t *handle)
|
||||
}
|
||||
}
|
||||
|
||||
static bool send_udp_packet(const char *host, uint16_t port, const char *msg)
|
||||
{
|
||||
struct addrinfo hints, *res = NULL;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
#if defined(_WIN32) || defined(HAVE_SOCKET_LEGACY)
|
||||
hints.ai_family = AF_INET;
|
||||
#else
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
#endif
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
int fd = -1;
|
||||
bool ret = true;
|
||||
char port_buf[16];
|
||||
|
||||
snprintf(port_buf, sizeof(port_buf), "%hu", (unsigned short)port);
|
||||
if (getaddrinfo(host, port_buf, &hints, &res) < 0)
|
||||
return false;
|
||||
|
||||
// Send to all possible targets.
|
||||
// "localhost" might resolve to several different IPs.
|
||||
const struct addrinfo *tmp = res;
|
||||
while (tmp)
|
||||
{
|
||||
fd = socket(tmp->ai_family, tmp->ai_socktype, tmp->ai_protocol);
|
||||
if (fd < 0)
|
||||
{
|
||||
ret = false;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ssize_t len = strlen(msg);
|
||||
ssize_t ret = sendto(fd, msg, len, 0, tmp->ai_addr, tmp->ai_addrlen);
|
||||
if (ret < len)
|
||||
{
|
||||
ret = false;
|
||||
goto end;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
fd = -1;
|
||||
tmp = tmp->ai_next;
|
||||
}
|
||||
|
||||
end:
|
||||
freeaddrinfo(res);
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool verify_command(const char *cmd)
|
||||
{
|
||||
for (unsigned i = 0; i < sizeof(map) / sizeof(map[0]); i++)
|
||||
{
|
||||
if (strcmp(map[i].str, cmd) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
RARCH_ERR("Command \"%s\" is not recognized by RetroArch.\n", cmd);
|
||||
RARCH_ERR("\tValid commands:\n");
|
||||
for (unsigned i = 0; i < sizeof(map) / sizeof(map[0]); i++)
|
||||
RARCH_ERR("\t\t%s\n", map[i].str);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool network_cmd_send(const char *cmd_)
|
||||
{
|
||||
char *command = strdup(cmd_);
|
||||
if (!command)
|
||||
return false;
|
||||
|
||||
bool old_verbose = g_extern.verbose;
|
||||
g_extern.verbose = true;
|
||||
|
||||
const char *cmd = NULL;
|
||||
const char *host = NULL;
|
||||
const char *port_ = NULL;
|
||||
uint16_t port = DEFAULT_NETWORK_CMD_PORT;
|
||||
|
||||
cmd = strtok(command, ":");
|
||||
if (cmd)
|
||||
host = strtok(NULL, ":");
|
||||
if (host)
|
||||
port_ = strtok(NULL, ":");
|
||||
|
||||
if (!host)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
host = "127.0.0.1";
|
||||
#else
|
||||
host = "localhost";
|
||||
#endif
|
||||
}
|
||||
|
||||
if (port_)
|
||||
port = strtoul(port_, NULL, 0);
|
||||
|
||||
RARCH_LOG("Sending command: \"%s\" to %s:%hu\n", cmd, host, (unsigned short)port);
|
||||
|
||||
bool ret = verify_command(cmd) && send_udp_packet(host, port, cmd);
|
||||
free(command);
|
||||
|
||||
g_extern.verbose = old_verbose;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -28,5 +28,7 @@ void network_cmd_pre_frame(network_cmd_t *handle);
|
||||
void network_cmd_set(network_cmd_t *handle, unsigned id);
|
||||
bool network_cmd_get(network_cmd_t *handle, unsigned id);
|
||||
|
||||
bool network_cmd_send(const char *cmd);
|
||||
|
||||
#endif
|
||||
|
||||
|
18
retroarch.c
18
retroarch.c
@ -539,6 +539,10 @@ static void print_help(void)
|
||||
puts("\t\tHowever, the client will not be able to play. Multiple clients can connect to the host.");
|
||||
puts("\t--nick: Picks a nickname for use with netplay. Not mandatory.");
|
||||
#endif
|
||||
#ifdef HAVE_NETWORK_CMD
|
||||
puts("\t--command: Sends a command over UDP to an already running RetroArch process.");
|
||||
puts("\t\tAvailable commands are listed if command is invalid.");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FFMPEG
|
||||
puts("\t-r/--record: Path to record video file.\n\t\tUsing .mkv extension is recommended.");
|
||||
@ -695,6 +699,9 @@ static void parse_input(int argc, char *argv[])
|
||||
{ "port", 1, &val, 'p' },
|
||||
{ "spectate", 0, &val, 'S' },
|
||||
{ "nick", 1, &val, 'N' },
|
||||
#endif
|
||||
#ifdef HAVE_NETWORK_CMD
|
||||
{ "command", 1, &val, 'c' },
|
||||
#endif
|
||||
{ "ups", 1, NULL, 'U' },
|
||||
{ "bps", 1, &val, 'B' },
|
||||
@ -892,8 +899,6 @@ static void parse_input(int argc, char *argv[])
|
||||
|
||||
case 'F':
|
||||
g_extern.netplay_sync_frames = strtol(optarg, NULL, 0);
|
||||
if (g_extern.netplay_sync_frames > 16)
|
||||
g_extern.netplay_sync_frames = 16;
|
||||
break;
|
||||
#endif
|
||||
|
||||
@ -929,6 +934,15 @@ static void parse_input(int argc, char *argv[])
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NETWORK_CMD
|
||||
case 'c':
|
||||
if (network_cmd_send(optarg))
|
||||
exit(0);
|
||||
else
|
||||
rarch_fail(1, "network_cmd_send()");
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 'B':
|
||||
strlcpy(g_extern.bps_name, optarg, sizeof(g_extern.bps_name));
|
||||
g_extern.bps_pref = true;
|
||||
|
Loading…
Reference in New Issue
Block a user