diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c index a71b0ca5c77..68acaaac56b 100644 --- a/cheevos/cheevos.c +++ b/cheevos/cheevos.c @@ -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) diff --git a/cheevos/cheevos_locals.h b/cheevos/cheevos_locals.h index 16907a5faca..74caf86e462 100644 --- a/cheevos/cheevos_locals.h +++ b/cheevos/cheevos_locals.h @@ -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 */ diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 98922cc165f..7c2788a2afa 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -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" diff --git a/msg_hash.h b/msg_hash.h index 4eaf9aac660..eddaa01f258 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -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, diff --git a/network/netplay/netplay.h b/network/netplay/netplay.h index ea027f5f8fa..3c1b90da776 100644 --- a/network/netplay/netplay.h +++ b/network/netplay/netplay.h @@ -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 */ diff --git a/network/netplay/netplay_frontend.c b/network/netplay/netplay_frontend.c index b12a324014e..cd99527d52a 100644 --- a/network/netplay/netplay_frontend.c +++ b/network/netplay/netplay_frontend.c @@ -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 @@ static netplay_t *netplay_new(const char *server, const char *mitm, */ 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 * diff --git a/network/netplay/netplay_private.h b/network/netplay/netplay_private.h index 3ab7a517685..187822da3a4 100644 --- a/network/netplay/netplay_private.h +++ b/network/netplay/netplay_private.h @@ -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; diff --git a/network/netplay/netplay_protocol.h b/network/netplay/netplay_protocol.h index 7583a2e2561..608fca18b77 100644 --- a/network/netplay/netplay_protocol.h +++ b/network/netplay/netplay_protocol.h @@ -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 diff --git a/runloop.c b/runloop.c index a8079f9fe3b..84bba27423a 100644 --- a/runloop.c +++ b/runloop.c @@ -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. */