/* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2017 - Daniel De Matteis * Copyright (C) 2016-2017 - Gregor Richards * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with RetroArch. * If not, see . */ #include #include #include #include #include "netplay_private.h" #include "../../autosave.h" #include "../../driver.h" #include "../../input/input_driver.h" #if 0 #define DEBUG_NONDETERMINISTIC_CORES #endif /** * netplay_update_unread_ptr * * Update the global unread_ptr and unread_frame_count to correspond to the * earliest unread frame count of any connected player */ void netplay_update_unread_ptr(netplay_t *netplay) { if (netplay->is_server && netplay->connected_players<=1) { /* Nothing at all to read! */ netplay->unread_ptr = netplay->self_ptr; netplay->unread_frame_count = netplay->self_frame_count; } else { size_t new_unread_ptr = 0; uint32_t new_unread_frame_count = (uint32_t) -1; uint32_t client; for (client = 0; client < MAX_CLIENTS; client++) { if (!(netplay->connected_players & (1<connected_slaves & (1<read_frame_count[client] < new_unread_frame_count) { new_unread_ptr = netplay->read_ptr[client]; new_unread_frame_count = netplay->read_frame_count[client]; } } if ( !netplay->is_server && netplay->server_frame_count < new_unread_frame_count) { new_unread_ptr = netplay->server_ptr; new_unread_frame_count = netplay->server_frame_count; } if (new_unread_frame_count != (uint32_t) -1) { netplay->unread_ptr = new_unread_ptr; netplay->unread_frame_count = new_unread_frame_count; } else { netplay->unread_ptr = netplay->self_ptr; netplay->unread_frame_count = netplay->self_frame_count; } } } struct vote_count { uint16_t votes[32]; }; /** * netplay_device_client_state * @netplay : pointer to netplay object * @simframe : frame in which merging is being performed * @device : device being merged * @client : client to find state for */ netplay_input_state_t netplay_device_client_state(netplay_t *netplay, struct delta_frame *simframe, uint32_t device, uint32_t client) { uint32_t dsize = netplay_expected_input_size(netplay, 1 << device); netplay_input_state_t simstate = netplay_input_state_for( &simframe->real_input[device], client, dsize, false, true); if (!simstate) { if (netplay->read_frame_count[client] > simframe->frame) return NULL; simstate = netplay_input_state_for(&simframe->simlated_input[device], client, dsize, false, true); } return simstate; } /** * netplay_merge_digital * @netplay : pointer to netplay object * @resstate : state being resolved * @simframe : frame in which merging is being performed * @device : device being merged * @clients : bitmap of clients being merged * @digital : bitmap of digital bits */ static void netplay_merge_digital(netplay_t *netplay, netplay_input_state_t resstate, struct delta_frame *simframe, uint32_t device, uint32_t clients, const uint32_t *digital) { netplay_input_state_t simstate; uint32_t word, bit, client; uint8_t share_mode = netplay->device_share_modes[device] & NETPLAY_SHARE_DIGITAL_BITS; /* Make sure all real clients are accounted for */ for (simstate = simframe->real_input[device]; simstate; simstate = simstate->next) { if (!simstate->used || simstate->size != resstate->size) continue; clients |= 1<client_num; } if (share_mode == NETPLAY_SHARE_DIGITAL_VOTE) { unsigned i, j; /* This just assumes we have no more than * three words, will need to be adjusted for new devices */ struct vote_count votes[3]; /* Vote mode requires counting all the bits */ uint32_t client_count = 0; for (i = 0; i < 3; i++) for (j = 0; j < 32; j++) votes[i].votes[j] = 0; for (client = 0; client < MAX_CLIENTS; client++) { if (!(clients & (1<size; word++) { if (!digital[word]) continue; for (bit = 0; bit < 32; bit++) { if (!(digital[word] & (1<data[word] & (1<size; word++) { for (bit = 0; bit < 32; bit++) { if (votes[word].votes[bit] > client_count) resstate->data[word] |= (1<size; word++) { uint32_t part; if (!digital[word]) continue; part = simstate->data[word]; if (digital[word] == (uint32_t) -1) { /* Combine the whole word */ switch (share_mode) { case NETPLAY_SHARE_DIGITAL_XOR: resstate->data[word] ^= part; break; default: resstate->data[word] |= part; } } else /* !whole word */ { for (bit = 0; bit < 32; bit++) { if (!(digital[word] & (1<data[word] ^= part & (1<data[word] |= part & (1<device_share_modes[device] & NETPLAY_SHARE_ANALOG_BITS; int32_t value = 0, new_value; /* Make sure all real clients are accounted for */ for (simstate = simframe->real_input[device]; simstate; simstate = simstate->next) { if (!simstate->used || simstate->size != resstate->size) continue; clients |= 1<client_num; } for (client = 0; client < MAX_CLIENTS; client++) { if (!(clients & (1<data[word]>>bit) & 0xFFFF); switch (share_mode) { case NETPLAY_SHARE_ANALOG_AVERAGE: value += (int32_t) new_value; break; default: if (abs(new_value) > abs(value) || (abs(new_value) == abs(value) && new_value > value)) value = new_value; } } if (share_mode == NETPLAY_SHARE_ANALOG_AVERAGE) if (client_count > 0) /* Prevent potential divide by zero */ value /= client_count; resstate->data[word] |= ((uint32_t) (uint16_t) value) << bit; } /** * netplay_merge_analog * @netplay : pointer to netplay object * @resstate : state being resolved * @simframe : frame in which merging is being performed * @device : device being merged * @clients : bitmap of clients being merged * @dtype : device type */ static void netplay_merge_analog(netplay_t *netplay, netplay_input_state_t resstate, struct delta_frame *simframe, uint32_t device, uint32_t clients, unsigned dtype) { /* Devices with no analog parts */ if (dtype == RETRO_DEVICE_JOYPAD || dtype == RETRO_DEVICE_KEYBOARD) return; /* All other devices have at least one analog word */ merge_analog_part(netplay, resstate, simframe, device, clients, 1, 0); merge_analog_part(netplay, resstate, simframe, device, clients, 1, 16); /* And the ANALOG device has two (two sticks) */ if (dtype == RETRO_DEVICE_ANALOG) { merge_analog_part(netplay, resstate, simframe, device, clients, 2, 0); merge_analog_part(netplay, resstate, simframe, device, clients, 2, 16); } } /** * netplay_resolve_input * @netplay : pointer to netplay object * @sim_ptr : frame pointer for which to resolve input * @resim : are we resimulating, or simulating this frame for the * first time? * * "Simulate" input by assuming it hasn't changed since the last read input. * Returns true if the resolved input changed from the last time it was * resolved. */ bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) { size_t prev; uint32_t device; uint32_t clients, client, client_count; netplay_input_state_t simstate, client_state = NULL, resstate, oldresstate, pstate; bool ret = false; struct delta_frame *pframe = NULL; struct delta_frame *simframe = &netplay->buffer[sim_ptr]; for (device = 0; device < MAX_INPUT_DEVICES; device++) { unsigned dtype = netplay->config_devices[device]&RETRO_DEVICE_MASK; uint32_t dsize = netplay_expected_input_size(netplay, 1 << device); clients = netplay->device_clients[device]; client_count = 0; /* Make sure all real clients are accounted for */ for (simstate = simframe->real_input[device]; simstate; simstate = simstate->next) { if (!simstate->used || simstate->size != dsize) continue; clients |= 1<client_num; } for (client = 0; client < MAX_CLIENTS; client++) { if (!(clients & (1<real_input[device], client, dsize, false, true); if (!simstate) { /* Don't already have this input, so must * simulate if we're supposed to have it at all */ if (netplay->read_frame_count[client] > simframe->frame) continue; simstate = netplay_input_state_for(&simframe->simlated_input[device], client, dsize, false, false); if (!simstate) continue; prev = PREV_PTR(netplay->read_ptr[client]); pframe = &netplay->buffer[prev]; pstate = netplay_input_state_for(&pframe->real_input[device], client, dsize, false, true); if (!pstate) continue; if (resim && (dtype == RETRO_DEVICE_JOYPAD || dtype == RETRO_DEVICE_ANALOG)) { /* In resimulation mode, we only copy the buttons. The reason for this * is nonobvious: * * If we resimulated nothing, then the /duration/ with which any input * was pressed would be approximately correct, since the original * simulation came in as the input came in, but the /number of times/ * the input was pressed would be wrong, as there would be an * advancing wavefront of real data overtaking the simulated data * (which is really just real data offset by some frames). * * That's acceptable for arrows in most situations, since the amount * you move is tied to the duration, but unacceptable for buttons, * which will seem to jerkily be pressed numerous times with those * wavefronts. */ const uint32_t keep = (1U<data[0] &= keep; simstate->data[0] |= pstate->data[0] & ~keep; } else memcpy(simstate->data, pstate->data, dsize * sizeof(uint32_t)); } client_state = simstate; client_count++; } /* The frontend always uses the first resolved input, * so make sure it's right */ while (simframe->resolved_input[device] && (simframe->resolved_input[device]->size != dsize || simframe->resolved_input[device]->client_num != 0)) { /* The default resolved input is of the wrong size! */ netplay_input_state_t nextistate = simframe->resolved_input[device]->next; free(simframe->resolved_input[device]); simframe->resolved_input[device] = nextistate; } /* Now we copy the state, whether real or simulated, * out into the resolved state */ resstate = netplay_input_state_for( &simframe->resolved_input[device], 0, dsize, false, false); if (!resstate) continue; if (client_count == 1) { /* Trivial in the common 1-client case */ if (memcmp(resstate->data, client_state->data, dsize * sizeof(uint32_t))) ret = true; memcpy(resstate->data, client_state->data, dsize * sizeof(uint32_t)); } else if (client_count == 0) { uint32_t word; for (word = 0; word < dsize; word++) { if (resstate->data[word]) ret = true; resstate->data[word] = 0; } } else { /* Merge them */ /* Most devices have all the digital parts in the first word. */ static const uint32_t digital_common[3] = {~0u, 0u, 0u}; static const uint32_t digital_keyboard[5] = {~0u, ~0u, ~0u, ~0u, ~0u}; const uint32_t *digital = NULL; if (dtype == RETRO_DEVICE_KEYBOARD) digital = digital_keyboard; else digital = digital_common; oldresstate = netplay_input_state_for( &simframe->resolved_input[device], 1, dsize, false, false); if (!oldresstate) continue; memcpy(oldresstate->data, resstate->data, dsize * sizeof(uint32_t)); memset(resstate->data, 0, dsize * sizeof(uint32_t)); netplay_merge_digital(netplay, resstate, simframe, device, clients, digital); netplay_merge_analog(netplay, resstate, simframe, device, clients, dtype); if (memcmp(resstate->data, oldresstate->data, dsize * sizeof(uint32_t))) ret = true; } } return ret; } static void netplay_handle_frame_hash(netplay_t *netplay, struct delta_frame *delta) { if (netplay->is_server) { if (netplay->check_frames && delta->frame % abs(netplay->check_frames) == 0) { delta->crc = netplay_delta_frame_crc(netplay, delta); netplay_cmd_crc(netplay, delta); } } else if (delta->crc && netplay->crcs_valid) { /* We have a remote CRC, so check it */ uint32_t local_crc = netplay_delta_frame_crc(netplay, delta); if (local_crc != delta->crc) { /* If the very first check frame is wrong, * they probably just don't work */ if (!netplay->crc_validity_checked) netplay->crcs_valid = false; else if (netplay->crcs_valid) { /* Fix this! */ if (netplay->check_frames < 0) { /* Just report */ RARCH_ERR("Netplay CRCs mismatch!\n"); } else netplay_cmd_request_savestate(netplay); } } else if (!netplay->crc_validity_checked) netplay->crc_validity_checked = true; } } /** * netplay_sync_pre_frame * @netplay : pointer to netplay object * * Pre-frame for Netplay synchronization. */ bool netplay_sync_pre_frame(netplay_t *netplay, const char *netplay_password, const char *netplay_spectate_password ) { retro_ctx_serialize_info_t serial_info; if (netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->run_ptr], netplay->run_frame_count)) { serial_info.data_const = NULL; serial_info.data = netplay->buffer[netplay->run_ptr].state; serial_info.size = netplay->state_size; memset(serial_info.data, 0, serial_info.size); if ((netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) || netplay->run_frame_count == 0) { /* 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 && !netplay->remote_paused) { /* Bring our running frame and input frames into * parity so we don't send old info. */ if (netplay->run_ptr != netplay->self_ptr) { memcpy(netplay->buffer[netplay->self_ptr].state, netplay->buffer[netplay->run_ptr].state, netplay->state_size); netplay->run_ptr = netplay->self_ptr; netplay->run_frame_count = netplay->self_frame_count; } /* 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; } } else { /* If the core can't serialize properly, we must stall for the * remote input on EVERY frame, because we can't recover */ netplay->quirks |= NETPLAY_QUIRK_NO_SAVESTATES; netplay->stateless_mode = true; } /* If we can't transmit savestates, we must stall * until the client is ready. */ if (netplay->run_frame_count > 0 && (netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION)) && (netplay->connections_size == 0 || !netplay->connections[0].active || netplay->connections[0].mode < NETPLAY_CONNECTION_CONNECTED)) netplay->stall = NETPLAY_STALL_NO_CONNECTION; } if (netplay->is_server) { fd_set fds; struct timeval tmp_tv = {0}; int new_fd; struct sockaddr_storage their_addr; socklen_t addr_size; struct netplay_connection *connection; size_t connection_num; /* Check for a connection */ FD_ZERO(&fds); FD_SET(netplay->listen_fd, &fds); if (socket_select(netplay->listen_fd + 1, &fds, NULL, NULL, &tmp_tv) > 0 && FD_ISSET(netplay->listen_fd, &fds)) { addr_size = sizeof(their_addr); new_fd = accept(netplay->listen_fd, (struct sockaddr*)&their_addr, &addr_size); if (new_fd < 0) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_NETPLAY_FAILED)); goto process; } /* Set the socket nonblocking */ if (!socket_nonblock(new_fd)) { /* Catastrophe! */ socket_close(new_fd); goto process; } #if defined(IPPROTO_TCP) && defined(TCP_NODELAY) { int flag = 1; if (setsockopt(new_fd, IPPROTO_TCP, TCP_NODELAY, #ifdef _WIN32 (const char*) #else (const void*) #endif &flag, sizeof(int)) < 0) RARCH_WARN("Could not set netplay TCP socket to nodelay. Expect jitter.\n"); } #endif #if defined(F_SETFD) && defined(FD_CLOEXEC) /* Don't let any inherited processes keep open our port */ if (fcntl(new_fd, F_SETFD, FD_CLOEXEC) < 0) RARCH_WARN("Cannot set Netplay port to close-on-exec. It may fail to reopen if the client disconnects.\n"); #endif /* Allocate a connection */ for (connection_num = 0; connection_num < netplay->connections_size; connection_num++) if (!netplay->connections[connection_num].active && netplay->connections[connection_num].mode != NETPLAY_CONNECTION_DELAYED_DISCONNECT) break; if (connection_num == netplay->connections_size) { if (connection_num == 0) { netplay->connections = (struct netplay_connection*) malloc(sizeof(struct netplay_connection)); if (!netplay->connections) { socket_close(new_fd); goto process; } netplay->connections_size = 1; } else { size_t new_connections_size = netplay->connections_size * 2; struct netplay_connection *new_connections = (struct netplay_connection*) realloc(netplay->connections, new_connections_size*sizeof(struct netplay_connection)); if (!new_connections) { socket_close(new_fd); goto process; } memset(new_connections + netplay->connections_size, 0, netplay->connections_size * sizeof(struct netplay_connection)); netplay->connections = new_connections; netplay->connections_size = new_connections_size; } } connection = &netplay->connections[connection_num]; /* Set it up */ memset(connection, 0, sizeof(*connection)); connection->active = true; connection->fd = new_fd; connection->mode = NETPLAY_CONNECTION_INIT; if (!netplay_init_socket_buffer(&connection->send_packet_buffer, netplay->packet_buffer_size) || !netplay_init_socket_buffer(&connection->recv_packet_buffer, netplay->packet_buffer_size)) { if (connection->send_packet_buffer.data) netplay_deinit_socket_buffer(&connection->send_packet_buffer); connection->active = false; socket_close(new_fd); goto process; } netplay_handshake_init_send(netplay, connection, netplay_password, netplay_spectate_password); } } process: netplay->can_poll = true; input_poll_net(netplay); return (netplay->stall != NETPLAY_STALL_NO_CONNECTION); } /** * netplay_sync_post_frame * @netplay : pointer to netplay object * * Post-frame for Netplay synchronization. * We check if we have new input and replay from recorded input. */ void netplay_sync_post_frame(netplay_t *netplay, bool stalled) { uint32_t lo_frame_count, hi_frame_count; retro_time_t current_time = cpu_features_get_time_usec(); /* Unless we're stalling, we've just finished running a frame */ if (!stalled) { netplay->run_ptr = NEXT_PTR(netplay->run_ptr); netplay->run_frame_count++; } /* We've finished an input frame even if we're stalling */ if ((!stalled || netplay->stall == NETPLAY_STALL_INPUT_LATENCY) && netplay->self_frame_count < netplay->run_frame_count + netplay->input_latency_frames) { netplay->self_ptr = NEXT_PTR(netplay->self_ptr); netplay->self_frame_count++; } /* Only relevant if we're connected and not in a desynching operation */ if ((netplay->is_server && (netplay->connected_players<=1)) || (netplay->self_mode < NETPLAY_CONNECTION_CONNECTED) || (netplay->desync)) { netplay->other_frame_count = netplay->self_frame_count; netplay->other_ptr = netplay->self_ptr; /* FIXME: Duplication */ if (netplay->catch_up) { netplay->catch_up = false; input_driver_unset_nonblock_state(); driver_set_nonblock_state(); } return; } /* Reset if it was requested */ if (netplay->force_reset) { core_reset(); netplay->force_reset = false; } netplay->replay_ptr = netplay->other_ptr; netplay->replay_frame_count = netplay->other_frame_count; #ifndef DEBUG_NONDETERMINISTIC_CORES if (!netplay->force_rewind) { bool cont = true; /* Skip ahead if we predicted correctly. * Skip until our simulation failed. */ while (netplay->other_frame_count < netplay->unread_frame_count && netplay->other_frame_count < netplay->run_frame_count) { struct delta_frame *ptr = &netplay->buffer[netplay->other_ptr]; /* If resolving the input changes it, we used bad input */ if (netplay_resolve_input(netplay, netplay->other_ptr, true)) { cont = false; break; } netplay_handle_frame_hash(netplay, ptr); netplay->other_ptr = NEXT_PTR(netplay->other_ptr); netplay->other_frame_count++; } netplay->replay_ptr = netplay->other_ptr; netplay->replay_frame_count = netplay->other_frame_count; if (cont) { while (netplay->replay_frame_count < netplay->run_frame_count) { if (netplay_resolve_input(netplay, netplay->replay_ptr, true)) break; netplay->replay_ptr = NEXT_PTR(netplay->replay_ptr); netplay->replay_frame_count++; } } } #endif /* Now replay the real input if we've gotten ahead of it */ if (netplay->force_rewind || netplay->replay_frame_count < netplay->run_frame_count) { retro_ctx_serialize_info_t serial_info; /* Replay frames. */ netplay->is_replay = true; /* If we have a keyboard device, we replay the previous frame's input * just to assert that the keydown/keyup events work if the core * translates them in that way */ if (netplay->have_updown_device) { netplay->replay_ptr = PREV_PTR(netplay->replay_ptr); netplay->replay_frame_count--; #ifdef HAVE_THREADS autosave_lock(); #endif core_run(); #ifdef HAVE_THREADS autosave_unlock(); #endif netplay->replay_ptr = NEXT_PTR(netplay->replay_ptr); netplay->replay_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; if (!core_unserialize(&serial_info)) { RARCH_ERR("Netplay savestate loading failed: Prepare for desync!\n"); } while (netplay->replay_frame_count < netplay->run_frame_count) { retro_time_t tm; struct delta_frame *ptr = &netplay->buffer[netplay->replay_ptr]; retro_time_t start = current_time; serial_info.data = ptr->state; serial_info.size = netplay->state_size; serial_info.data_const = NULL; /* Remember the current state */ memset(serial_info.data, 0, serial_info.size); core_serialize(&serial_info); if (netplay->replay_frame_count < netplay->unread_frame_count) netplay_handle_frame_hash(netplay, ptr); /* Re-simulate this frame's input */ netplay_resolve_input(netplay, netplay->replay_ptr, true); #ifdef HAVE_THREADS autosave_lock(); #endif core_run(); #ifdef HAVE_THREADS autosave_unlock(); #endif netplay->replay_ptr = NEXT_PTR(netplay->replay_ptr); netplay->replay_frame_count++; #ifdef DEBUG_NONDETERMINISTIC_CORES if (ptr->have_remote && netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->replay_ptr], netplay->replay_frame_count)) { RARCH_LOG("PRE %u: %X\n", netplay->replay_frame_count-1, netplay_delta_frame_crc(netplay, ptr)); if (netplay->is_server) RARCH_LOG("INP %X %X\n", ptr->real_input_state[0], ptr->self_state[0]); else RARCH_LOG("INP %X %X\n", ptr->self_state[0], ptr->real_input_state[0]); ptr = &netplay->buffer[netplay->replay_ptr]; serial_info.data = ptr->state; memset(serial_info.data, 0, serial_info.size); core_serialize(&serial_info); RARCH_LOG("POST %u: %X\n", netplay->replay_frame_count-1, netplay_delta_frame_crc(netplay, ptr)); } #endif /* Get our time window */ tm = current_time - start; netplay->frame_run_time_sum -= netplay->frame_run_time[netplay->frame_run_time_ptr]; netplay->frame_run_time[netplay->frame_run_time_ptr] = tm; netplay->frame_run_time_sum += tm; netplay->frame_run_time_ptr++; if (netplay->frame_run_time_ptr >= NETPLAY_FRAME_RUN_TIME_WINDOW) netplay->frame_run_time_ptr = 0; } /* Average our time */ netplay->frame_run_time_avg = netplay->frame_run_time_sum / NETPLAY_FRAME_RUN_TIME_WINDOW; if (netplay->unread_frame_count < netplay->run_frame_count) { netplay->other_ptr = netplay->unread_ptr; netplay->other_frame_count = netplay->unread_frame_count; } else { netplay->other_ptr = netplay->run_ptr; netplay->other_frame_count = netplay->run_frame_count; } netplay->is_replay = false; netplay->force_rewind = false; } if (netplay->is_server) { uint32_t client; lo_frame_count = hi_frame_count = netplay->unread_frame_count; /* Look for players that are ahead of us */ for (client = 0; client < MAX_CLIENTS; client++) { if (!(netplay->connected_players & (1<read_frame_count[client] > hi_frame_count) hi_frame_count = netplay->read_frame_count[client]; } } else lo_frame_count = hi_frame_count = netplay->server_frame_count; /* If we're behind, try to catch up */ if (netplay->catch_up) { /* Are we caught up? */ if (netplay->self_frame_count + 1 >= lo_frame_count) { netplay->catch_up = false; input_driver_unset_nonblock_state(); driver_set_nonblock_state(); } } else if (!stalled) { if (netplay->self_frame_count + 3 < lo_frame_count) { uint32_t cur_behind = lo_frame_count - netplay->self_frame_count; /* We're behind, but we'll only try to catch up if we're actually * falling behind, i.e. if we're more behind after some time */ if (netplay->catch_up_time == 0) { /* Record our current time to check for catch-up later */ netplay->catch_up_time = current_time; netplay->catch_up_behind = cur_behind; } else if (current_time - netplay->catch_up_time > CATCH_UP_CHECK_TIME_USEC) { /* Time to check how far behind we are */ if (netplay->catch_up_behind <= cur_behind) { /* We're definitely falling behind! */ netplay->catch_up = true; netplay->catch_up_time = 0; input_driver_set_nonblock_state(); driver_set_nonblock_state(); } else { /* Check again in another period */ netplay->catch_up_time = current_time; netplay->catch_up_behind = cur_behind; } } } else if (netplay->self_frame_count + 3 < hi_frame_count) { size_t i; netplay->catch_up_time = 0; /* We're falling behind some clients but not others, so request that * clients ahead of us stall */ for (i = 0; i < netplay->connections_size; i++) { uint32_t client_num; struct netplay_connection *connection = &netplay->connections[i]; if (!connection->active || connection->mode != NETPLAY_CONNECTION_PLAYING) continue; client_num = (uint32_t)(i + 1); /* Are they ahead? */ if (netplay->self_frame_count + 3 < netplay->read_frame_count[client_num]) { /* Tell them to stall */ if (connection->stall_frame + NETPLAY_MAX_REQ_STALL_FREQUENCY < netplay->self_frame_count) { connection->stall_frame = netplay->self_frame_count; netplay_cmd_stall(netplay, connection, netplay->read_frame_count[client_num] - netplay->self_frame_count + 1); } } } } else netplay->catch_up_time = 0; } else netplay->catch_up_time = 0; }