mirror of
https://github.com/libretro/RetroArch
synced 2025-03-02 19:13:34 +00:00
Support for reset in netplay
This patch transfers core_reset across netplay. Resets effectively worked before thanks to check_frames, but this makes resets work even without check_frames, and in particular should allow resets to force sync in savestateless cores, bringing them one step closer to actually being usable by non-experts.
This commit is contained in:
parent
d0b29f3d8e
commit
4c1abfaa71
@ -1918,6 +1918,9 @@ bool command_event(enum event_command cmd, void *data)
|
||||
core_reset();
|
||||
#ifdef HAVE_CHEEVOS
|
||||
cheevos_reset_game();
|
||||
#endif
|
||||
#if HAVE_NETWORKING
|
||||
netplay_driver_ctl(RARCH_NETPLAY_CTL_RESET, NULL);
|
||||
#endif
|
||||
break;
|
||||
case CMD_EVENT_SAVE_STATE:
|
||||
|
@ -316,6 +316,14 @@ Payload:
|
||||
Description:
|
||||
Request that a client stall for the given number of frames.
|
||||
|
||||
Command: RESET
|
||||
Payload:
|
||||
{
|
||||
frame: uint32
|
||||
}
|
||||
Description:
|
||||
Indicate that the core was reset at the beginning of the given frame.
|
||||
|
||||
Command: CHEATS
|
||||
Unused
|
||||
|
||||
|
@ -44,6 +44,7 @@ enum rarch_netplay_ctl_state
|
||||
RARCH_NETPLAY_CTL_PAUSE,
|
||||
RARCH_NETPLAY_CTL_UNPAUSE,
|
||||
RARCH_NETPLAY_CTL_LOAD_SAVESTATE,
|
||||
RARCH_NETPLAY_CTL_RESET,
|
||||
RARCH_NETPLAY_CTL_DISCONNECT
|
||||
};
|
||||
|
||||
|
@ -740,6 +740,48 @@ void netplay_post_frame(netplay_t *netplay)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_force_future
|
||||
* @netplay : pointer to netplay object
|
||||
*
|
||||
* Force netplay to ignore all past input, typically because we've just loaded
|
||||
* a state or reset.
|
||||
*/
|
||||
static void netplay_force_future(netplay_t *netplay)
|
||||
{
|
||||
/* Wherever we're inputting, that's where we consider our state to be loaded */
|
||||
netplay->run_ptr = netplay->self_ptr;
|
||||
netplay->run_frame_count = netplay->self_frame_count;
|
||||
|
||||
/* We need to ignore any intervening data from the other side,
|
||||
* and never rewind past this */
|
||||
netplay_update_unread_ptr(netplay);
|
||||
if (netplay->unread_frame_count < netplay->run_frame_count)
|
||||
{
|
||||
uint32_t player;
|
||||
for (player = 0; player < MAX_USERS; player++)
|
||||
{
|
||||
if (!(netplay->connected_players & (1<<player))) continue;
|
||||
if (netplay->read_frame_count[player] < netplay->run_frame_count)
|
||||
{
|
||||
netplay->read_ptr[player] = netplay->run_ptr;
|
||||
netplay->read_frame_count[player] = netplay->run_frame_count;
|
||||
}
|
||||
}
|
||||
if (netplay->server_frame_count < netplay->run_frame_count)
|
||||
{
|
||||
netplay->server_ptr = netplay->run_ptr;
|
||||
netplay->server_frame_count = netplay->run_frame_count;
|
||||
}
|
||||
netplay_update_unread_ptr(netplay);
|
||||
}
|
||||
if (netplay->other_frame_count < netplay->run_frame_count)
|
||||
{
|
||||
netplay->other_ptr = netplay->run_ptr;
|
||||
netplay->other_frame_count = netplay->run_frame_count;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_send_savestate
|
||||
* @netplay : pointer to netplay object
|
||||
@ -808,10 +850,7 @@ void netplay_load_savestate(netplay_t *netplay,
|
||||
{
|
||||
retro_ctx_serialize_info_t tmp_serial_info;
|
||||
|
||||
/* Wherever we're inputting, that's where we consider our state to be loaded
|
||||
* (FIXME: Need to be more careful about saving it?) */
|
||||
netplay->run_ptr = netplay->self_ptr;
|
||||
netplay->run_frame_count = netplay->self_frame_count;
|
||||
netplay_force_future(netplay);
|
||||
|
||||
/* Record it in our own buffer */
|
||||
if (save || !serial_info)
|
||||
@ -844,34 +883,6 @@ void netplay_load_savestate(netplay_t *netplay,
|
||||
}
|
||||
}
|
||||
|
||||
/* We need to ignore any intervening data from the other side,
|
||||
* and never rewind past this */
|
||||
netplay_update_unread_ptr(netplay);
|
||||
if (netplay->unread_frame_count < netplay->run_frame_count)
|
||||
{
|
||||
uint32_t player;
|
||||
for (player = 0; player < MAX_USERS; player++)
|
||||
{
|
||||
if (!(netplay->connected_players & (1<<player))) continue;
|
||||
if (netplay->read_frame_count[player] < netplay->run_frame_count)
|
||||
{
|
||||
netplay->read_ptr[player] = netplay->run_ptr;
|
||||
netplay->read_frame_count[player] = netplay->run_frame_count;
|
||||
}
|
||||
}
|
||||
if (netplay->server_frame_count < netplay->run_frame_count)
|
||||
{
|
||||
netplay->server_ptr = netplay->run_ptr;
|
||||
netplay->server_frame_count = netplay->run_frame_count;
|
||||
}
|
||||
netplay_update_unread_ptr(netplay);
|
||||
}
|
||||
if (netplay->other_frame_count < netplay->run_frame_count)
|
||||
{
|
||||
netplay->other_ptr = netplay->run_ptr;
|
||||
netplay->other_frame_count = netplay->run_frame_count;
|
||||
}
|
||||
|
||||
/* If we can't send it to the peer, loading a state was a bad idea */
|
||||
if (netplay->quirks & (
|
||||
NETPLAY_QUIRK_NO_SAVESTATES
|
||||
@ -886,6 +897,37 @@ void netplay_load_savestate(netplay_t *netplay,
|
||||
&netplay->compress_zlib);
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_core_reset
|
||||
* @netplay : pointer to netplay object
|
||||
*
|
||||
* Indicate that the core has been reset to netplay peers
|
||||
**/
|
||||
static void netplay_core_reset(netplay_t *netplay)
|
||||
{
|
||||
uint32_t cmd[3];
|
||||
size_t i;
|
||||
|
||||
/* Ignore past input */
|
||||
netplay_force_future(netplay);
|
||||
|
||||
/* Request that our peers reset */
|
||||
cmd[0] = htonl(NETPLAY_CMD_RESET);
|
||||
cmd[1] = htonl(sizeof(uint32_t));
|
||||
cmd[2] = htonl(netplay->self_frame_count);
|
||||
|
||||
for (i = 0; i < netplay->connections_size; i++)
|
||||
{
|
||||
struct netplay_connection *connection = &netplay->connections[i];
|
||||
if (!connection->active ||
|
||||
connection->mode < NETPLAY_CONNECTION_CONNECTED) continue;
|
||||
|
||||
if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd,
|
||||
sizeof(cmd)))
|
||||
netplay_hangup(netplay, connection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_toggle_play_spectate
|
||||
*
|
||||
@ -1145,6 +1187,9 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
|
||||
case RARCH_NETPLAY_CTL_LOAD_SAVESTATE:
|
||||
netplay_load_savestate(netplay_data, (retro_ctx_serialize_info_t*)data, true);
|
||||
break;
|
||||
case RARCH_NETPLAY_CTL_RESET:
|
||||
netplay_core_reset(netplay_data);
|
||||
break;
|
||||
case RARCH_NETPLAY_CTL_DISCONNECT:
|
||||
ret = netplay_disconnect(netplay_data);
|
||||
goto done;
|
||||
|
@ -1022,6 +1022,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
break;
|
||||
|
||||
case NETPLAY_CMD_LOAD_SAVESTATE:
|
||||
case NETPLAY_CMD_RESET:
|
||||
{
|
||||
uint32_t frame;
|
||||
uint32_t isize;
|
||||
@ -1071,7 +1072,11 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
* (strangely) force a rewind to the frame we're already on, so it
|
||||
* gets loaded. This is just to avoid having reloading implemented in
|
||||
* too many places. */
|
||||
if (cmd_size > netplay->zbuffer_size + 2*sizeof(uint32_t))
|
||||
|
||||
/* Check the payload size */
|
||||
if ((cmd == NETPLAY_CMD_LOAD_SAVESTATE &&
|
||||
(cmd_size < 2*sizeof(uint32_t) || cmd_size > netplay->zbuffer_size + 2*sizeof(uint32_t))) ||
|
||||
(cmd == NETPLAY_CMD_RESET && cmd_size != sizeof(uint32_t)))
|
||||
{
|
||||
RARCH_ERR("CMD_LOAD_SAVESTATE received an unexpected payload size.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
@ -1097,44 +1102,58 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
goto shrt;
|
||||
}
|
||||
|
||||
RECV(&isize, sizeof(isize))
|
||||
/* Now we switch based on whether we're loading a state or resetting */
|
||||
if (cmd == NETPLAY_CMD_LOAD_SAVESTATE)
|
||||
{
|
||||
RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive inflated size.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
isize = ntohl(isize);
|
||||
RECV(&isize, sizeof(isize))
|
||||
{
|
||||
RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive inflated size.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
isize = ntohl(isize);
|
||||
|
||||
if (isize != netplay->state_size)
|
||||
{
|
||||
RARCH_ERR("CMD_LOAD_SAVESTATE received an unexpected save state size.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
if (isize != netplay->state_size)
|
||||
{
|
||||
RARCH_ERR("CMD_LOAD_SAVESTATE received an unexpected save state size.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
RECV(netplay->zbuffer, cmd_size - 2*sizeof(uint32_t))
|
||||
{
|
||||
RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive savestate.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
RECV(netplay->zbuffer, cmd_size - 2*sizeof(uint32_t))
|
||||
{
|
||||
RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive savestate.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
/* And decompress it */
|
||||
switch (connection->compression_supported)
|
||||
{
|
||||
case NETPLAY_COMPRESSION_ZLIB:
|
||||
ctrans = &netplay->compress_zlib;
|
||||
break;
|
||||
default:
|
||||
ctrans = &netplay->compress_nil;
|
||||
}
|
||||
ctrans->decompression_backend->set_in(ctrans->decompression_stream,
|
||||
netplay->zbuffer, cmd_size - 2*sizeof(uint32_t));
|
||||
ctrans->decompression_backend->set_out(ctrans->decompression_stream,
|
||||
(uint8_t*)netplay->buffer[netplay->read_ptr[connection->player]].state,
|
||||
netplay->state_size);
|
||||
ctrans->decompression_backend->trans(ctrans->decompression_stream,
|
||||
true, &rd, &wn, NULL);
|
||||
|
||||
/* Force a rewind to the relevant frame */
|
||||
netplay->force_rewind = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Resetting */
|
||||
netplay->force_reset = true;
|
||||
|
||||
/* And decompress it */
|
||||
switch (connection->compression_supported)
|
||||
{
|
||||
case NETPLAY_COMPRESSION_ZLIB:
|
||||
ctrans = &netplay->compress_zlib;
|
||||
break;
|
||||
default:
|
||||
ctrans = &netplay->compress_nil;
|
||||
}
|
||||
ctrans->decompression_backend->set_in(ctrans->decompression_stream,
|
||||
netplay->zbuffer, cmd_size - 2*sizeof(uint32_t));
|
||||
ctrans->decompression_backend->set_out(ctrans->decompression_stream,
|
||||
(uint8_t*)netplay->buffer[netplay->read_ptr[connection->player]].state,
|
||||
netplay->state_size);
|
||||
ctrans->decompression_backend->trans(ctrans->decompression_stream,
|
||||
true, &rd, &wn, NULL);
|
||||
|
||||
/* Skip ahead if it's past where we are */
|
||||
if (frame > netplay->run_frame_count)
|
||||
if (frame > netplay->run_frame_count ||
|
||||
cmd == NETPLAY_CMD_RESET)
|
||||
{
|
||||
/* This is squirrely: We need to assure that when we advance the
|
||||
* frame in post_frame, THEN we're referring to the frame to
|
||||
@ -1161,8 +1180,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
}
|
||||
}
|
||||
|
||||
/* And force rewind to it */
|
||||
netplay->force_rewind = true;
|
||||
/* Make sure our states are correct */
|
||||
netplay->savestate_request_outstanding = false;
|
||||
netplay->other_ptr = netplay->read_ptr[connection->player];
|
||||
netplay->other_frame_count = frame;
|
||||
|
@ -153,8 +153,11 @@ enum netplay_cmd
|
||||
/* Request that a client stall because it's running fast */
|
||||
NETPLAY_CMD_STALL = 0x0045,
|
||||
|
||||
/* Request a core reset */
|
||||
NETPLAY_CMD_RESET = 0x0046,
|
||||
|
||||
/* Sends over cheats enabled on client (unsupported) */
|
||||
NETPLAY_CMD_CHEATS = 0x0046,
|
||||
NETPLAY_CMD_CHEATS = 0x0047,
|
||||
|
||||
/* Misc. commands */
|
||||
|
||||
@ -404,6 +407,9 @@ struct netplay
|
||||
* events, such as player flipping or savestate loading. */
|
||||
bool force_rewind;
|
||||
|
||||
/* Force a reset */
|
||||
bool force_reset;
|
||||
|
||||
/* Quirks in the savestate implementation */
|
||||
uint64_t quirks;
|
||||
|
||||
|
@ -396,6 +396,13 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reset if it was requested */
|
||||
if (netplay->force_reset)
|
||||
{
|
||||
core_reset();
|
||||
netplay->force_reset = false;
|
||||
}
|
||||
|
||||
#ifndef DEBUG_NONDETERMINISTIC_CORES
|
||||
if (!netplay->force_rewind)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user