diff --git a/network/netplay/netplay.c b/network/netplay/netplay.c index e4f5c90f86..6675aa22d7 100644 --- a/network/netplay/netplay.c +++ b/network/netplay/netplay.c @@ -729,9 +729,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); @@ -981,7 +983,7 @@ static bool netplay_init_buffers(netplay_t *netplay, unsigned frames) if (!netplay->buffer[i].state) { - netplay->savestates_work = false; + netplay->quirks |= NETPLAY_QUIRK_NO_SAVESTATES; netplay->stall_frames = 0; } } @@ -999,6 +1001,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). @@ -1007,7 +1010,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, uint32_t quirks) { netplay_t *netplay = (netplay_t*)calloc(1, sizeof(*netplay)); if (!netplay) @@ -1019,10 +1022,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)) { @@ -1332,6 +1335,8 @@ bool init_netplay(bool is_spectate, const char *server, unsigned port) { struct retro_callbacks cbs = {0}; settings_t *settings = config_get_ptr(); + uint32_t serialization_quirks = 0; + uint32_t quirks = 0; if (!netplay_enabled) return false; @@ -1345,6 +1350,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_serialize_quirks(); + if ((serialization_quirks & ~((uint32_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"); @@ -1361,7 +1380,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; diff --git a/network/netplay/netplay.h b/network/netplay/netplay.h index 6fb459b662..703437e0fc 100644 --- a/network/netplay/netplay.h +++ b/network/netplay/netplay.h @@ -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, uint32_t quirks); /** * netplay_free: diff --git a/network/netplay/netplay_net.c b/network/netplay/netplay_net.c index 2562f48231..e1a2ab7fbd 100644 --- a/network/netplay/netplay_net.c +++ b/network/netplay/netplay_net.c @@ -69,7 +69,7 @@ static bool netplay_net_pre_frame(netplay_t *netplay) 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_NO_SAVESTATES) && core_serialize(&serial_info)) { if (netplay->force_send_savestate && !netplay->stall) { @@ -83,7 +83,7 @@ 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; @@ -135,7 +135,7 @@ 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->quirks & NETPLAY_QUIRK_NO_TRANSMISSION)) { netplay_load_savestate(netplay, NULL, true); } diff --git a/network/netplay/netplay_private.h b/network/netplay/netplay_private.h index e792d94788..e2159e10e6 100644 --- a/network/netplay/netplay_private.h +++ b/network/netplay/netplay_private.h @@ -40,6 +40,27 @@ #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_INITIALIZING \ + |RETRO_SERIALIZATION_QUIRK_SINGLE_SESSION \ + |RETRO_SERIALIZATION_QUIRK_ARCHITECTURE_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_ARCHITECTURE_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 +139,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 */ + unsigned quirks; /* Force our state to be sent to the other side. Used when they request a * savestate, to send at the next pre-frame. */