From f665881d6d4d96c4cc8486ccec591a5c8ceffcf9 Mon Sep 17 00:00:00 2001 From: Gregor Richards Date: Tue, 27 Sep 2016 20:49:16 -0400 Subject: [PATCH] Workarounds for cores that can't be saved early Netplay now never serializes/unserializes cores before 60 frames have been emulated. This is a workaround for buggy cores and documented as such. --- network/netplay/netplay.c | 35 +++++++++++------------- network/netplay/netplay_net.c | 51 ++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 20 deletions(-) diff --git a/network/netplay/netplay.c b/network/netplay/netplay.c index e363d9ce08..dbd5007e6b 100644 --- a/network/netplay/netplay.c +++ b/network/netplay/netplay.c @@ -596,13 +596,18 @@ static bool netplay_poll(netplay_t *netplay) if (netplay_is_server(netplay) && netplay->spectate.enabled) return true; - /* Read Netplay input, block if we're configured to stall for input every - * frame */ - res = poll_input(netplay, (netplay->stall_frames == 0) && (netplay->read_frame_count <= netplay->self_frame_count)); - if (res == -1) + /* WORKAROUND: The only reason poll_input is ignored in the first frame is + * that some cores can't report state size until after the first frame. */ + if (netplay->self_frame_count > 0) { - hangup(netplay); - return false; + /* Read Netplay input, block if we're configured to stall for input every + * frame */ + res = poll_input(netplay, (netplay->stall_frames == 0) && (netplay->read_frame_count <= netplay->self_frame_count)); + if (res == -1) + { + hangup(netplay); + return false; + } } /* Simulate the input if we don't have real input */ @@ -940,7 +945,6 @@ static bool init_socket(netplay_t *netplay, const char *server, uint16_t port) static bool netplay_init_buffers(netplay_t *netplay, unsigned frames) { unsigned i; - retro_ctx_size_info_t info; if (!netplay) return false; @@ -957,17 +961,9 @@ static bool netplay_init_buffers(netplay_t *netplay, unsigned frames) if (!netplay->buffer) return false; - 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) - return false; - } + /* WORKAROUND: The code to initialize state buffers really should be here. + * It's been moved to work around cores that can't core_serialize_size + * early. */ return true; } @@ -1136,7 +1132,8 @@ void netplay_free(netplay_t *netplay) else { for (i = 0; i < netplay->buffer_size; i++) - free(netplay->buffer[i].state); + if (netplay->buffer[i].state) + free(netplay->buffer[i].state); free(netplay->buffer); } diff --git a/network/netplay/netplay_net.c b/network/netplay/netplay_net.c index ec1779dc90..17c405e723 100644 --- a/network/netplay/netplay_net.c +++ b/network/netplay/netplay_net.c @@ -27,6 +27,8 @@ #include "../../autosave.h" +#define TOO_EARLY_TO_SAVE 60 + static void netplay_handle_frame_hash(netplay_t *netplay, struct delta_frame *delta) { if (netplay_is_server(netplay)) @@ -66,7 +68,12 @@ static bool netplay_net_pre_frame(netplay_t *netplay) serial_info.data = netplay->buffer[netplay->self_ptr].state; serial_info.size = netplay->state_size; - if (core_serialize(&serial_info)) + if (!netplay->has_connection && netplay->self_frame_count < TOO_EARLY_TO_SAVE) + { + /* WORKAROUND: Some cores don't like being save/loadstated too early. + * If we're not even connected yet, just don't bother. */ + } + else if (netplay->savestates_work && core_serialize(&serial_info)) { if (netplay->force_send_savestate) { @@ -173,6 +180,30 @@ static void netplay_net_post_frame(netplay_t *netplay) netplay->self_ptr = NEXT_PTR(netplay->self_ptr); netplay->self_frame_count++; + /* WORKAROUND: We initialize the buffer states late to work around cores + * that can't even core_serialize_size early. */ + if (netplay->self_frame_count == 1 && netplay->state_size == 0) + { + int 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; + } + } + } + + /* Only relevant if we're connected */ if (!netplay->has_connection) return; @@ -208,6 +239,24 @@ static void netplay_net_post_frame(netplay_t *netplay) netplay->replay_ptr = netplay->other_ptr; netplay->replay_frame_count = netplay->other_frame_count; + /* WORKAROUND: Some cores cannot serialize or unserialize too early in + * execution. We avoid the problem by forcing some phantom frames to + * pass. */ + if (netplay->self_frame_count < TOO_EARLY_TO_SAVE) + { + int frameskip; + for (frameskip = 0; frameskip < TOO_EARLY_TO_SAVE; frameskip++) + { +#if defined(HAVE_THREADS) + autosave_lock(); +#endif + core_run(); +#if defined(HAVE_THREADS) + autosave_unlock(); +#endif + } + } + serial_info.data = NULL; serial_info.data_const = netplay->buffer[netplay->replay_ptr].state; serial_info.size = netplay->state_size;