mirror of
https://github.com/libretro/RetroArch
synced 2025-04-07 13:23:32 +00:00
Merge pull request #4647 from GregorR/netplay-reset
Support for reset in netplay
This commit is contained in:
commit
37baccab99
@ -1918,6 +1918,9 @@ bool command_event(enum event_command cmd, void *data)
|
|||||||
core_reset();
|
core_reset();
|
||||||
#ifdef HAVE_CHEEVOS
|
#ifdef HAVE_CHEEVOS
|
||||||
cheevos_reset_game();
|
cheevos_reset_game();
|
||||||
|
#endif
|
||||||
|
#if HAVE_NETWORKING
|
||||||
|
netplay_driver_ctl(RARCH_NETPLAY_CTL_RESET, NULL);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case CMD_EVENT_SAVE_STATE:
|
case CMD_EVENT_SAVE_STATE:
|
||||||
|
@ -316,6 +316,14 @@ Payload:
|
|||||||
Description:
|
Description:
|
||||||
Request that a client stall for the given number of frames.
|
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
|
Command: CHEATS
|
||||||
Unused
|
Unused
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ enum rarch_netplay_ctl_state
|
|||||||
RARCH_NETPLAY_CTL_PAUSE,
|
RARCH_NETPLAY_CTL_PAUSE,
|
||||||
RARCH_NETPLAY_CTL_UNPAUSE,
|
RARCH_NETPLAY_CTL_UNPAUSE,
|
||||||
RARCH_NETPLAY_CTL_LOAD_SAVESTATE,
|
RARCH_NETPLAY_CTL_LOAD_SAVESTATE,
|
||||||
|
RARCH_NETPLAY_CTL_RESET,
|
||||||
RARCH_NETPLAY_CTL_DISCONNECT
|
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_send_savestate
|
||||||
* @netplay : pointer to netplay object
|
* @netplay : pointer to netplay object
|
||||||
@ -808,10 +850,7 @@ void netplay_load_savestate(netplay_t *netplay,
|
|||||||
{
|
{
|
||||||
retro_ctx_serialize_info_t tmp_serial_info;
|
retro_ctx_serialize_info_t tmp_serial_info;
|
||||||
|
|
||||||
/* Wherever we're inputting, that's where we consider our state to be loaded
|
netplay_force_future(netplay);
|
||||||
* (FIXME: Need to be more careful about saving it?) */
|
|
||||||
netplay->run_ptr = netplay->self_ptr;
|
|
||||||
netplay->run_frame_count = netplay->self_frame_count;
|
|
||||||
|
|
||||||
/* Record it in our own buffer */
|
/* Record it in our own buffer */
|
||||||
if (save || !serial_info)
|
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 we can't send it to the peer, loading a state was a bad idea */
|
||||||
if (netplay->quirks & (
|
if (netplay->quirks & (
|
||||||
NETPLAY_QUIRK_NO_SAVESTATES
|
NETPLAY_QUIRK_NO_SAVESTATES
|
||||||
@ -886,6 +897,37 @@ void netplay_load_savestate(netplay_t *netplay,
|
|||||||
&netplay->compress_zlib);
|
&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
|
* 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:
|
case RARCH_NETPLAY_CTL_LOAD_SAVESTATE:
|
||||||
netplay_load_savestate(netplay_data, (retro_ctx_serialize_info_t*)data, true);
|
netplay_load_savestate(netplay_data, (retro_ctx_serialize_info_t*)data, true);
|
||||||
break;
|
break;
|
||||||
|
case RARCH_NETPLAY_CTL_RESET:
|
||||||
|
netplay_core_reset(netplay_data);
|
||||||
|
break;
|
||||||
case RARCH_NETPLAY_CTL_DISCONNECT:
|
case RARCH_NETPLAY_CTL_DISCONNECT:
|
||||||
ret = netplay_disconnect(netplay_data);
|
ret = netplay_disconnect(netplay_data);
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -1022,6 +1022,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case NETPLAY_CMD_LOAD_SAVESTATE:
|
case NETPLAY_CMD_LOAD_SAVESTATE:
|
||||||
|
case NETPLAY_CMD_RESET:
|
||||||
{
|
{
|
||||||
uint32_t frame;
|
uint32_t frame;
|
||||||
uint32_t isize;
|
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
|
* (strangely) force a rewind to the frame we're already on, so it
|
||||||
* gets loaded. This is just to avoid having reloading implemented in
|
* gets loaded. This is just to avoid having reloading implemented in
|
||||||
* too many places. */
|
* 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");
|
RARCH_ERR("CMD_LOAD_SAVESTATE received an unexpected payload size.\n");
|
||||||
return netplay_cmd_nak(netplay, connection);
|
return netplay_cmd_nak(netplay, connection);
|
||||||
@ -1097,44 +1102,58 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||||||
goto shrt;
|
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");
|
RECV(&isize, sizeof(isize))
|
||||||
return netplay_cmd_nak(netplay, connection);
|
{
|
||||||
}
|
RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive inflated size.\n");
|
||||||
isize = ntohl(isize);
|
return netplay_cmd_nak(netplay, connection);
|
||||||
|
}
|
||||||
|
isize = ntohl(isize);
|
||||||
|
|
||||||
if (isize != netplay->state_size)
|
if (isize != netplay->state_size)
|
||||||
{
|
{
|
||||||
RARCH_ERR("CMD_LOAD_SAVESTATE received an unexpected save state size.\n");
|
RARCH_ERR("CMD_LOAD_SAVESTATE received an unexpected save state size.\n");
|
||||||
return netplay_cmd_nak(netplay, connection);
|
return netplay_cmd_nak(netplay, connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
RECV(netplay->zbuffer, cmd_size - 2*sizeof(uint32_t))
|
RECV(netplay->zbuffer, cmd_size - 2*sizeof(uint32_t))
|
||||||
{
|
{
|
||||||
RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive savestate.\n");
|
RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive savestate.\n");
|
||||||
return netplay_cmd_nak(netplay, connection);
|
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 */
|
/* 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
|
/* This is squirrely: We need to assure that when we advance the
|
||||||
* frame in post_frame, THEN we're referring to the frame to
|
* 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 */
|
/* Make sure our states are correct */
|
||||||
netplay->force_rewind = true;
|
|
||||||
netplay->savestate_request_outstanding = false;
|
netplay->savestate_request_outstanding = false;
|
||||||
netplay->other_ptr = netplay->read_ptr[connection->player];
|
netplay->other_ptr = netplay->read_ptr[connection->player];
|
||||||
netplay->other_frame_count = frame;
|
netplay->other_frame_count = frame;
|
||||||
|
@ -153,8 +153,11 @@ enum netplay_cmd
|
|||||||
/* Request that a client stall because it's running fast */
|
/* Request that a client stall because it's running fast */
|
||||||
NETPLAY_CMD_STALL = 0x0045,
|
NETPLAY_CMD_STALL = 0x0045,
|
||||||
|
|
||||||
|
/* Request a core reset */
|
||||||
|
NETPLAY_CMD_RESET = 0x0046,
|
||||||
|
|
||||||
/* Sends over cheats enabled on client (unsupported) */
|
/* Sends over cheats enabled on client (unsupported) */
|
||||||
NETPLAY_CMD_CHEATS = 0x0046,
|
NETPLAY_CMD_CHEATS = 0x0047,
|
||||||
|
|
||||||
/* Misc. commands */
|
/* Misc. commands */
|
||||||
|
|
||||||
@ -404,6 +407,9 @@ struct netplay
|
|||||||
* events, such as player flipping or savestate loading. */
|
* events, such as player flipping or savestate loading. */
|
||||||
bool force_rewind;
|
bool force_rewind;
|
||||||
|
|
||||||
|
/* Force a reset */
|
||||||
|
bool force_reset;
|
||||||
|
|
||||||
/* Quirks in the savestate implementation */
|
/* Quirks in the savestate implementation */
|
||||||
uint64_t quirks;
|
uint64_t quirks;
|
||||||
|
|
||||||
|
@ -396,6 +396,13 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Reset if it was requested */
|
||||||
|
if (netplay->force_reset)
|
||||||
|
{
|
||||||
|
core_reset();
|
||||||
|
netplay->force_reset = false;
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef DEBUG_NONDETERMINISTIC_CORES
|
#ifndef DEBUG_NONDETERMINISTIC_CORES
|
||||||
if (!netplay->force_rewind)
|
if (!netplay->force_rewind)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user