mirror of
https://github.com/libretro/RetroArch
synced 2025-04-17 02:43:03 +00:00
(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:
parent
beceb88cd7
commit
0113226936
@ -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)
|
||||
|
@ -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 */
|
||||
|
@ -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"
|
||||
|
@ -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,
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
36
runloop.c
36
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. */
|
||||
|
Loading…
x
Reference in New Issue
Block a user