mirror of
https://github.com/libretro/RetroArch
synced 2025-03-03 04:14:00 +00:00
Merge pull request #3700 from GregorR/netplay-serialization-quirks
Serialization quirks
This commit is contained in:
commit
46ab267f40
4
core.h
4
core.h
@ -178,6 +178,10 @@ bool core_set_environment(retro_ctx_environ_info_t *info);
|
||||
|
||||
bool core_serialize_size(retro_ctx_size_info_t *info);
|
||||
|
||||
uint64_t core_serialization_quirks(void);
|
||||
|
||||
void core_set_serialization_quirks(uint64_t quirks);
|
||||
|
||||
bool core_serialize(retro_ctx_serialize_info_t *info);
|
||||
|
||||
bool core_unserialize(retro_ctx_serialize_info_t *info);
|
||||
|
11
core_impl.c
11
core_impl.c
@ -52,6 +52,7 @@ static bool core_game_loaded = false;
|
||||
static bool core_input_polled = false;
|
||||
static bool core_has_set_input_descriptors = false;
|
||||
static struct retro_callbacks retro_ctx;
|
||||
static uint64_t core_serialization_quirks_v = 0;
|
||||
|
||||
static void core_input_state_poll_maybe(void)
|
||||
{
|
||||
@ -308,6 +309,16 @@ bool core_serialize_size(retro_ctx_size_info_t *info)
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t core_serialization_quirks(void)
|
||||
{
|
||||
return core_serialization_quirks_v;
|
||||
}
|
||||
|
||||
void core_set_serialization_quirks(uint64_t quirks)
|
||||
{
|
||||
core_serialization_quirks_v = quirks;
|
||||
}
|
||||
|
||||
bool core_frame(retro_ctx_frame_info_t *info)
|
||||
{
|
||||
if (!info || !retro_ctx.frame_cb)
|
||||
|
@ -1608,6 +1608,13 @@ bool rarch_environment_cb(unsigned cmd, void *data)
|
||||
break;
|
||||
}
|
||||
|
||||
case RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS:
|
||||
{
|
||||
uint64_t *quirks = (uint64_t *) data;
|
||||
core_set_serialization_quirks(*quirks);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Default */
|
||||
default:
|
||||
RARCH_LOG("Environ UNSUPPORTED (#%u).\n", cmd);
|
||||
|
@ -976,6 +976,36 @@ struct retro_hw_render_context_negotiation_interface
|
||||
* so it will be used after SET_HW_RENDER, but before the context_reset callback.
|
||||
*/
|
||||
|
||||
/* Serialized state is incomplete in some way. Set if serialization is
|
||||
* usable in typical end-user cases but should not be relied upon to
|
||||
* implement frame-sensitive frontend features such as netplay or
|
||||
* rerecording. */
|
||||
#define RETRO_SERIALIZATION_QUIRK_INCOMPLETE (1 << 0)
|
||||
/* The core must spend some time initializing before serialization is
|
||||
* supported. */
|
||||
#define RETRO_SERIALIZATION_QUIRK_MUST_INITIALIZE (1 << 1)
|
||||
/* Serialization size may change within a session. */
|
||||
#define RETRO_SERIALIZATION_QUIRK_CORE_VARIABLE_SIZE (1 << 2)
|
||||
/* Set by the frontend to acknowledge that it supports variable-sized
|
||||
* states. */
|
||||
#define RETRO_SERIALIZATION_QUIRK_FRONT_VARIABLE_SIZE (1 << 3)
|
||||
/* Serialized state can only be loaded during the same session. */
|
||||
#define RETRO_SERIALIZATION_QUIRK_SINGLE_SESSION (1 << 4)
|
||||
/* Serialized state cannot be loaded on an architecture with a different
|
||||
* endianness from the one it was saved on. */
|
||||
#define RETRO_SERIALIZATION_QUIRK_ENDIAN_DEPENDENT (1 << 5)
|
||||
/* Serialized state cannot be loaded on a different platform from the one it
|
||||
* was saved on for reasons other than endianness, such as word size
|
||||
* dependence */
|
||||
#define RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT (1 << 6)
|
||||
|
||||
#define RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS 44
|
||||
/* uint64_t * --
|
||||
* Sets quirk flags associated with serialization. The frontend will zero any flags it doesn't
|
||||
* recognize or support. Should be set in either retro_init or retro_load_game, but not both.
|
||||
*/
|
||||
|
||||
|
||||
#define RETRO_MEMDESC_CONST (1 << 0) /* The frontend will never change this memory area once retro_load_game has returned. */
|
||||
#define RETRO_MEMDESC_BIGENDIAN (1 << 1) /* The memory area contains big endian data. Default is little endian. */
|
||||
#define RETRO_MEMDESC_ALIGN_2 (1 << 16) /* All memory access in this area is aligned to their own size, or 2, whichever is smaller. */
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#include "netplay_private.h"
|
||||
|
||||
#include "../../autosave.h"
|
||||
#include "../../configuration.h"
|
||||
#include "../../command.h"
|
||||
#include "../../movie.h"
|
||||
@ -556,6 +557,23 @@ static bool netplay_get_cmd(netplay_t *netplay)
|
||||
{
|
||||
uint32_t frame;
|
||||
|
||||
/* Make sure we're ready for it */
|
||||
if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION)
|
||||
{
|
||||
if (!netplay->is_replay)
|
||||
{
|
||||
netplay->is_replay = true;
|
||||
netplay->replay_ptr = netplay->self_ptr;
|
||||
netplay->replay_frame_count = netplay->self_frame_count;
|
||||
netplay_wait_and_init_serialization(netplay);
|
||||
netplay->is_replay = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
netplay_wait_and_init_serialization(netplay);
|
||||
}
|
||||
}
|
||||
|
||||
/* There is a subtlty in whether the load comes before or after the
|
||||
* current frame:
|
||||
*
|
||||
@ -729,9 +747,11 @@ static bool netplay_poll(void)
|
||||
|
||||
/* Read Netplay input, block if we're configured to stall for input every
|
||||
* frame */
|
||||
res = poll_input(netplay_data,
|
||||
(netplay_data->stall_frames == 0)
|
||||
&& (netplay_data->read_frame_count <= netplay_data->self_frame_count));
|
||||
if (netplay_data->stall_frames == 0 &&
|
||||
netplay_data->read_frame_count <= netplay_data->self_frame_count)
|
||||
res = poll_input(netplay_data, true);
|
||||
else
|
||||
res = poll_input(netplay_data, false);
|
||||
if (res == -1)
|
||||
{
|
||||
hangup(netplay_data);
|
||||
@ -970,6 +990,82 @@ void netplay_log_connection(const struct sockaddr_storage *their_addr,
|
||||
|
||||
|
||||
|
||||
bool netplay_try_init_serialization(netplay_t *netplay)
|
||||
{
|
||||
retro_ctx_serialize_info_t serial_info;
|
||||
|
||||
if (netplay->state_size)
|
||||
return true;
|
||||
|
||||
if (!netplay_init_serialization(netplay))
|
||||
return false;
|
||||
|
||||
/* Check if we can actually save */
|
||||
serial_info.data_const = NULL;
|
||||
serial_info.data = netplay->buffer[netplay->self_ptr].state;
|
||||
serial_info.size = netplay->state_size;
|
||||
|
||||
if (!core_serialize(&serial_info))
|
||||
return false;
|
||||
|
||||
/* Once initialized, we no longer exhibit this quirk */
|
||||
netplay->quirks &= ~((uint64_t) NETPLAY_QUIRK_INITIALIZATION);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool netplay_wait_and_init_serialization(netplay_t *netplay)
|
||||
{
|
||||
int frame;
|
||||
|
||||
if (netplay->state_size)
|
||||
return true;
|
||||
|
||||
/* Wait a maximum of 60 frames */
|
||||
for (frame = 0; frame < 60; frame++) {
|
||||
if (netplay_try_init_serialization(netplay))
|
||||
return true;
|
||||
|
||||
#if defined(HAVE_THREADS)
|
||||
autosave_lock();
|
||||
#endif
|
||||
core_run();
|
||||
#if defined(HAVE_THREADS)
|
||||
autosave_unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool netplay_init_serialization(netplay_t *netplay)
|
||||
{
|
||||
unsigned i;
|
||||
retro_ctx_size_info_t info;
|
||||
|
||||
if (netplay->state_size)
|
||||
return true;
|
||||
|
||||
core_serialize_size(&info);
|
||||
|
||||
if (!info.size)
|
||||
return false;
|
||||
|
||||
netplay->state_size = info.size;
|
||||
|
||||
for (i = 0; i < netplay->buffer_size; i++)
|
||||
{
|
||||
netplay->buffer[i].state = calloc(netplay->state_size, 1);
|
||||
|
||||
if (!netplay->buffer[i].state)
|
||||
{
|
||||
netplay->quirks |= NETPLAY_QUIRK_NO_SAVESTATES;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool netplay_init_buffers(netplay_t *netplay, unsigned frames)
|
||||
{
|
||||
@ -988,25 +1084,8 @@ static bool netplay_init_buffers(netplay_t *netplay, unsigned frames)
|
||||
if (!netplay->buffer)
|
||||
return false;
|
||||
|
||||
{
|
||||
unsigned i;
|
||||
retro_ctx_size_info_t info;
|
||||
|
||||
core_serialize_size(&info);
|
||||
|
||||
netplay->state_size = info.size;
|
||||
|
||||
for (i = 0; i < netplay->buffer_size; i++)
|
||||
{
|
||||
netplay->buffer[i].state = calloc(netplay->state_size, 1);
|
||||
|
||||
if (!netplay->buffer[i].state)
|
||||
{
|
||||
netplay->savestates_work = false;
|
||||
netplay->stall_frames = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(netplay->quirks & NETPLAY_QUIRK_INITIALIZATION))
|
||||
netplay_init_serialization(netplay);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1020,6 +1099,7 @@ static bool netplay_init_buffers(netplay_t *netplay, unsigned frames)
|
||||
* @cb : Libretro callbacks.
|
||||
* @spectate : If true, enable spectator mode.
|
||||
* @nick : Nickname of user.
|
||||
* @quirks : Netplay quirks required for this session.
|
||||
*
|
||||
* Creates a new netplay handle. A NULL host means we're
|
||||
* hosting (user 1).
|
||||
@ -1028,7 +1108,7 @@ static bool netplay_init_buffers(netplay_t *netplay, unsigned frames)
|
||||
**/
|
||||
netplay_t *netplay_new(const char *server, uint16_t port,
|
||||
unsigned frames, unsigned check_frames, const struct retro_callbacks *cb,
|
||||
bool spectate, const char *nick)
|
||||
bool spectate, const char *nick, uint64_t quirks)
|
||||
{
|
||||
netplay_t *netplay = (netplay_t*)calloc(1, sizeof(*netplay));
|
||||
if (!netplay)
|
||||
@ -1040,10 +1120,10 @@ netplay_t *netplay_new(const char *server, uint16_t port,
|
||||
netplay->port = server ? 0 : 1;
|
||||
netplay->spectate.enabled = spectate;
|
||||
netplay->is_server = server == NULL;
|
||||
netplay->savestates_work = true;
|
||||
strlcpy(netplay->nick, nick, sizeof(netplay->nick));
|
||||
netplay->stall_frames = frames;
|
||||
netplay->check_frames = check_frames;
|
||||
netplay->quirks = quirks;
|
||||
|
||||
if (!netplay_init_buffers(netplay, frames))
|
||||
{
|
||||
@ -1205,6 +1285,12 @@ bool netplay_pre_frame(netplay_t *netplay)
|
||||
if (netplay->local_paused)
|
||||
netplay_frontend_paused(netplay, false);
|
||||
|
||||
if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION)
|
||||
{
|
||||
/* Are we ready now? */
|
||||
netplay_try_init_serialization(netplay);
|
||||
}
|
||||
|
||||
if (!netplay->net_cbs->pre_frame(netplay))
|
||||
return false;
|
||||
|
||||
@ -1298,6 +1384,10 @@ void netplay_load_savestate(netplay_t *netplay, retro_ctx_serialize_info_t *seri
|
||||
netplay->other_frame_count = netplay->self_frame_count;
|
||||
}
|
||||
|
||||
/* If we can't send it to the peer, loading a state was a bad idea */
|
||||
if (netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION))
|
||||
return;
|
||||
|
||||
/* And send it to the peer (FIXME: this is an ugly way to do this) */
|
||||
header[0] = htonl(NETPLAY_CMD_LOAD_SAVESTATE);
|
||||
header[1] = htonl(serial_info->size + sizeof(uint32_t));
|
||||
@ -1353,6 +1443,8 @@ bool init_netplay(bool is_spectate, const char *server, unsigned port)
|
||||
{
|
||||
struct retro_callbacks cbs = {0};
|
||||
settings_t *settings = config_get_ptr();
|
||||
uint64_t serialization_quirks = 0;
|
||||
uint64_t quirks = 0;
|
||||
|
||||
if (!netplay_enabled)
|
||||
return false;
|
||||
@ -1366,6 +1458,20 @@ bool init_netplay(bool is_spectate, const char *server, unsigned port)
|
||||
|
||||
core_set_default_callbacks(&cbs);
|
||||
|
||||
/* Map the core's quirks to our quirks */
|
||||
serialization_quirks = core_serialization_quirks();
|
||||
if (serialization_quirks & ~((uint64_t) NETPLAY_QUIRK_MAP_UNDERSTOOD))
|
||||
{
|
||||
/* Quirks we don't support! Just disable everything. */
|
||||
quirks |= NETPLAY_QUIRK_NO_SAVESTATES;
|
||||
}
|
||||
if (serialization_quirks & NETPLAY_QUIRK_MAP_NO_SAVESTATES)
|
||||
quirks |= NETPLAY_QUIRK_NO_SAVESTATES;
|
||||
if (serialization_quirks & NETPLAY_QUIRK_MAP_NO_TRANSMISSION)
|
||||
quirks |= NETPLAY_QUIRK_NO_TRANSMISSION;
|
||||
if (serialization_quirks & NETPLAY_QUIRK_MAP_INITIALIZATION)
|
||||
quirks |= NETPLAY_QUIRK_INITIALIZATION;
|
||||
|
||||
if (netplay_is_client)
|
||||
{
|
||||
RARCH_LOG("Connecting to netplay host...\n");
|
||||
@ -1382,7 +1488,7 @@ bool init_netplay(bool is_spectate, const char *server, unsigned port)
|
||||
netplay_is_client ? server : NULL,
|
||||
port ? port : RARCH_DEFAULT_PORT,
|
||||
settings->netplay.sync_frames, settings->netplay.check_frames, &cbs,
|
||||
is_spectate, settings->username);
|
||||
is_spectate, settings->username, quirks);
|
||||
|
||||
if (netplay_data)
|
||||
return true;
|
||||
|
@ -139,6 +139,7 @@ size_t audio_sample_batch_net(const int16_t *data, size_t frames);
|
||||
* @cb : Libretro callbacks.
|
||||
* @spectate : If true, enable spectator mode.
|
||||
* @nick : Nickname of user.
|
||||
* @quirks : Netplay quirks.
|
||||
*
|
||||
* Creates a new netplay handle. A NULL host means we're
|
||||
* hosting (user 1).
|
||||
@ -148,7 +149,7 @@ size_t audio_sample_batch_net(const int16_t *data, size_t frames);
|
||||
netplay_t *netplay_new(const char *server,
|
||||
uint16_t port, unsigned frames, unsigned check_frames,
|
||||
const struct retro_callbacks *cb, bool spectate,
|
||||
const char *nick);
|
||||
const char *nick, uint64_t quirks);
|
||||
|
||||
/**
|
||||
* netplay_free:
|
||||
|
@ -74,15 +74,18 @@ static bool netplay_net_pre_frame(netplay_t *netplay)
|
||||
{
|
||||
retro_ctx_serialize_info_t serial_info;
|
||||
|
||||
if (netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->self_ptr], netplay->self_frame_count) &&
|
||||
netplay->self_frame_count > 0)
|
||||
if (netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->self_ptr], netplay->self_frame_count))
|
||||
{
|
||||
serial_info.data_const = NULL;
|
||||
serial_info.data = netplay->buffer[netplay->self_ptr].state;
|
||||
serial_info.size = netplay->state_size;
|
||||
|
||||
memset(serial_info.data, 0, serial_info.size);
|
||||
if (netplay->savestates_work && core_serialize(&serial_info))
|
||||
if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION)
|
||||
{
|
||||
/* Don't serialize until it's safe */
|
||||
}
|
||||
else if (!(netplay->quirks & NETPLAY_QUIRK_NO_SAVESTATES) && core_serialize(&serial_info))
|
||||
{
|
||||
if (netplay->force_send_savestate && !netplay->stall)
|
||||
{
|
||||
@ -96,11 +99,14 @@ static bool netplay_net_pre_frame(netplay_t *netplay)
|
||||
{
|
||||
/* If the core can't serialize properly, we must stall for the
|
||||
* remote input on EVERY frame, because we can't recover */
|
||||
netplay->savestates_work = false;
|
||||
netplay->quirks |= NETPLAY_QUIRK_NO_SAVESTATES;
|
||||
netplay->stall_frames = 0;
|
||||
if (!netplay->has_connection)
|
||||
netplay->stall = RARCH_NETPLAY_STALL_NO_CONNECTION;
|
||||
}
|
||||
|
||||
/* If we can't transmit savestates, we must stall until the client is ready */
|
||||
if (!netplay->has_connection &&
|
||||
(netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION)))
|
||||
netplay->stall = RARCH_NETPLAY_STALL_NO_CONNECTION;
|
||||
}
|
||||
|
||||
if (netplay->is_server && !netplay->has_connection)
|
||||
@ -148,10 +154,8 @@ static bool netplay_net_pre_frame(netplay_t *netplay)
|
||||
netplay->has_connection = true;
|
||||
|
||||
/* Send them the savestate */
|
||||
if (netplay->savestates_work)
|
||||
{
|
||||
if (!(netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION)))
|
||||
netplay_load_savestate(netplay, NULL, true);
|
||||
}
|
||||
|
||||
/* And expect the current frame from the other side */
|
||||
netplay->read_frame_count = netplay->other_frame_count = netplay->self_frame_count;
|
||||
@ -230,6 +234,10 @@ static void netplay_net_post_frame(netplay_t *netplay)
|
||||
netplay->replay_ptr = netplay->other_ptr;
|
||||
netplay->replay_frame_count = netplay->other_frame_count;
|
||||
|
||||
if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION)
|
||||
/* Make sure we're initialized before we start loading things */
|
||||
netplay_wait_and_init_serialization(netplay);
|
||||
|
||||
serial_info.data = NULL;
|
||||
serial_info.data_const = netplay->buffer[netplay->replay_ptr].state;
|
||||
serial_info.size = netplay->state_size;
|
||||
|
@ -40,6 +40,28 @@
|
||||
#define PREV_PTR(x) ((x) == 0 ? netplay->buffer_size - 1 : (x) - 1)
|
||||
#define NEXT_PTR(x) ((x + 1) % netplay->buffer_size)
|
||||
|
||||
/* Quirks mandated by how particular cores save states. This is distilled from
|
||||
* the larger set of quirks that the quirks environment can communicate. */
|
||||
#define NETPLAY_QUIRK_NO_SAVESTATES (1<<0)
|
||||
#define NETPLAY_QUIRK_NO_TRANSMISSION (1<<1)
|
||||
#define NETPLAY_QUIRK_INITIALIZATION (1<<2)
|
||||
|
||||
/* Mapping of serialization quirks to netplay quirks. */
|
||||
#define NETPLAY_QUIRK_MAP_UNDERSTOOD \
|
||||
(RETRO_SERIALIZATION_QUIRK_INCOMPLETE \
|
||||
|RETRO_SERIALIZATION_QUIRK_MUST_INITIALIZE \
|
||||
|RETRO_SERIALIZATION_QUIRK_SINGLE_SESSION \
|
||||
|RETRO_SERIALIZATION_QUIRK_ENDIAN_DEPENDENT \
|
||||
|RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT)
|
||||
#define NETPLAY_QUIRK_MAP_NO_SAVESTATES \
|
||||
(RETRO_SERIALIZATION_QUIRK_INCOMPLETE)
|
||||
#define NETPLAY_QUIRK_MAP_NO_TRANSMISSION \
|
||||
(RETRO_SERIALIZATION_QUIRK_SINGLE_SESSION \
|
||||
|RETRO_SERIALIZATION_QUIRK_ENDIAN_DEPENDENT \
|
||||
|RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT)
|
||||
#define NETPLAY_QUIRK_MAP_INITIALIZATION \
|
||||
(RETRO_SERIALIZATION_QUIRK_MUST_INITIALIZE)
|
||||
|
||||
struct delta_frame
|
||||
{
|
||||
bool used; /* a bit derpy, but this is how we know if the delta's been used at all */
|
||||
@ -118,8 +140,8 @@ struct netplay
|
||||
* events, such as player flipping or savestate loading. */
|
||||
bool force_rewind;
|
||||
|
||||
/* Does the core support savestates? */
|
||||
bool savestates_work;
|
||||
/* Quirks in the savestate implementation */
|
||||
uint64_t quirks;
|
||||
|
||||
/* Force our state to be sent to the other side. Used when they request a
|
||||
* savestate, to send at the next pre-frame. */
|
||||
@ -177,6 +199,12 @@ struct netplay_callbacks* netplay_get_cbs_net(void);
|
||||
|
||||
struct netplay_callbacks* netplay_get_cbs_spectate(void);
|
||||
|
||||
/* Normally called at init time, unless the INITIALIZATION quirk is set */
|
||||
bool netplay_init_serialization(netplay_t *netplay);
|
||||
|
||||
/* Force serialization to be ready by fast-forwarding the core */
|
||||
bool netplay_wait_and_init_serialization(netplay_t *netplay);
|
||||
|
||||
void netplay_simulate_input(netplay_t *netplay, uint32_t sim_ptr);
|
||||
|
||||
void netplay_log_connection(const struct sockaddr_storage *their_addr,
|
||||
|
@ -111,6 +111,16 @@ static bool netplay_spectate_pre_frame(netplay_t *netplay)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Wait until it's safe to serialize */
|
||||
if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION)
|
||||
{
|
||||
netplay->is_replay = true;
|
||||
netplay->replay_ptr = netplay->self_ptr;
|
||||
netplay->replay_frame_count = netplay->self_frame_count;
|
||||
netplay_wait_and_init_serialization(netplay);
|
||||
netplay->is_replay = false;
|
||||
}
|
||||
|
||||
/* Start them at the current frame */
|
||||
netplay->spectate.frames[idx] = netplay->self_frame_count;
|
||||
serial_info.data_const = NULL;
|
||||
@ -197,6 +207,10 @@ static void netplay_spectate_post_frame(netplay_t *netplay)
|
||||
netplay->replay_ptr = netplay->other_ptr;
|
||||
netplay->replay_frame_count = netplay->other_frame_count;
|
||||
|
||||
/* Wait until it's safe to serialize */
|
||||
if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION)
|
||||
netplay_wait_and_init_serialization(netplay);
|
||||
|
||||
serial_info.data = NULL;
|
||||
serial_info.data_const = netplay->buffer[netplay->replay_ptr].state;
|
||||
serial_info.size = netplay->state_size;
|
||||
|
Loading…
x
Reference in New Issue
Block a user