(cheevos) include achievement state in netplay states (#17416)

* add achievement data to netplay save state

* honor achievement state from netplay server

* keep processing achievements if menu doesn't pause game

* remove unused variable

* only CRC coremem

* force send savestate on join and hardcore change

* allow hardcore enablement to be synced to clients

* still calculate cheevos_size for non-server

* use appropriate buffer

* optimizations for when achievements are disabled

* support interfacing with older protocols

* formatting

* c89
This commit is contained in:
Jamiras 2025-01-16 12:00:02 -07:00 committed by GitHub
parent beceb88cd7
commit 0113226936
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 419 additions and 90 deletions

View File

@ -87,7 +87,6 @@ static rcheevos_locals_t rcheevos_locals =
{{0}},/* memory */
#ifdef HAVE_THREADS
CMD_EVENT_NONE, /* queued_command */
false, /* game_placard_requested */
#endif
"", /* user_agent_prefix */
"", /* user_agent_core */
@ -112,8 +111,8 @@ rcheevos_locals_t* get_rcheevos_locals(void)
Supporting functions.
*****************************************************************************/
#define CMD_CHEEVOS_NON_COMMAND -1
static void rcheevos_show_game_placard(void);
#define CMD_CHEEVOS_FINALIZE_LOAD -1
static void rcheevos_finalize_game_load_on_ui_thread(void);
#ifndef CHEEVOS_VERBOSE
void rcheevos_log(const char* fmt, ...)
@ -686,7 +685,6 @@ bool rcheevos_unload(void)
#ifdef HAVE_THREADS
rcheevos_locals.queued_command = CMD_EVENT_NONE;
rcheevos_locals.game_placard_requested = false;
#endif
if (rcheevos_locals.memory.count > 0)
@ -827,6 +825,11 @@ static void rcheevos_toggle_hardcore_active(rcheevos_locals_t* locals)
command_event(CMD_EVENT_REWIND_INIT, NULL);
}
}
#ifdef HAVE_NETWORKING
/* force sending a savestate to clients so they'll drop out of hardcore too */
netplay_force_send_savestate();
#endif
}
void rcheevos_toggle_hardcore_paused(void)
@ -978,16 +981,12 @@ void rcheevos_test(void)
#ifdef HAVE_THREADS
if (rcheevos_locals.queued_command != CMD_EVENT_NONE)
{
if ((int)rcheevos_locals.queued_command != CMD_CHEEVOS_NON_COMMAND)
if ((int)rcheevos_locals.queued_command == CMD_CHEEVOS_FINALIZE_LOAD)
rcheevos_finalize_game_load_on_ui_thread();
else
command_event(rcheevos_locals.queued_command, NULL);
rcheevos_locals.queued_command = CMD_EVENT_NONE;
if (rcheevos_locals.game_placard_requested)
{
rcheevos_locals.game_placard_requested = false;
rcheevos_show_game_placard();
}
}
#endif
@ -1382,6 +1381,25 @@ static void rcheevos_finalize_game_load(rc_client_t* client)
}
}
static void rcheevos_finalize_game_load_on_ui_thread(void)
{
rcheevos_show_game_placard();
#if HAVE_REWIND
if (!rcheevos_hardcore_active())
{
const settings_t* settings = config_get_ptr();
/* Re-enable rewind. Additional space will be allocated for the achievement state data */
if (settings->bools.rewind_enable)
command_event(CMD_EVENT_REWIND_REINIT, NULL);
}
#endif
#ifdef HAVE_NETWORKING
netplay_reinit_serialization();
#endif
}
static void rcheevos_client_load_game_callback(int result,
const char* error_message, rc_client_t* client, void* userdata)
{
@ -1446,17 +1464,6 @@ static void rcheevos_client_load_game_callback(int result,
rc_client_set_read_memory_function(client, rcheevos_client_read_memory);
}
#ifdef HAVE_THREADS
if (!video_driver_is_threaded() && !task_is_on_main_thread())
{
/* have to "schedule" this. game image should not be loaded on background thread */
rcheevos_locals.queued_command = CMD_CHEEVOS_NON_COMMAND;
rcheevos_locals.game_placard_requested = true;
}
else
#endif
rcheevos_show_game_placard();
rcheevos_finalize_game_load(client);
if (rcheevos_hardcore_active())
@ -1466,27 +1473,18 @@ static void rcheevos_client_load_game_callback(int result,
rcheevos_validate_config_settings();
rcheevos_enforce_hardcore_settings();
}
else
{
#if HAVE_REWIND
/* Re-enable rewind. Additional space will be allocated for the achievement state data */
if (settings->bools.rewind_enable)
{
#ifdef HAVE_THREADS
if (!task_is_on_main_thread())
{
/* Have to "schedule" this. CMD_EVENT_REWIND_REINIT should
* only be called on the main thread */
rcheevos_locals.queued_command = CMD_EVENT_REWIND_REINIT;
}
else
#endif
command_event(CMD_EVENT_REWIND_REINIT, NULL);
}
#endif
}
rcheevos_spectating_changed(); /* synchronize spectating state */
#ifdef HAVE_THREADS
if (!task_is_on_main_thread())
{
/* have to "schedule" this. game image should not be loaded into memory on background thread */
rcheevos_locals.queued_command = CMD_CHEEVOS_FINALIZE_LOAD;
}
else
#endif
rcheevos_finalize_game_load_on_ui_thread();
}
static rc_clock_t rcheevos_client_get_time_millisecs(const rc_client_t* client)

View File

@ -86,7 +86,6 @@ typedef struct rcheevos_locals_t
#ifdef HAVE_THREADS
enum event_command queued_command; /* action queued by background thread to be run on main thread */
bool game_placard_requested; /* request to display game placard */
#endif
char user_agent_prefix[128]; /* RetroArch/OS version information */

View File

@ -15583,6 +15583,14 @@ MSG_HASH(
MSG_CHEEVOS_HARDCORE_MODE_DISABLED_CHEAT,
"A cheat was activated. Achievements Hardcore Mode disabled for the current session."
)
MSG_HASH(
MSG_CHEEVOS_HARDCORE_MODE_CHANGED_BY_HOST,
"Achievements Hardcore Mode changed by host."
)
MSG_HASH(
MSG_CHEEVOS_HARDCORE_MODE_REQUIRES_NEWER_HOST,
"Netplay host needs to be updated. Achievements Hardcore Mode disabled for current session."
)
MSG_HASH(
MSG_CHEEVOS_MASTERED_GAME,
"Mastered %s"

View File

@ -4097,6 +4097,8 @@ enum msg_hash_enums
MSG_CHEEVOS_LOAD_STATE_PREVENTED_BY_HARDCORE_MODE,
MSG_CHEEVOS_HARDCORE_MODE_DISABLED,
MSG_CHEEVOS_HARDCORE_MODE_DISABLED_CHEAT,
MSG_CHEEVOS_HARDCORE_MODE_CHANGED_BY_HOST,
MSG_CHEEVOS_HARDCORE_MODE_REQUIRES_NEWER_HOST,
MSG_CHEEVOS_MASTERED_GAME,
MSG_CHEEVOS_COMPLETED_GAME,
MSG_CHEEVOS_HARDCORE_MODE_ENABLE,

View File

@ -195,7 +195,9 @@ void deinit_netplay(void);
bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data);
bool netplay_reinit_serialization(void);
bool netplay_is_spectating(void);
void netplay_force_send_savestate(void);
#ifdef HAVE_NETPLAYDISCOVERY
/** Initialize Netplay discovery */

View File

@ -216,6 +216,16 @@ net_driver_state_t *networking_state_get_ptr(void)
return &networking_driver_st;
}
static bool netplay_build_savestate(netplay_t* netplay, retro_ctx_serialize_info_t* serial_info, bool force_capture_achievements);
static bool netplay_process_savestate(netplay_t* netplay, retro_ctx_serialize_info_t* serial_info);
/* Align to 8-byte boundary */
#define CONTENT_ALIGN_SIZE(size) ((((size) + 7) & ~7))
#define NETPLAYSTATE_VERSION 1
#define NETPLAYSTATE_MEM_BLOCK "MEM "
#define NETPLAYSTATE_CHEEVOS_BLOCK "ACHV"
#define NETPLAYSTATE_END_BLOCK "END "
#ifdef HAVE_NETPLAYDISCOVERY
/** Initialize Netplay discovery (client) */
bool init_netplay_discovery(void)
@ -2099,6 +2109,31 @@ bool netplay_delta_frame_ready(netplay_t *netplay, struct delta_frame *delta,
return true;
}
static const uint8_t* netplay_get_savestate_coremem(netplay_t* netplay, const uint8_t* input)
{
/* If the container header is detected, find the coremem block */
if (memcmp(input, "NETPLAY", 7) == 0)
{
const uint8_t* stop = input + netplay->state_size;
input += 8; /* NETPLAY# */
while (input < stop)
{
size_t block_size = (input[7] << 24 | input[6] << 16 | input[5] << 8 | input[4]);
const uint8_t* marker = input;
input += 8;
if (memcmp(marker, NETPLAYSTATE_MEM_BLOCK, 4) == 0)
break;
input += CONTENT_ALIGN_SIZE(block_size);
}
}
return input;
}
/**
* netplay_delta_frame_crc
*
@ -2107,9 +2142,13 @@ bool netplay_delta_frame_ready(netplay_t *netplay, struct delta_frame *delta,
static uint32_t netplay_delta_frame_crc(netplay_t *netplay,
struct delta_frame *delta)
{
const uint8_t* input;
NETPLAY_ASSERT_MODUS(NETPLAY_MODUS_INPUT_FRAME_SYNC);
return encoding_crc32(0L, (const unsigned char*)delta->state,
netplay->state_size);
input = netplay_get_savestate_coremem(netplay,
(const uint8_t*)delta->state);
return encoding_crc32(0L, input, netplay->coremem_size);
}
/*
@ -3657,11 +3696,9 @@ static bool netplay_sync_pre_frame(netplay_t *netplay)
if (!(netplay->quirks & NETPLAY_QUIRK_INITIALIZATION))
{
retro_ctx_serialize_info_t serial_info = {0};
serial_info.data = netplay->buffer[netplay->run_ptr].state;
serial_info.size = netplay->state_size;
memset(serial_info.data, 0, serial_info.size);
if (core_serialize_special(&serial_info))
if (netplay_build_savestate(netplay, &serial_info, false))
{
if (netplay->force_send_savestate && !netplay->stall &&
!netplay->remote_paused)
@ -3678,9 +3715,6 @@ static bool netplay_sync_pre_frame(netplay_t *netplay)
}
/* Send this along to the other side. */
serial_info.data_const =
netplay->buffer[netplay->run_ptr].state;
netplay_load_savestate(netplay, &serial_info, false);
netplay->force_send_savestate = false;
@ -3892,7 +3926,7 @@ static void netplay_sync_input_post_frame(netplay_t *netplay, bool stalled)
serial_info.data = NULL;
serial_info.data_const = netplay->buffer[netplay->replay_ptr].state;
serial_info.size = netplay->state_size;
if (!core_unserialize_special(&serial_info))
if (!netplay_process_savestate(netplay, &serial_info))
RARCH_ERR("[Netplay] Netplay savestate loading failed: Prepare for desync!\n");
while (netplay->replay_frame_count < netplay->run_frame_count)
@ -3908,7 +3942,7 @@ static void netplay_sync_input_post_frame(netplay_t *netplay, bool stalled)
/* Remember the current state */
memset(serial_info.data, 0, serial_info.size);
core_serialize_special(&serial_info);
netplay_build_savestate(netplay, &serial_info, true);
if (netplay->replay_frame_count < netplay->unread_frame_count)
netplay_handle_frame_hash(netplay, ptr);
@ -4670,6 +4704,9 @@ static void netplay_announce_play_spectate(netplay_t *netplay,
#ifdef HAVE_CHEEVOS
rcheevos_spectating_changed();
if (!netplay->is_server && !netplay_is_spectating()) /* force sync of achievement state */
netplay_cmd_request_savestate(netplay);
#endif
RARCH_LOG("[Netplay] %s\n", _msg);
@ -6041,8 +6078,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
state_size = ntohl(state_size);
state_size_raw = cmd_size - (sizeof(frame) + sizeof(state_size));
if (state_size != netplay->state_size ||
state_size_raw > netplay->zbuffer_size)
if (state_size_raw > netplay->zbuffer_size)
{
RARCH_ERR("[Netplay] Netplay state load with an unexpected save state size.\n");
return netplay_cmd_nak(netplay, connection);
@ -6061,6 +6097,18 @@ static bool netplay_get_cmd(netplay_t *netplay,
break;
}
if (state_size > netplay->state_size)
{
/* other client state size is larger than ours, grow ours */
netplay->state_size = state_size;
for (i = 0; i < netplay->buffer_size; i++)
{
netplay->buffer[i].state = realloc(netplay->buffer[i].state, netplay->state_size);
if (!netplay->buffer[i].state)
return false;
}
}
ctrans->decompression_backend->set_in(
ctrans->decompression_stream,
netplay->zbuffer, state_size_raw);
@ -6071,6 +6119,28 @@ static bool netplay_get_cmd(netplay_t *netplay,
ctrans->decompression_stream,
true, &rd, &wn, NULL);
if (memcmp(netplay->buffer[load_ptr].state, "NETPLAY", 7) != 0)
{
if (state_size != netplay->coremem_size)
{
RARCH_ERR("[Netplay] Netplay state load with an unexpected save state size.\n");
return netplay_cmd_nak(netplay, connection);
}
#ifdef HAVE_CHEEVOS
/* did not receive a protocol 7 packet. server isn't sending achievement data. disable hardcore */
if (!netplay->is_server && rcheevos_hardcore_active() && !netplay_is_spectating())
{
const char* msg = msg_hash_to_str(MSG_CHEEVOS_HARDCORE_MODE_REQUIRES_NEWER_HOST);
runloop_msg_queue_push(msg, strlen(msg), 0, 180, true, NULL,
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
RARCH_WARN("[Netplay] Server did not send achievement information.\n", msg);
rcheevos_pause_hardcore();
}
#endif
}
/* Force a rewind to the relevant frame. */
netplay->force_rewind = true;
@ -6984,6 +7054,15 @@ static bool netplay_init_socket_buffers(netplay_t *netplay)
return true;
}
static void netplay_write_block_header(unsigned char* output, const char* header, size_t size)
{
memcpy(output, header, 4);
output[4] = ((size) & 0xFF);
output[5] = ((size >> 8) & 0xFF);
output[6] = ((size >> 16) & 0xFF);
output[7] = ((size >> 24) & 0xFF);
}
static bool netplay_init_serialization(netplay_t *netplay)
{
size_t i;
@ -7001,6 +7080,31 @@ static bool netplay_init_serialization(netplay_t *netplay)
size_t info_size = core_serialize_size_special();
if (!info_size)
return false;
netplay->coremem_size = info_size;
/* 8-byte identifier, 8-byte block header, content, 8-byte terminator */
info_size = 8 + 8 + CONTENT_ALIGN_SIZE(info_size) + 8;
#ifdef HAVE_CHEEVOS
{
const settings_t* settings = config_get_ptr();
if (settings->bools.cheevos_enable)
{
/* 8-byte flags + content */
netplay->cheevos_size = 8 + rcheevos_get_serialize_size();
}
else
{
/* just 8-byte flags */
netplay->cheevos_size = 8;
}
/* 8-byte block header + content */
info_size += 8 + CONTENT_ALIGN_SIZE(netplay->cheevos_size);
}
#endif
netplay->state_size = info_size;
}
@ -7042,7 +7146,7 @@ static bool netplay_try_init_serialization(netplay_t *netplay)
/* Check if we can actually save. */
serial_info.data_const = NULL;
serial_info.data = netplay->buffer[netplay->run_ptr].state;
serial_info.size = netplay->state_size;
serial_info.size = netplay->coremem_size;
if (!core_serialize_special(&serial_info))
return false;
@ -7327,11 +7431,12 @@ failure:
*/
static void netplay_send_savestate(netplay_t *netplay,
retro_ctx_serialize_info_t *serial_info, uint32_t cx,
struct compression_transcoder *z)
struct compression_transcoder *z, bool is_legacy_data)
{
uint32_t header[4];
uint32_t rd, wn;
size_t i;
bool has_legacy_connection = false;
/* Compress it */
z->compression_backend->set_in(z->compression_stream,
@ -7355,19 +7460,48 @@ static void netplay_send_savestate(netplay_t *netplay,
for (i = 0; i < netplay->connections_size; i++)
{
struct netplay_connection *connection = &netplay->connections[i];
if ( (!(connection->flags & NETPLAY_CONN_FLAG_ACTIVE))
|| (connection->mode < NETPLAY_CONNECTION_CONNECTED)
|| (connection->compression_supported != cx))
continue;
struct netplay_connection* connection = &netplay->connections[i];
bool can_send;
if ( !netplay_send(&connection->send_packet_buffer,
connection->fd, header,
sizeof(header))
|| !netplay_send(&connection->send_packet_buffer,
connection->fd,
netplay->zbuffer, wn))
netplay_hangup(netplay, connection);
/* if is_legacy_data is false, only send to peers on protocol 7 or higher */
REQUIRE_PROTOCOL_VERSION(connection, 7)
can_send = !is_legacy_data;
else
can_send = is_legacy_data;
if (can_send)
{
if ( (!(connection->flags & NETPLAY_CONN_FLAG_ACTIVE))
|| (connection->mode < NETPLAY_CONNECTION_CONNECTED)
|| (connection->compression_supported != cx))
continue;
if ( !netplay_send(&connection->send_packet_buffer,
connection->fd, header, sizeof(header))
|| !netplay_send(&connection->send_packet_buffer,
connection->fd, netplay->zbuffer, wn))
netplay_hangup(netplay, connection);
}
else
{
has_legacy_connection = true;
}
}
if (has_legacy_connection && !is_legacy_data)
{
/* at least one peer is not on protocol 7 or higher. extract the coremem segment
* and only send it. */
const uint8_t* input = netplay_get_savestate_coremem(netplay,
(const uint8_t*)serial_info->data_const);
if (input != serial_info->data_const)
{
serial_info->data_const = input;
serial_info->size = netplay->coremem_size;
netplay_send_savestate(netplay, serial_info, cx, z, true);
}
}
}
@ -7507,6 +7641,143 @@ static void netplay_core_reset(netplay_t *netplay)
}
}
static bool netplay_process_savestate1(retro_ctx_serialize_info_t* serial_info)
{
#ifdef HAVE_CHEEVOS
const settings_t* settings = config_get_ptr();
#endif
const uint8_t* input = serial_info->data_const;
const uint8_t* stop = input + serial_info->size;
bool seen_core = false;
input += 8; /* NETPLAY1 */
while (input < stop)
{
size_t block_size = (input[7] << 24 | input[6] << 16 | input[5] << 8 | input[4]);
const uint8_t *marker = input;
input += 8;
if (memcmp(marker, NETPLAYSTATE_MEM_BLOCK, 4) == 0)
{
retro_ctx_serialize_info_t serial_info;
serial_info.data_const = (void*)input;
serial_info.size = block_size;
if (!core_unserialize_special(&serial_info))
return false;
seen_core = true;
}
#ifdef HAVE_CHEEVOS
else if (memcmp(marker, NETPLAYSTATE_CHEEVOS_BLOCK, 4) == 0 && settings->bools.cheevos_enable)
{
const bool hardcore_state = (input[0] != 0);
if (hardcore_state != rcheevos_hardcore_active() && !netplay_is_spectating())
{
const char *msg = msg_hash_to_str(MSG_CHEEVOS_HARDCORE_MODE_CHANGED_BY_HOST);
runloop_msg_queue_push(msg, strlen(msg), 0, 180, true, NULL,
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
if (!hardcore_state)
{
/* use pause_hardcore to ensure player can't re-enabled it on client side */
rcheevos_pause_hardcore();
}
else
{
/* if the player had hardcore on, this will reset it to on. otherwise, it'll
* stay off, even if the server is playing with hardcore restrictions now. we
* don't want to force hardcore unlocks for a player that doesn't want them. */
rcheevos_hardcore_enabled_changed();
}
}
if (block_size > 8)
{
input += 8;
rcheevos_set_serialized_data((void*)input);
}
}
#endif
input += CONTENT_ALIGN_SIZE(block_size);
}
if (!seen_core)
return false;
return true;
}
static bool netplay_process_savestate(netplay_t* netplay, retro_ctx_serialize_info_t* serial_info)
{
/* if no NETPLAY marker, it's just raw core data */
if (memcmp(serial_info->data_const, "NETPLAY", 7) != 0)
{
serial_info->size = netplay->coremem_size;
return core_unserialize_special(serial_info);
}
switch (((uint8_t*)serial_info->data_const)[7])
{
case 1:
return netplay_process_savestate1(serial_info);
default:
return false;
}
}
static bool netplay_build_savestate(netplay_t* netplay, retro_ctx_serialize_info_t* serial_info, bool force_capture_achievements)
{
uint8_t* buffer = (uint8_t*)serial_info->data;
uint8_t* output = buffer;
memcpy(output, "NETPLAY", 7);
output[7] = NETPLAYSTATE_VERSION;
output += 8;
/* important - write the unaligned size - some cores fail if they aren't passed the exact right size. */
netplay_write_block_header(output, NETPLAYSTATE_MEM_BLOCK, netplay->coremem_size);
output += 8;
/* capture the core state */
serial_info->data = output;
serial_info->size = netplay->coremem_size;
if (!core_serialize_special(serial_info))
return false;
output += CONTENT_ALIGN_SIZE(netplay->coremem_size);
#ifdef HAVE_CHEEVOS
if (netplay->is_server || (force_capture_achievements && netplay->cheevos_size > 8))
{
const settings_t* settings = config_get_ptr();
size_t cheevos_size = 8;
if (settings->bools.cheevos_enable)
{
if (netplay->cheevos_size > 8 && rcheevos_get_serialized_data(output + 16))
cheevos_size = netplay->cheevos_size;
}
netplay_write_block_header(output, NETPLAYSTATE_CHEEVOS_BLOCK, cheevos_size);
output += 8;
memset(output, 0, 8);
output[0] = rcheevos_hardcore_active() ? 1 : 0;
output += cheevos_size;
}
#endif
netplay_write_block_header(output, NETPLAYSTATE_END_BLOCK, 0);
output += 8;
serial_info->data_const = serial_info->data = buffer;
serial_info->size = (output - buffer);
return true;
}
void netplay_load_savestate(netplay_t *netplay,
retro_ctx_serialize_info_t *serial_info, bool save)
{
@ -7528,11 +7799,9 @@ void netplay_load_savestate(netplay_t *netplay,
if (!serial_info)
{
tmp_serial_info.data = netplay->buffer[netplay->run_ptr].state;
tmp_serial_info.size = netplay->state_size;
if (!core_serialize_special(&tmp_serial_info))
tmp_serial_info.data = netplay->buffer[netplay->run_ptr].state;
if (!netplay_build_savestate(netplay, &tmp_serial_info, false))
return;
tmp_serial_info.data_const = tmp_serial_info.data;
serial_info = &tmp_serial_info;
}
else if (serial_info->size <= netplay->state_size)
@ -7546,10 +7815,10 @@ void netplay_load_savestate(netplay_t *netplay,
/* Send this to every peer. */
if (netplay->compress_nil.compression_backend)
netplay_send_savestate(netplay, serial_info, 0,
&netplay->compress_nil);
&netplay->compress_nil, false);
if (netplay->compress_zlib.compression_backend)
netplay_send_savestate(netplay, serial_info, NETPLAY_COMPRESSION_ZLIB,
&netplay->compress_zlib);
&netplay->compress_zlib, false);
}
}
@ -9074,6 +9343,43 @@ bool netplay_is_spectating(void)
return (netplay && (netplay->self_mode == NETPLAY_CONNECTION_SPECTATING));
}
void netplay_force_send_savestate(void)
{
net_driver_state_t* net_st = &networking_driver_st;
netplay_t* netplay = net_st->data;
if (netplay && netplay->is_server)
netplay->force_send_savestate = true;
}
bool netplay_reinit_serialization(void)
{
net_driver_state_t* net_st = &networking_driver_st;
netplay_t* netplay = net_st->data;
size_t i;
if (!netplay)
return true;
netplay->state_size = 0;
/* netplay_init_serialization rebuilds the delta states and zbuffer, but
* nothing else, so we have to free them directly */
for (i = 0; i < netplay->buffer_size; i++)
{
free(netplay->buffer[i].state);
netplay->buffer[i].state = NULL;
}
if (netplay->zbuffer)
{
free(netplay->zbuffer);
netplay->zbuffer = NULL;
}
return netplay_init_serialization(netplay);
}
/**
* netplay_driver_ctl
*

View File

@ -527,8 +527,12 @@ struct netplay
size_t zbuffer_size;
/* The size of our packet buffers */
size_t packet_buffer_size;
/* Size of savestates */
/* Size of savestates (coremem_size + cheevos_size + headers) */
size_t state_size;
size_t coremem_size; /* core_serialize_special_size() */
#ifdef HAVE_CHEEVOS
size_t cheevos_size; /* rcheevos_get_serialize_size() */
#endif
/* The frame we're currently inputting */
size_t self_ptr;

View File

@ -19,7 +19,7 @@
#define __RARCH_NETPLAY_PROTOCOL_H
#define LOW_NETPLAY_PROTOCOL_VERSION 5
#define HIGH_NETPLAY_PROTOCOL_VERSION 6
#define HIGH_NETPLAY_PROTOCOL_VERSION 7
#define NETPLAY_PROTOCOL_VERSION HIGH_NETPLAY_PROTOCOL_VERSION

View File

@ -5428,6 +5428,22 @@ static void runloop_pause_toggle(
command_event(CMD_EVENT_PAUSE, NULL);
}
static bool runloop_is_libretro_running(runloop_state_t* runloop_st, settings_t* settings)
{
const bool runloop_is_inited = (runloop_st->flags & RUNLOOP_FLAG_IS_INITED) ? true : false;
#ifdef HAVE_NETWORKING
const bool menu_pause_libretro = settings->bools.menu_pause_libretro
&& netplay_driver_ctl(RARCH_NETPLAY_CTL_ALLOW_PAUSE, NULL);
#else
const bool menu_pause_libretro = settings->bools.menu_pause_libretro;
#endif
return runloop_is_inited
&& !(runloop_st->flags & RUNLOOP_FLAG_PAUSED)
&& (!menu_pause_libretro
&& runloop_st->flags & RUNLOOP_FLAG_CORE_RUNNING);
}
static enum runloop_state_enum runloop_check_state(
bool error_on_init,
settings_t *settings,
@ -5963,18 +5979,7 @@ static enum runloop_state_enum runloop_check_state(
if (focused || !(runloop_st->flags & RUNLOOP_FLAG_IDLE))
{
bool runloop_is_inited = (runloop_st->flags & RUNLOOP_FLAG_IS_INITED) ? true : false;
#ifdef HAVE_NETWORKING
bool menu_pause_libretro = settings->bools.menu_pause_libretro
&& netplay_driver_ctl(RARCH_NETPLAY_CTL_ALLOW_PAUSE, NULL);
#else
bool menu_pause_libretro = settings->bools.menu_pause_libretro;
#endif
bool libretro_running =
runloop_is_inited
&& !(runloop_st->flags & RUNLOOP_FLAG_PAUSED)
&& ( !menu_pause_libretro
&& runloop_st->flags & RUNLOOP_FLAG_CORE_RUNNING);
const bool libretro_running = runloop_is_libretro_running(runloop_st, settings);
if (menu)
{
@ -6967,7 +6972,12 @@ int runloop_iterate(void)
#endif
#ifdef HAVE_CHEEVOS
if (cheevos_enable)
rcheevos_idle();
{
if (runloop_is_libretro_running(runloop_st, settings))
rcheevos_test();
else
rcheevos_idle();
}
#endif
#ifdef HAVE_MENU
/* Rely on vsync throttling unless VRR is enabled and menu throttle is disabled. */