Add netpacket interface

Adds a new libretro interface for a core to send and receive custom network packets for implementing a communication based multiplayer system instead of using the default state serialization based multiplayer. Connection management is still done by the frontend while a core gains the ability to easily support tunneling of multi-console data communication traffic.
This commit is contained in:
Bernhard Schelling 2023-06-21 02:35:18 +09:00 committed by LibretroAdmin
parent 3c1c58cfc3
commit c60878a10d
9 changed files with 357 additions and 23 deletions

View File

@ -1809,6 +1809,28 @@ enum retro_mod
* even before the microphone driver is ready.
*/
#define RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE 76
/* const struct retro_netpacket_callback * --
* When set, a core gets control over network packets sent and
* received during a multiplayer session. This can be used to emulate
* multiplayer games that were originally played on 2 or more separate
* consoles or computers connected together.
*
* The frontend will take care of connecting players together.
* The core only needs to send the actual data as needed for the
* emulation while handshake and connection management happens in
* the background.
*
* When 2 or more players are connected and this interface has been
* set, time manipulation features (pausing, slow motion, fast forward,
* rewinding, save state loading, etc.) are disabled to not interrupt
* communication.
*
* When not set, a frontend may use state serialization based
* multiplayer where a deterministic core supporting multiple
* input devices does not need to do anything on its own.
*/
/* VFS functionality */
/* File paths:
@ -3030,6 +3052,89 @@ struct retro_disk_control_ext_callback
retro_get_image_label_t get_image_label; /* Optional - may be NULL */
};
/* Callbacks for RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE.
* A core can set it if sending and receiving custom network packets
* during a multiplayer session is desired.
*/
/* Used by the core to send a packet to one or more connected players.
* A single packet sent via this interface can contain up to 64kb of data.
*
* If the ready callback has indicated the local player to be the host:
* - The broadcast flag can be set to true to send to multiple connected clients
* - On a broadcast, the client_id argument indicates 1 client NOT to send the packet to
* - Otherwise, the client_id argument indicates a single client to send the packet to
* If the local player is a client connected to a host:
* - The broadcast flag is ignored
* - The client_id argument must be set to 0
*
* This function is not guaranteed to be thread-safe and must be called during
* retro_run or any of the netpacket callbacks passed with this interface.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_send_t)(const void* buf, size_t len, uint16_t client_id, bool broadcast);
/* Called by the frontend to signify that a multiplayer session has started.
* If client_id is 0 the local player is the host of the session and at this
* point no other player has connected yet.
*
* If client_id is > 0 the local player is a client connected to a host and
* at this point is already fully connected to the host.
*
* The core will have to store the retro_netpacket_send_t function pointer
* passed here and use it whenever it wants to send a packet. That send
* function pointer is valid until the frontend calls retro_netpacket_stop_t.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_start_t)(uint16_t client_id, retro_netpacket_send_t send_fn);
/* Called by the frontend when a new packet arrives which has been sent from
* a connected client or the host with retro_netpacket_send_t.
* The client_id argument indicates who has sent the packet. On the host side
* this will always be > 0 (coming from a connected client).
* On a client connected to the host it is always 0 (coming from the host).
* Packets sent with this interface arrive at this callback in a reliable
* manner, meaning in the same order they were sent and without packet loss.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_receive_t)(const void* buf, size_t len, uint16_t client_id);
/* Called by the frontend when the multiplayer session has ended.
* Once this gets called the retro_netpacket_send_t function pointer passed
* to retro_netpacket_start_t will not be valid anymore.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_stop_t)(void);
/* Called by the frontend every frame (between calls to retro_run while
* updating the state of the multiplayer session.
* This is a good place for the core to call retro_netpacket_send_t from.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_poll_t)(void);
/* Called by the frontend when a new player connects to the hosted session.
* This is only called on the host side, not for clients connected to the host.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_connected_t)(uint16_t client_id);
/* Called by the frontend when a player leaves or disconnects from the hosted session.
* This is only called on the host side, not for clients connected to the host.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_disconnected_t)(uint16_t client_id);
/**
* A callback interface for giving a core the ability to send and receive custom
* network packets during a multiplayer session between two or more instances
* of a libretro frontend.
*
* @see RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE
*/
struct retro_netpacket_callback
{
retro_netpacket_start_t start;
retro_netpacket_receive_t receive;
retro_netpacket_stop_t stop; /* Optional - may be NULL */
retro_netpacket_poll_t poll; /* Optional - may be NULL */
retro_netpacket_connected_t connected; /* Optional - may be NULL */
retro_netpacket_disconnected_t disconnected; /* Optional - may be NULL */
};
enum retro_pixel_format
{
/* 0RGB1555, native endian.

View File

@ -147,6 +147,7 @@ typedef struct
struct netplay_room host_room;
struct netplay_room *room_list;
struct netplay_rooms *rooms_data;
struct retro_netpacket_callback *core_netpacket_interface;
/* Used while Netplay is running */
netplay_t *data;
netplay_client_info_t *client_info;

View File

@ -74,7 +74,10 @@ enum rarch_netplay_ctl_state
RARCH_NETPLAY_CTL_DESYNC_PUSH,
RARCH_NETPLAY_CTL_DESYNC_POP,
RARCH_NETPLAY_CTL_KICK_CLIENT,
RARCH_NETPLAY_CTL_BAN_CLIENT
RARCH_NETPLAY_CTL_BAN_CLIENT,
RARCH_NETPLAY_CTL_SET_CORE_PACKET_INTERFACE,
RARCH_NETPLAY_CTL_SKIP_NETPLAY_CALLBACKS,
RARCH_NETPLAY_CTL_ALLOW_TIMESKIP
};
/* The current status of a connection */

View File

@ -675,6 +675,9 @@ static uint32_t simple_rand_uint32(unsigned long *simple_rand_next)
return ((part0 << 30) + (part1 << 15) + part2);
}
static void RETRO_CALLCONV netplay_netpacket_send(const void* buf, size_t len,
uint16_t client_id, bool broadcast);
/*
* netplay_init_socket_buffer
*
@ -1876,6 +1879,12 @@ static bool netplay_handshake_pre_sync(netplay_t *netplay,
netplay->next_ping = cpu_features_get_time_usec() + NETPLAY_PING_AFTER;
/* Tell a core that uses the netpacket interface that the client is ready */
if (networking_driver_st.core_netpacket_interface &&
networking_driver_st.core_netpacket_interface->start)
networking_driver_st.core_netpacket_interface->start
((uint16_t)netplay->self_client_num, netplay_netpacket_send);
/* Ask to switch to playing mode if we should */
if (!settings->bools.netplay_start_as_spectator)
return netplay_cmd_mode(netplay, NETPLAY_CONNECTION_PLAYING);
@ -3541,7 +3550,8 @@ static bool netplay_sync_pre_frame(netplay_t *netplay)
bool ret = true;
if (netplay->run_frame_count > 0 && netplay_delta_frame_ready(netplay,
&netplay->buffer[netplay->run_ptr], netplay->run_frame_count))
&netplay->buffer[netplay->run_ptr], netplay->run_frame_count)
&& !networking_driver_st.core_netpacket_interface)
{
/* Don't serialize until it's safe. */
if (!(netplay->quirks & NETPLAY_QUIRK_INITIALIZATION))
@ -3664,6 +3674,10 @@ static void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
{
uint32_t lo_frame_count, hi_frame_count;
/* When a core uses the netpacket interface frames are not synced */
if (networking_driver_st.core_netpacket_interface)
return;
/* Unless we're stalling, we've just finished running a frame */
if (!stalled)
{
@ -3748,8 +3762,9 @@ static void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
#endif
/* Now replay the real input if we've gotten ahead of it */
if (netplay->force_rewind ||
if ((netplay->force_rewind ||
netplay->replay_frame_count < netplay->run_frame_count)
&& !networking_driver_st.core_netpacket_interface)
{
retro_ctx_serialize_info_t serial_info;
@ -4067,6 +4082,12 @@ static void netplay_hangup(netplay_t *netplay,
#endif
}
if (networking_driver_st.core_netpacket_interface
&& was_playing && netplay->is_server
&& networking_driver_st.core_netpacket_interface->disconnected)
networking_driver_st.core_netpacket_interface->disconnected
((uint16_t)(connection - netplay->connections + 1));
RARCH_LOG("[Netplay] %s\n", dmsg);
/* This notification is really only important to the server if the client was playing.
* Let it be optional if server and the client wasn't playing. */
@ -4180,6 +4201,10 @@ static bool send_input_frame(netplay_t *netplay, struct delta_frame *dframe,
buffer[2] = htonl(dframe->frame);
buffer[3] = htonl(client_num);
/* When a core uses the netpacket interface input is not shared */
if (networking_driver_st.core_netpacket_interface)
return true;
/* Add the device data */
devices = netplay->client_devices[client_num];
for (device = 0; device < MAX_INPUT_DEVICES; device++)
@ -4749,7 +4774,10 @@ static void handle_play_spectate(netplay_t *netplay,
if (settings->bools.netplay_allow_slaves)
{
if (settings->bools.netplay_require_slaves)
/* Slave mode unused when core uses netpacket interface */
if (networking_driver_st.core_netpacket_interface)
slave = false;
else if (settings->bools.netplay_require_slaves)
slave = true;
else
slave = (mode & NETPLAY_CMD_PLAY_BIT_SLAVE) ?
@ -4783,6 +4811,11 @@ static void handle_play_spectate(netplay_t *netplay,
announce_play_spectate(netplay, connection->nick,
connection->mode, devices, connection->ping);
if (networking_driver_st.core_netpacket_interface
&& networking_driver_st.core_netpacket_interface->connected)
networking_driver_st.core_netpacket_interface->connected
((uint16_t)(connection - netplay->connections + 1));
}
else
{
@ -5511,7 +5544,9 @@ static bool netplay_get_cmd(netplay_t *netplay,
/* A change to me! */
if (mode & NETPLAY_CMD_MODE_BIT_PLAYING)
{
if (frame != netplay->server_frame_count)
/* When a core uses the netpacket interface this is valid */
if (frame != netplay->server_frame_count
&& !networking_driver_st.core_netpacket_interface)
{
RARCH_ERR("[Netplay] Received mode change out of order.\n");
return netplay_cmd_nak(netplay, connection);
@ -5626,7 +5661,9 @@ static bool netplay_get_cmd(netplay_t *netplay,
/* Somebody else is joining or parting */
if (mode & NETPLAY_CMD_MODE_BIT_PLAYING)
{
if (frame != netplay->server_frame_count)
/* When a core uses the netpacket interface this is valid */
if (frame != netplay->server_frame_count
&& !networking_driver_st.core_netpacket_interface)
{
RARCH_ERR("[Netplay] Received mode change out of order.\n");
return netplay_cmd_nak(netplay, connection);
@ -6126,6 +6163,32 @@ static bool netplay_get_cmd(netplay_t *netplay,
break;
}
case NETPLAY_CMD_NETPACKET:
{
if (!networking_driver_st.core_netpacket_interface)
{
RARCH_ERR("[Netplay] NETPLAY_CMD_NETPACKET while core netpacket interface is not set.\n");
return netplay_cmd_nak(netplay, connection);
}
if (cmd_size > netplay->zbuffer_size)
{
RARCH_ERR("[Netplay] Received netpacket of unexpected size.\n");
return netplay_cmd_nak(netplay, connection);
}
RECV(netplay->zbuffer, cmd_size)
return false;
if (networking_driver_st.core_netpacket_interface->receive)
{
uint16_t client_id = (!netplay->is_server ? (uint16_t)0 :
(uint16_t)(connection - netplay->connections + 1));
networking_driver_st.core_netpacket_interface->receive
(netplay->zbuffer, cmd_size, client_id);
}
break;
}
case NETPLAY_CMD_PLAYER_CHAT:
{
char nickname[NETPLAY_NICK_LEN];
@ -6742,15 +6805,23 @@ static bool netplay_init_socket_buffers(netplay_t *netplay)
static bool netplay_init_serialization(netplay_t *netplay)
{
size_t i, info_size;
size_t i;
if (netplay->state_size)
return true;
info_size = core_serialize_size_special();
if (!info_size)
return false;
netplay->state_size = info_size;
if (networking_driver_st.core_netpacket_interface)
{
/* max core netpacket size is 64 kb, hold 2 of them */
netplay->state_size = 64*1024*2;
}
else
{
size_t info_size = core_serialize_size_special();
if (!info_size)
return false;
netplay->state_size = info_size;
}
for (i = 0; i < netplay->buffer_size; i++)
{
@ -7127,6 +7198,12 @@ static void netplay_frontend_paused(netplay_t *netplay, bool paused)
size_t i;
uint32_t paused_ct = 0;
/* When a core uses the netpacket interface netplay doesn't control pause.
* We need this because even if RARCH_NETPLAY_CTL_ALLOW_PAUSE returns false
* on some platforms the frontend may try to force netplay to pause. */
if (networking_driver_st.core_netpacket_interface)
return;
netplay->local_paused = paused;
/* Communicating this is a bit odd: If exactly one other connection is
@ -7250,6 +7327,10 @@ void netplay_load_savestate(netplay_t *netplay,
{
retro_ctx_serialize_info_t tmp_serial_info = {0};
/* When a core uses the netpacket interface save states are not shared */
if (networking_driver_st.core_netpacket_interface)
return;
if (!serial_info)
save = true;
@ -7581,6 +7662,18 @@ static bool netplay_poll(netplay_t *netplay, bool block_libretro_input)
{
size_t i;
/* Use simplified polling loop when a core uses the netpacket interface. */
if (networking_driver_st.core_netpacket_interface)
{
if (netplay->self_mode == NETPLAY_CONNECTION_NONE)
return true;
netplay_poll_net_input(netplay);
if (networking_driver_st.core_netpacket_interface->poll
&& netplay->self_mode == NETPLAY_CONNECTION_PLAYING)
networking_driver_st.core_netpacket_interface->poll();
return true;
}
if (!get_self_input_state(block_libretro_input, netplay))
goto catastrophe;
@ -8442,6 +8535,10 @@ void deinit_netplay(void)
/* Reinitialize preemptive frames if enabled */
preempt_init(runloop_state_get_ptr());
#endif
if (net_st->core_netpacket_interface
&& net_st->core_netpacket_interface->stop)
net_st->core_netpacket_interface->stop();
}
free(net_st->client_info);
@ -8471,9 +8568,10 @@ bool init_netplay(const char *server, unsigned port, const char *mitm_session)
serialization_quirks = core_serialization_quirks();
if (!core_info_current_supports_netplay() ||
if ((!core_info_current_supports_netplay() ||
serialization_quirks & (RETRO_SERIALIZATION_QUIRK_INCOMPLETE |
RETRO_SERIALIZATION_QUIRK_SINGLE_SESSION))
&& !net_st->core_netpacket_interface)
{
RARCH_ERR("[Netplay] %s\n", msg_hash_to_str(MSG_NETPLAY_UNSUPPORTED));
runloop_msg_queue_push(
@ -8593,6 +8691,11 @@ bool init_netplay(const char *server, unsigned port, const char *mitm_session)
netplay->self_mode = NETPLAY_CONNECTION_INIT;
}
/* Tell a core that uses the netpacket interface that the host is ready */
if (netplay->is_server && net_st->core_netpacket_interface &&
net_st->core_netpacket_interface->start)
net_st->core_netpacket_interface->start(0, netplay_netpacket_send);
return true;
failure:
@ -8748,6 +8851,18 @@ static bool kick_client_by_id_and_name(netplay_t *netplay,
return true;
}
static bool netplay_have_any_active_connection(netplay_t *netplay)
{
size_t i;
if (!netplay || !netplay->is_server)
return (netplay && netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED);
for (i = 0; i < netplay->connections_size; i++)
if ( (netplay->connections[i].flags & NETPLAY_CONN_FLAG_ACTIVE)
&& (netplay->connections[i].mode >= NETPLAY_CONNECTION_CONNECTED))
return true;
return false;
}
/**
* netplay_driver_ctl
*
@ -8915,7 +9030,7 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
break;
case RARCH_NETPLAY_CTL_GAME_WATCH:
if (netplay)
if (netplay && net_st->core_netpacket_interface == NULL)
netplay_toggle_play_spectate(netplay);
else
ret = false;
@ -8929,7 +9044,9 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
break;
case RARCH_NETPLAY_CTL_ALLOW_PAUSE:
ret = (!netplay || netplay->allow_pausing);
ret = (!netplay || netplay->allow_pausing) &&
((net_st->core_netpacket_interface == NULL)
|| !netplay_have_any_active_connection(netplay));
break;
case RARCH_NETPLAY_CTL_PAUSE:
@ -8944,13 +9061,13 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
break;
case RARCH_NETPLAY_CTL_LOAD_SAVESTATE:
if (netplay)
if (netplay && !net_st->core_netpacket_interface)
netplay_load_savestate(netplay,
(retro_ctx_serialize_info_t*)data, true);
break;
case RARCH_NETPLAY_CTL_RESET:
if (netplay)
if (netplay && !net_st->core_netpacket_interface)
netplay_core_reset(netplay);
break;
@ -8967,7 +9084,10 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
break;
case RARCH_NETPLAY_CTL_DESYNC_PUSH:
if (netplay)
if (net_st->core_netpacket_interface
&& netplay_have_any_active_connection(netplay))
ret = false;
else if (netplay)
netplay->desync++;
break;
@ -9027,6 +9147,35 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
ret = false;
break;
case RARCH_NETPLAY_CTL_SET_CORE_PACKET_INTERFACE:
if (net_st->core_netpacket_interface)
{
free(net_st->core_netpacket_interface);
net_st->core_netpacket_interface = NULL;
}
if (data)
{
/* copy passed interface to local state */
net_st->core_netpacket_interface = (struct retro_netpacket_callback*)
malloc(sizeof(*net_st->core_netpacket_interface));
*net_st->core_netpacket_interface = *(struct retro_netpacket_callback*)data;
/* reset savefile dir as core_netpacket_interface affects it */
runloop_path_set_redirect(config_get_ptr(),
dir_get_ptr(RARCH_DIR_SAVEFILE),
dir_get_ptr(RARCH_DIR_CURRENT_SAVESTATE));
}
break;
case RARCH_NETPLAY_CTL_SKIP_NETPLAY_CALLBACKS:
ret = (net_st->core_netpacket_interface != NULL);
break;
case RARCH_NETPLAY_CTL_ALLOW_TIMESKIP:
ret = ((net_st->core_netpacket_interface == NULL)
|| !netplay_have_any_active_connection(netplay));
break;
case RARCH_NETPLAY_CTL_NONE:
default:
ret = false;
@ -9122,6 +9271,44 @@ bool netplay_decode_hostname(const char *hostname,
return true;
}
static void RETRO_CALLCONV netplay_netpacket_send(const void* buf, size_t len,
uint16_t client_id, bool broadcast)
{
net_driver_state_t *net_st = &networking_driver_st;
netplay_t *netplay = net_st->data;
if (!netplay) return;
if (broadcast && netplay->is_server)
{
size_t i, skip = client_id;
for (i = 0; i < netplay->connections_size; i++)
{
struct netplay_connection *connection = &netplay->connections[i];
if (i+1 != skip && (connection->flags & NETPLAY_CONN_FLAG_ACTIVE)
&& (connection->mode == NETPLAY_CONNECTION_PLAYING)
&& !netplay_send_raw_cmd(netplay, connection,
NETPLAY_CMD_NETPACKET, buf, len))
netplay_hangup(netplay, connection);
}
}
else
{
size_t i = client_id - (netplay->is_server ? 1 : 0);
if (i >= netplay->connections_size)
{
RARCH_ERR("[Netplay] Unable to send netpacket to client id %d.\n",
client_id);
return;
}
struct netplay_connection *connection = &netplay->connections[i];
if ( (connection->flags & NETPLAY_CONN_FLAG_ACTIVE)
&& (connection->mode == NETPLAY_CONNECTION_PLAYING)
&& !netplay_send_raw_cmd(netplay, connection,
NETPLAY_CMD_NETPACKET, buf, len))
netplay_hangup(netplay, connection);
}
}
/* Netplay Widgets */
#ifdef HAVE_GFX_WIDGETS

View File

@ -162,6 +162,9 @@ enum netplay_cmd
/* Sends over cheats enabled on client (unsupported) */
NETPLAY_CMD_CHEATS = 0x0047,
/* Send a network packet from the raw packet core interface */
NETPLAY_CMD_NETPACKET = 0x0048,
/* Misc. commands */
/* Sends multiple config requests over,

View File

@ -2528,6 +2528,10 @@ bool command_event(enum event_command cmd, void *data)
runloop_msg_queue_push(msg_hash_to_str(MSG_CHEEVOS_LOAD_STATE_PREVENTED_BY_HARDCORE_MODE), 0, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_WARNING);
return false;
}
#endif
#ifdef HAVE_NETWORKING
if (!netplay_driver_ctl(RARCH_NETPLAY_CTL_ALLOW_TIMESKIP, NULL))
return false;
#endif
if (!command_event_main_state(cmd))
return false;

View File

@ -3484,6 +3484,13 @@ bool runloop_environment_cb(unsigned cmd, void *data)
}
break;
case RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE:
#ifdef HAVE_NETWORKING
RARCH_LOG("[Environ]: RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE.\n");
netplay_driver_ctl(RARCH_NETPLAY_CTL_SET_CORE_PACKET_INTERFACE, data);
#endif
break;
default:
RARCH_LOG("[Environ]: UNSUPPORTED (#%u).\n", cmd);
return false;
@ -4032,6 +4039,9 @@ void runloop_event_deinit_core(void)
#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
runloop_st->runtime_shader_preset_path[0] = '\0';
#endif
#ifdef HAVE_NETWORKING
netplay_driver_ctl(RARCH_NETPLAY_CTL_SET_CORE_PACKET_INTERFACE, NULL);
#endif
}
static bool runloop_path_init_subsystem(runloop_state_t *runloop_st)
@ -6310,6 +6320,12 @@ static enum runloop_state_enum runloop_check_state(
input_st->flags |= INP_FLAG_NONBLOCKING;
}
#ifdef HAVE_NETWORKING
if (check2
&& !netplay_driver_ctl(RARCH_NETPLAY_CTL_ALLOW_TIMESKIP, NULL))
check2 = false;
#endif
if (check2)
{
if (input_st->flags & INP_FLAG_NONBLOCKING)
@ -6408,6 +6424,14 @@ static enum runloop_state_enum runloop_check_state(
runloop_st->flags &= ~RUNLOOP_FLAG_SLOWMOTION;
}
#ifdef HAVE_NETWORKING
if ((runloop_st->flags & RUNLOOP_FLAG_SLOWMOTION)
&& !netplay_driver_ctl(RARCH_NETPLAY_CTL_ALLOW_TIMESKIP, NULL))
{
runloop_st->flags &= ~RUNLOOP_FLAG_SLOWMOTION;
}
#endif
if (runloop_st->flags & RUNLOOP_FLAG_SLOWMOTION)
{
if (settings->uints.video_black_frame_insertion)
@ -7404,6 +7428,9 @@ bool core_set_netplay_callbacks(void)
{
runloop_state_t *runloop_st = &runloop_state;
if (netplay_driver_ctl(RARCH_NETPLAY_CTL_SKIP_NETPLAY_CALLBACKS, NULL))
return true;
/* Force normal poll type for netplay. */
runloop_st->current_core.poll_type = POLL_TYPE_NORMAL;
@ -7983,7 +8010,8 @@ void runloop_path_set_redirect(settings_t *settings,
#ifdef HAVE_NETWORKING
/* Special save directory for netplay clients. */
if ( netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL)
&& !netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_SERVER, NULL))
&& !netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_SERVER, NULL)
&& !netplay_driver_ctl(RARCH_NETPLAY_CTL_SKIP_NETPLAY_CALLBACKS, NULL))
{
fill_pathname_join(new_savefile_dir, new_savefile_dir, ".netplay",
sizeof(new_savefile_dir));

View File

@ -748,8 +748,9 @@ bool state_manager_check_rewind(
{
#ifdef HAVE_NETWORKING
/* Make sure netplay isn't confused */
if (!was_reversed)
netplay_driver_ctl(RARCH_NETPLAY_CTL_DESYNC_PUSH, NULL);
if (!was_reversed
&& !netplay_driver_ctl(RARCH_NETPLAY_CTL_DESYNC_PUSH, NULL))
return false;
#endif
rewind_st->flags |= STATE_MGR_REWIND_ST_FLAG_FRAME_IS_REVERSED;

View File

@ -894,9 +894,7 @@ bool task_push_netplay_content_reload(const char *hostname)
strlcpy(data->hostname, hostname, sizeof(data->hostname));
flags = content_get_flags();
if (flags & CONTENT_ST_FLAG_CORE_DOES_NOT_NEED_CONTENT)
scan_state.state |= STATE_LOAD_CONTENTLESS;
else if (flags & CONTENT_ST_FLAG_IS_INITED)
if (flags & CONTENT_ST_FLAG_IS_INITED)
{
const char *psubsystem = path_get(RARCH_PATH_SUBSYSTEM);
@ -929,6 +927,10 @@ bool task_push_netplay_content_reload(const char *hostname)
}
}
if ((flags & CONTENT_ST_FLAG_CORE_DOES_NOT_NEED_CONTENT) &&
!(scan_state.state & (STATE_LOAD|STATE_LOAD_SUBSYSTEM)))
scan_state.state |= STATE_LOAD_CONTENTLESS;
data->current.core_loaded = true;
scan_state.running = true;