(Netplay) Ensure current content is reloaded before joining a host (#14117)

This commit is contained in:
Cthulhu-throwaway 2022-06-30 20:02:04 -03:00 committed by GitHub
parent 58fc77850c
commit ab208fca91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 201 additions and 101 deletions

View File

@ -5984,22 +5984,12 @@ static int action_ok_netplay_connect_room(const char *path, const char *label,
room = &net_st->room_list[room_index]; room = &net_st->room_list[room_index];
if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL))
generic_action_ok_command(CMD_EVENT_NETPLAY_DEINIT);
netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL);
if (room->host_method == NETPLAY_HOST_METHOD_MITM) if (room->host_method == NETPLAY_HOST_METHOD_MITM)
snprintf(hostname, sizeof(hostname), "%s|%d|%s", snprintf(hostname, sizeof(hostname), "%s|%d|%s",
room->mitm_address, room->mitm_port, room->mitm_session); room->mitm_address, room->mitm_port, room->mitm_session);
else else
snprintf(hostname, sizeof(hostname), "%s|%d", room->address, room->port); snprintf(hostname, sizeof(hostname), "%s|%d", room->address, room->port);
#if 0
RARCH_LOG("[Lobby] Connecting to: %s with game: %s/%08x\n",
hostname, room->gamename, room->gamecrc);
#endif
task_push_netplay_crc_scan(room->gamecrc, room->gamename, task_push_netplay_crc_scan(room->gamecrc, room->gamename,
room->subsystem_name, room->corename, hostname); room->subsystem_name, room->corename, hostname);
@ -7337,81 +7327,66 @@ static int action_ok_netplay_enable_host(const char *path,
{ {
if (command_event(CMD_EVENT_NETPLAY_ENABLE_HOST, NULL)) if (command_event(CMD_EVENT_NETPLAY_ENABLE_HOST, NULL))
return generic_action_ok_command(CMD_EVENT_RESUME); return generic_action_ok_command(CMD_EVENT_RESUME);
return -1; return -1;
} }
static void action_ok_netplay_enable_client_hostname_cb( static void action_ok_netplay_enable_client_hostname_cb(void *userdata,
void *ignore, const char *hostname) const char *line)
{ {
if (!string_is_empty(line))
if (hostname && hostname[0])
{ {
bool contentless = false; if (!task_push_netplay_content_reload(line))
bool is_inited = false;
char *tmp_hostname = strdup(hostname);
content_get_status(&contentless, &is_inited);
if (!is_inited)
{ {
command_event(CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED, command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
(void*)tmp_hostname); netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL);
command_event(CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED, (void*)line);
runloop_msg_queue_push( runloop_msg_queue_push(
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_START_WHEN_LOADED), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_START_WHEN_LOADED),
1, 480, true, 1, 480, true, NULL,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
}
else
{
command_event(CMD_EVENT_NETPLAY_INIT_DIRECT,
(void*)tmp_hostname);
generic_action_ok_command(CMD_EVENT_RESUME);
} }
free(tmp_hostname); menu_input_dialog_end();
retroarch_menu_running_finished(false);
} }
else else
{
menu_input_dialog_end(); menu_input_dialog_end();
return;
}
menu_input_dialog_end();
retroarch_menu_running_finished(false);
} }
static int action_ok_netplay_enable_client(const char *path, static int action_ok_netplay_enable_client(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx) const char *label, unsigned type, size_t idx, size_t entry_idx)
{ {
menu_input_ctx_line_t line; settings_t *settings = config_get_ptr();
settings_t *settings = config_get_ptr();
const char *netplay_server = settings->paths.netplay_server; const char *netplay_server = settings->paths.netplay_server;
if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL))
generic_action_ok_command(CMD_EVENT_NETPLAY_DEINIT);
netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL);
if (!string_is_empty(netplay_server)) if (!string_is_empty(netplay_server))
{ {
action_ok_netplay_enable_client_hostname_cb(NULL, netplay_server); action_ok_netplay_enable_client_hostname_cb(NULL, netplay_server);
return 0; return 0;
} }
line.label = msg_hash_to_str( else
MENU_ENUM_LABEL_VALUE_NETPLAY_IP_ADDRESS); {
line.label_setting = "no_setting"; menu_input_ctx_line_t hostname = {0};
line.type = 0;
line.idx = 0; hostname.label =
line.cb = action_ok_netplay_enable_client_hostname_cb; msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_IP_ADDRESS);
hostname.label_setting = "no_setting";
hostname.cb = action_ok_netplay_enable_client_hostname_cb;
if (menu_input_dialog_start(&hostname))
return 0;
}
if (menu_input_dialog_start(&line))
return 0;
return -1; return -1;
} }
static int action_ok_netplay_disconnect(const char *path, static int action_ok_netplay_disconnect(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx) const char *label, unsigned type, size_t idx, size_t entry_idx)
{ {
generic_action_ok_command(CMD_EVENT_NETPLAY_DISCONNECT); command_event(CMD_EVENT_NETPLAY_DISCONNECT, NULL);
return generic_action_ok_command(CMD_EVENT_RESUME); return generic_action_ok_command(CMD_EVENT_RESUME);
} }

View File

@ -181,11 +181,6 @@ static void handle_discord_join_cb(retro_task_t *task, void *task_data,
room = netplay_room_get(0); room = netplay_room_get(0);
if (room) if (room)
{ {
if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL))
deinit_netplay();
netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL);
if (room->host_method == NETPLAY_HOST_METHOD_MITM) if (room->host_method == NETPLAY_HOST_METHOD_MITM)
snprintf(hostname, sizeof(hostname), "%s|%d|%s", snprintf(hostname, sizeof(hostname), "%s|%d|%s",
room->mitm_address, room->mitm_port, room->mitm_session); room->mitm_address, room->mitm_port, room->mitm_session);
@ -193,12 +188,12 @@ static void handle_discord_join_cb(retro_task_t *task, void *task_data,
snprintf(hostname, sizeof(hostname), "%s|%d", snprintf(hostname, sizeof(hostname), "%s|%d",
room->address, room->port); room->address, room->port);
task_push_netplay_crc_scan(room->gamecrc, room->gamename,
room->subsystem_name, room->corename, hostname);
discord_st->connecting = true; discord_st->connecting = true;
if (discord_st->ready) if (discord_st->ready)
discord_update(PRESENCE_NETPLAY_CLIENT); discord_update(PRESENCE_NETPLAY_CLIENT);
task_push_netplay_crc_scan(room->gamecrc, room->gamename,
room->subsystem_name, room->corename, hostname);
} }
netplay_rooms_free(); netplay_rooms_free();

View File

@ -47,11 +47,15 @@
enum enum
{ {
STATE_NONE, /* values */
STATE_NONE = 0,
STATE_LOAD, STATE_LOAD,
STATE_LOAD_SUBSYSTEM, STATE_LOAD_SUBSYSTEM,
STATE_LOAD_CONTENTLESS, STATE_LOAD_CONTENTLESS,
STATE_LOAD_CURRENT /* values mask */
STATE_MASK = 0xFF,
/* flags */
STATE_RELOAD = 0x100
}; };
struct netplay_crc_scan_state struct netplay_crc_scan_state
@ -67,6 +71,7 @@ struct netplay_crc_scan_data
struct string_list *subsystem_content; struct string_list *subsystem_content;
uint32_t crc; uint32_t crc;
char content[NETPLAY_HOST_LONGSTR_LEN]; char content[NETPLAY_HOST_LONGSTR_LEN];
char content_path[PATH_MAX_LENGTH];
char subsystem[NETPLAY_HOST_LONGSTR_LEN]; char subsystem[NETPLAY_HOST_LONGSTR_LEN];
char extension[32]; char extension[32];
bool core_loaded; bool core_loaded;
@ -82,6 +87,8 @@ struct netplay_crc_scan_data
char hostname[512]; char hostname[512];
}; };
static struct netplay_crc_scan_state scan_state = {0};
static bool find_content_by_crc(playlist_config_t *playlist_config, static bool find_content_by_crc(playlist_config_t *playlist_config,
const struct string_list *playlists, uint32_t crc, const struct string_list *playlists, uint32_t crc,
struct string_list *paths, bool first_only) struct string_list *paths, bool first_only)
@ -242,6 +249,9 @@ static void task_netplay_crc_scan_handler(retro_task_t *task)
const char *title = NULL; const char *title = NULL;
struct string_list content_list = {0}; struct string_list content_list = {0};
if (state->state != STATE_NONE)
goto finished; /* We already have what we need. */
/* We really can't do much without the core's path. */ /* We really can't do much without the core's path. */
if (string_is_empty(data->core)) if (string_is_empty(data->core))
{ {
@ -255,8 +265,11 @@ static void task_netplay_crc_scan_handler(retro_task_t *task)
{ {
title = title =
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_COMPAT_CONTENT_FOUND); msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_COMPAT_CONTENT_FOUND);
state->state = data->current.core_loaded ? STATE_LOAD_CURRENT : state->state = STATE_LOAD_CONTENTLESS;
STATE_LOAD_CONTENTLESS;
if (data->current.core_loaded)
state->state |= STATE_RELOAD;
goto finished; goto finished;
} }
@ -273,7 +286,7 @@ static void task_netplay_crc_scan_handler(retro_task_t *task)
title = msg_hash_to_str( title = msg_hash_to_str(
MENU_ENUM_LABEL_VALUE_NETPLAY_COMPAT_CONTENT_FOUND); MENU_ENUM_LABEL_VALUE_NETPLAY_COMPAT_CONTENT_FOUND);
state->state = STATE_LOAD_CURRENT; state->state = STATE_LOAD | STATE_RELOAD;
goto finished; goto finished;
} }
} }
@ -297,7 +310,7 @@ static void task_netplay_crc_scan_handler(retro_task_t *task)
title = msg_hash_to_str( title = msg_hash_to_str(
MENU_ENUM_LABEL_VALUE_NETPLAY_COMPAT_CONTENT_FOUND); MENU_ENUM_LABEL_VALUE_NETPLAY_COMPAT_CONTENT_FOUND);
state->state = STATE_LOAD_CURRENT; state->state = STATE_LOAD | STATE_RELOAD;
goto finished; goto finished;
} }
} }
@ -385,7 +398,7 @@ static void task_netplay_crc_scan_handler(retro_task_t *task)
title = msg_hash_to_str( title = msg_hash_to_str(
MENU_ENUM_LABEL_VALUE_NETPLAY_COMPAT_CONTENT_FOUND); MENU_ENUM_LABEL_VALUE_NETPLAY_COMPAT_CONTENT_FOUND);
state->state = STATE_LOAD_CURRENT; state->state = STATE_LOAD_SUBSYSTEM | STATE_RELOAD;
goto finished; goto finished;
} }
} }
@ -440,24 +453,27 @@ static void task_netplay_crc_scan_callback(retro_task_t *task,
struct netplay_crc_scan_data *data = struct netplay_crc_scan_data *data =
(struct netplay_crc_scan_data*)task_data; (struct netplay_crc_scan_data*)task_data;
switch (state->state) switch (state->state & STATE_MASK)
{ {
case STATE_LOAD: case STATE_LOAD:
{ {
const char *content_path =
data->content_paths.elems[0].data;
content_ctx_info_t content_info = {0}; content_ctx_info_t content_info = {0};
const char *content_path = (state->state & STATE_RELOAD) ?
data->current.content_path : data->content_paths.elems[0].data;
if (data->current.core_loaded)
command_event(CMD_EVENT_UNLOAD_CORE, NULL);
RARCH_LOG("[Lobby] Loading core '%s' with content file '%s'.\n", RARCH_LOG("[Lobby] Loading core '%s' with content file '%s'.\n",
data->core, content_path); data->core, content_path);
if (!data->current.core_loaded) command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
task_push_load_new_core(data->core, netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL);
NULL, NULL, CORE_TYPE_PLAIN, NULL, NULL);
command_event(CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED, command_event(CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED,
data->hostname); data->hostname);
task_push_load_new_core(data->core,
NULL, NULL, CORE_TYPE_PLAIN, NULL, NULL);
task_push_load_content_with_core(content_path, task_push_load_content_with_core(content_path,
&content_info, CORE_TYPE_PLAIN, NULL, NULL); &content_info, CORE_TYPE_PLAIN, NULL, NULL);
} }
@ -465,22 +481,41 @@ static void task_netplay_crc_scan_callback(retro_task_t *task,
case STATE_LOAD_SUBSYSTEM: case STATE_LOAD_SUBSYSTEM:
{ {
RARCH_LOG("[Lobby] Loading core '%s' with subsystem '%s'.\n", const char *subsystem;
data->core, data->subsystem); struct string_list *subsystem_content;
if (!data->current.core_loaded) if (data->current.core_loaded)
task_push_load_new_core(data->core, command_event(CMD_EVENT_UNLOAD_CORE, NULL);
NULL, NULL, CORE_TYPE_PLAIN, NULL, NULL);
if (state->state & STATE_RELOAD)
{
subsystem = data->current.subsystem;
subsystem_content = data->current.subsystem_content;
}
else
{
subsystem = data->subsystem;
subsystem_content = &data->content_paths;
}
RARCH_LOG("[Lobby] Loading core '%s' with subsystem '%s'.\n",
data->core, subsystem);
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL);
task_push_load_new_core(data->core,
NULL, NULL, CORE_TYPE_PLAIN, NULL, NULL);
content_clear_subsystem(); content_clear_subsystem();
if (content_set_subsystem_by_name(data->subsystem)) if (content_set_subsystem_by_name(subsystem))
{ {
size_t i; size_t i;
content_ctx_info_t content_info = {0}; content_ctx_info_t content_info = {0};
for (i = 0; i < data->content_paths.size; i++) for (i = 0; i < subsystem_content->size; i++)
content_add_subsystem(data->content_paths.elems[i].data); content_add_subsystem(subsystem_content->elems[i].data);
command_event(CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED, command_event(CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED,
data->hostname); data->hostname);
@ -494,6 +529,8 @@ static void task_netplay_crc_scan_callback(retro_task_t *task,
/* Disable netplay if we don't have the subsystem. */ /* Disable netplay if we don't have the subsystem. */
netplay_driver_ctl(RARCH_NETPLAY_CTL_DISABLE, NULL); netplay_driver_ctl(RARCH_NETPLAY_CTL_DISABLE, NULL);
command_event(CMD_EVENT_UNLOAD_CORE, NULL);
} }
} }
break; break;
@ -502,33 +539,40 @@ static void task_netplay_crc_scan_callback(retro_task_t *task,
{ {
content_ctx_info_t content_info = {0}; content_ctx_info_t content_info = {0};
if (data->current.core_loaded)
command_event(CMD_EVENT_UNLOAD_CORE, NULL);
RARCH_LOG("[Lobby] Loading contentless core '%s'.\n", data->core); RARCH_LOG("[Lobby] Loading contentless core '%s'.\n", data->core);
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL);
command_event(CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED, command_event(CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED,
data->hostname); data->hostname);
task_push_load_new_core(data->core, task_push_load_new_core(data->core,
NULL, NULL, CORE_TYPE_PLAIN, NULL, NULL); NULL, NULL, CORE_TYPE_PLAIN, NULL, NULL);
task_push_start_current_core(&content_info); task_push_start_current_core(&content_info);
} }
break; break;
case STATE_LOAD_CURRENT: case STATE_NONE:
{ {
RARCH_LOG("[Lobby] Loading core '%s' with current content.\n", if (state->state & STATE_RELOAD)
data->core); {
if (data->current.core_loaded)
command_event(CMD_EVENT_UNLOAD_CORE, NULL);
command_event(CMD_EVENT_NETPLAY_INIT_DIRECT, data->hostname); command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
command_event(CMD_EVENT_RESUME, NULL); netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL);
command_event(CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED,
data->hostname);
}
else
RARCH_WARN("[Lobby] Nothing to load.\n");
} }
break; break;
case STATE_NONE:
RARCH_WARN("[Lobby] Nothing to load.\n");
/* fallthrough */
default: default:
netplay_driver_ctl(RARCH_NETPLAY_CTL_DISABLE, NULL);
break; break;
} }
} }
@ -554,7 +598,6 @@ static void task_netplay_crc_scan_cleanup(retro_task_t *task)
bool task_push_netplay_crc_scan(uint32_t crc, const char *content, bool task_push_netplay_crc_scan(uint32_t crc, const char *content,
const char *subsystem, const char *core, const char *hostname) const char *subsystem, const char *core, const char *hostname)
{ {
static struct netplay_crc_scan_state state = {0};
size_t i; size_t i;
struct netplay_crc_scan_data *data; struct netplay_crc_scan_data *data;
retro_task_t *task; retro_task_t *task;
@ -564,7 +607,7 @@ bool task_push_netplay_crc_scan(uint32_t crc, const char *content,
struct retro_system_info *system = &runloop_state_get_ptr()->system.info; struct retro_system_info *system = &runloop_state_get_ptr()->system.info;
/* Do not run more than one CRC scan task at a time. */ /* Do not run more than one CRC scan task at a time. */
if (state.running) if (scan_state.running)
return false; return false;
data = (struct netplay_crc_scan_data*)calloc(1, sizeof(*data)); data = (struct netplay_crc_scan_data*)calloc(1, sizeof(*data));
@ -575,9 +618,6 @@ bool task_push_netplay_crc_scan(uint32_t crc, const char *content,
free(data); free(data);
free(task); free(task);
/* Make sure we disable netplay on failure. */
netplay_driver_ctl(RARCH_NETPLAY_CTL_DISABLE, NULL);
return false; return false;
} }
@ -637,8 +677,12 @@ bool task_push_netplay_crc_scan(uint32_t crc, const char *content,
pcontent = path_get(RARCH_PATH_CONTENT); pcontent = path_get(RARCH_PATH_CONTENT);
if (!string_is_empty(pcontent)) if (!string_is_empty(pcontent))
{
strlcpy(data->current.content_path, pcontent,
sizeof(data->current.content_path));
strlcpy(data->current.extension, path_get_extension(pcontent), strlcpy(data->current.extension, path_get_extension(pcontent),
sizeof(data->current.extension)); sizeof(data->current.extension));
}
psubsystem = path_get(RARCH_PATH_SUBSYSTEM); psubsystem = path_get(RARCH_PATH_SUBSYSTEM);
if (!string_is_empty(psubsystem)) if (!string_is_empty(psubsystem))
@ -652,14 +696,14 @@ bool task_push_netplay_crc_scan(uint32_t crc, const char *content,
data->current.core_loaded = data->current.core_loaded =
string_is_equal_case_insensitive(system->library_name, core); string_is_equal_case_insensitive(system->library_name, core);
state.state = STATE_NONE; scan_state.state = STATE_NONE;
state.running = true; scan_state.running = true;
task->handler = task_netplay_crc_scan_handler; task->handler = task_netplay_crc_scan_handler;
task->callback = task_netplay_crc_scan_callback; task->callback = task_netplay_crc_scan_callback;
task->cleanup = task_netplay_crc_scan_cleanup; task->cleanup = task_netplay_crc_scan_cleanup;
task->task_data = data; task->task_data = data;
task->state = &state; task->state = &scan_state;
task->title = strdup( task->title = strdup(
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_COMPAT_CONTENT_LOOK)); msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_COMPAT_CONTENT_LOOK));
@ -667,10 +711,95 @@ bool task_push_netplay_crc_scan(uint32_t crc, const char *content,
return true; return true;
} }
bool task_push_netplay_content_reload(const char *hostname)
{
struct netplay_crc_scan_data *data;
retro_task_t *task;
const char *pcore;
bool contentless, is_inited;
/* Do not run more than one CRC scan task at a time. */
if (scan_state.running)
return false;
pcore = path_get(RARCH_PATH_CORE);
if (string_is_empty(pcore) || string_is_equal(pcore, "builtin"))
return false; /* Nothing to reload. */
data = (struct netplay_crc_scan_data*)calloc(1, sizeof(*data));
task = task_init();
if (!data || !task)
{
free(data);
free(task);
return false;
}
scan_state.state = STATE_RELOAD;
strlcpy(data->core, pcore, sizeof(data->core));
strlcpy(data->hostname, hostname, sizeof(data->hostname));
content_get_status(&contentless, &is_inited);
if (contentless)
{
scan_state.state |= STATE_LOAD_CONTENTLESS;
}
else if (is_inited)
{
const char *psubsystem = path_get(RARCH_PATH_SUBSYSTEM);
if (!string_is_empty(psubsystem))
{
strlcpy(data->current.subsystem, psubsystem,
sizeof(data->current.subsystem));
if (path_get_subsystem_list())
data->current.subsystem_content =
string_list_clone(path_get_subsystem_list());
scan_state.state |= STATE_LOAD_SUBSYSTEM;
}
else if (!string_is_empty(path_get(RARCH_PATH_BASENAME)))
{
const char *pcontent = path_get(RARCH_PATH_CONTENT);
if (!string_is_empty(pcontent))
{
strlcpy(data->current.content_path, pcontent,
sizeof(data->current.content_path));
scan_state.state |= STATE_LOAD;
}
}
}
data->current.core_loaded = true;
scan_state.running = true;
task->handler = task_netplay_crc_scan_handler;
task->callback = task_netplay_crc_scan_callback;
task->cleanup = task_netplay_crc_scan_cleanup;
task->task_data = data;
task->state = &scan_state;
task_queue_push(task);
return true;
}
#else #else
bool task_push_netplay_crc_scan(uint32_t crc, const char *content, bool task_push_netplay_crc_scan(uint32_t crc, const char *content,
const char *subsystem, const char *core, const char *hostname) const char *subsystem, const char *core, const char *hostname)
{ {
return false; return false;
} }
bool task_push_netplay_content_reload(const char *hostname)
{
return false;
}
#endif #endif

View File

@ -91,6 +91,7 @@ bool task_push_netplay_lan_scan(void (*cb)(const void*), unsigned timeout);
bool task_push_netplay_crc_scan(uint32_t crc, const char *content, bool task_push_netplay_crc_scan(uint32_t crc, const char *content,
const char *subsystem, const char *core, const char *hostname); const char *subsystem, const char *core, const char *hostname);
bool task_push_netplay_content_reload(const char *hostname);
bool task_push_netplay_nat_traversal(void *data, uint16_t port); bool task_push_netplay_nat_traversal(void *data, uint16_t port);
bool task_push_netplay_nat_close(void *data); bool task_push_netplay_nat_close(void *data);