static int poll_input(netplay_t *netplay, bool block) { bool had_input = false; int max_fd = netplay->fd + 1; struct timeval tv = {0}; tv.tv_sec = 0; tv.tv_usec = block ? (RETRY_MS * 1000) : 0; do { fd_set fds; /* select() does not take pointer to const struct timeval. * Technically possible for select() to modify tmp_tv, so * we go paranoia mode. */ struct timeval tmp_tv = tv; had_input = false; netplay->timeout_cnt++; FD_ZERO(&fds); FD_SET(netplay->fd, &fds); if (socket_select(max_fd, &fds, NULL, NULL, &tmp_tv) < 0) return -1; if (FD_ISSET(netplay->fd, &fds)) { /* If we're not ready for input, wait until we are. * Could fill the TCP buffer, stalling the other side. */ if (netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->read_ptr], netplay->read_frame_count)) { had_input = true; if (!netplay_get_cmd(netplay)) return -1; } } /* If we were blocked for input, pass if we have this frame's input */ if (block && netplay->read_frame_count > netplay->self_frame_count) break; /* If we had input, we might have more */ if (had_input || !block) continue; RARCH_LOG("Network is stalling at frame %u, count %u of %d ...\n", netplay->self_frame_count, netplay->timeout_cnt, MAX_RETRIES); if (netplay->timeout_cnt >= MAX_RETRIES && !netplay->remote_paused) return -1; } while (had_input || block); return 0; }
/** * netplay_load_savestate * @netplay : pointer to netplay object * @serial_info : the savestate being loaded, NULL means * "load it yourself" * @save : Whether to save the provided serial_info * into the frame buffer * * Inform Netplay of a savestate load and send it to the other side **/ void netplay_load_savestate(netplay_t *netplay, retro_ctx_serialize_info_t *serial_info, bool save) { retro_ctx_serialize_info_t tmp_serial_info; netplay_force_future(netplay); /* Record it in our own buffer */ if (save || !serial_info) { if (netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->run_ptr], netplay->run_frame_count)) { if (!serial_info) { tmp_serial_info.size = netplay->state_size; tmp_serial_info.data = netplay->buffer[netplay->run_ptr].state; if (!core_serialize(&tmp_serial_info)) return; tmp_serial_info.data_const = tmp_serial_info.data; serial_info = &tmp_serial_info; } else { if (serial_info->size <= netplay->state_size) { memcpy(netplay->buffer[netplay->run_ptr].state, serial_info->data_const, serial_info->size); } } } else { /* FIXME: This is a critical failure! */ return; } } /* Don't send it if we're expected to be desynced */ if (netplay->desync) return; /* 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; /* Send this to every peer */ if (netplay->compress_nil.compression_backend) netplay_send_savestate(netplay, serial_info, 0, &netplay->compress_nil); if (netplay->compress_zlib.compression_backend) netplay_send_savestate(netplay, serial_info, NETPLAY_COMPRESSION_ZLIB, &netplay->compress_zlib); }
/** * netplay_net_pre_frame: * @netplay : pointer to netplay object * * Pre-frame for Netplay (normal version). **/ 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)) { 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->quirks & NETPLAY_QUIRK_INITIALIZATION) || netplay->self_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) { /* Send this along to the other side */ serial_info.data_const = netplay->buffer[netplay->self_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->stall_frames = 0; } /* If we can't transmit savestates, we must stall until the client is ready */ if (!netplay->has_connection && netplay->self_frame_count > 0 && (netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION))) netplay->stall = RARCH_NETPLAY_STALL_NO_CONNECTION; } if (netplay->is_server && !netplay->has_connection) { fd_set fds; struct timeval tmp_tv = {0}; int new_fd; struct sockaddr_storage their_addr; socklen_t addr_size; /* Check for a connection */ FD_ZERO(&fds); FD_SET(netplay->fd, &fds); if (socket_select(netplay->fd + 1, &fds, NULL, NULL, &tmp_tv) > 0 && FD_ISSET(netplay->fd, &fds)) { addr_size = sizeof(their_addr); new_fd = accept(netplay->fd, (struct sockaddr*)&their_addr, &addr_size); if (new_fd < 0) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_NETPLAY_FAILED)); return true; } socket_close(netplay->fd); netplay->fd = new_fd; #if defined(IPPROTO_TCP) && defined(TCP_NODELAY) { int flag = 1; if (setsockopt(netplay->fd, IPPROTO_TCP, TCP_NODELAY, (void*)&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(netplay->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 /* Establish the connection */ if (netplay_handshake(netplay)) { netplay->has_connection = true; /* Send them the savestate */ if (!(netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION))) { netplay->force_send_savestate = true; } else { /* Because the first frame isn't serialized, we're actually at * frame 1 */ netplay->self_ptr = NEXT_PTR(netplay->self_ptr); netplay->self_frame_count = 1; } /* And expect the current frame from the other side */ netplay->read_frame_count = netplay->other_frame_count = netplay->self_frame_count; netplay->read_ptr = netplay->other_ptr = netplay->self_ptr; /* Unstall if we were waiting for this */ if (netplay->stall == RARCH_NETPLAY_STALL_NO_CONNECTION) netplay->stall = 0; } else { socket_close(netplay->fd); /* FIXME: Get in a state to accept another client */ } } } netplay->can_poll = true; input_poll_net(); return (netplay->stall != RARCH_NETPLAY_STALL_NO_CONNECTION); }
/** * netplay_net_post_frame: * @netplay : pointer to netplay object * * Post-frame for Netplay (normal version). * We check if we have new input and replay from recorded input. **/ static void netplay_net_post_frame(netplay_t *netplay) { netplay->self_ptr = NEXT_PTR(netplay->self_ptr); netplay->self_frame_count++; /* Only relevant if we're connected */ if (!netplay->has_connection) { netplay->read_frame_count = netplay->other_frame_count = netplay->self_frame_count; netplay->read_ptr = netplay->other_ptr = netplay->self_ptr; return; } #ifndef DEBUG_NONDETERMINISTIC_CORES if (!netplay->force_rewind) { /* Skip ahead if we predicted correctly. * Skip until our simulation failed. */ while (netplay->other_frame_count < netplay->read_frame_count && netplay->other_frame_count < netplay->self_frame_count) { struct delta_frame *ptr = &netplay->buffer[netplay->other_ptr]; if (memcmp(ptr->simulated_input_state, ptr->real_input_state, sizeof(ptr->real_input_state)) != 0 && !ptr->used_real) break; netplay_handle_frame_hash(netplay, ptr); netplay->other_ptr = NEXT_PTR(netplay->other_ptr); netplay->other_frame_count++; } } #endif /* Now replay the real input if we've gotten ahead of it */ if (netplay->force_rewind || (netplay->other_frame_count < netplay->read_frame_count && netplay->other_frame_count < netplay->self_frame_count)) { retro_ctx_serialize_info_t serial_info; /* Replay frames. */ netplay->is_replay = true; 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; if (!core_unserialize(&serial_info)) { RARCH_ERR("Netplay savestate loading failed: Prepare for desync!\n"); } while (netplay->replay_frame_count < netplay->self_frame_count) { struct delta_frame *ptr = &netplay->buffer[netplay->replay_ptr]; 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->read_frame_count) netplay_handle_frame_hash(netplay, ptr); /* Simulate this frame's input */ if (netplay->replay_frame_count >= netplay->read_frame_count) netplay_simulate_input(netplay, netplay->replay_ptr); autosave_lock(); core_run(); autosave_unlock(); 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 } if (netplay->read_frame_count < netplay->self_frame_count) { netplay->other_ptr = netplay->read_ptr; netplay->other_frame_count = netplay->read_frame_count; } else { netplay->other_ptr = netplay->self_ptr; netplay->other_frame_count = netplay->self_frame_count; } netplay->is_replay = false; netplay->force_rewind = false; } /* If we're supposed to stall, rewind (we shouldn't get this far if we're * stalled, so this is a last resort) */ if (netplay->stall) { retro_ctx_serialize_info_t serial_info; netplay->self_ptr = PREV_PTR(netplay->self_ptr); netplay->self_frame_count--; serial_info.data = NULL; serial_info.data_const = netplay->buffer[netplay->self_ptr].state; serial_info.size = netplay->state_size; core_unserialize(&serial_info); } }
/** * get_self_input_state: * @netplay : pointer to netplay object * * Grab our own input state and send this over the network. * * Returns: true (1) if successful, otherwise false (0). **/ static bool get_self_input_state(netplay_t *netplay) { uint32_t state[WORDS_PER_FRAME - 1] = {0, 0, 0}; struct delta_frame *ptr = &netplay->buffer[netplay->self_ptr]; if (!netplay_delta_frame_ready(netplay, ptr, netplay->self_frame_count)) return false; if (ptr->have_local) { /* We've already read this frame! */ return true; } if (!input_driver_is_libretro_input_blocked() && netplay->self_frame_count > 0) { unsigned i; settings_t *settings = config_get_ptr(); /* First frame we always give zero input since relying on * input from first frame screws up when we use -F 0. */ retro_input_state_t cb = netplay->cbs.state_cb; for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++) { int16_t tmp = cb(settings->netplay.swap_input ? 0 : !netplay->port, RETRO_DEVICE_JOYPAD, 0, i); state[0] |= tmp ? 1 << i : 0; } for (i = 0; i < 2; i++) { int16_t tmp_x = cb(settings->netplay.swap_input ? 0 : !netplay->port, RETRO_DEVICE_ANALOG, i, 0); int16_t tmp_y = cb(settings->netplay.swap_input ? 0 : !netplay->port, RETRO_DEVICE_ANALOG, i, 1); state[1 + i] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16); } } /* Here we construct the payload format: * frame { * uint32_t frame_number * uint32_t RETRO_DEVICE_JOYPAD state (top 16 bits zero) * uint32_t ANALOG state[0] * uint32_t ANALOG state[1] * } * * payload { * cmd (CMD_INPUT) * cmd_size (4 words) * frame * } */ netplay->packet_buffer[0] = htonl(NETPLAY_CMD_INPUT); netplay->packet_buffer[1] = htonl(WORDS_PER_FRAME * sizeof(uint32_t)); netplay->packet_buffer[2] = htonl(netplay->self_frame_count); netplay->packet_buffer[3] = htonl(state[0]); netplay->packet_buffer[4] = htonl(state[1]); netplay->packet_buffer[5] = htonl(state[2]); if (!netplay->spectate.enabled) /* Spectate sends in its own way */ { if (!socket_send_all_blocking(netplay->fd, netplay->packet_buffer, sizeof(netplay->packet_buffer), false)) { hangup(netplay); return false; } } memcpy(ptr->self_state, state, sizeof(state)); ptr->have_local = true; return true; }
/** * netplay_load_savestate * @netplay : pointer to netplay object * @serial_info : the savestate being loaded, NULL means "load it yourself" * @save : whether to save the provided serial_info into the frame buffer * * Inform Netplay of a savestate load and send it to the other side **/ void netplay_load_savestate(netplay_t *netplay, retro_ctx_serialize_info_t *serial_info, bool save) { uint32_t header[3]; retro_ctx_serialize_info_t tmp_serial_info; if (!netplay->has_connection) return; /* Record it in our own buffer */ if (save || !serial_info) { if (netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->self_ptr], netplay->self_frame_count)) { if (!serial_info) { tmp_serial_info.size = netplay->state_size; tmp_serial_info.data = netplay->buffer[netplay->self_ptr].state; if (!core_serialize(&tmp_serial_info)) return; tmp_serial_info.data_const = tmp_serial_info.data; serial_info = &tmp_serial_info; } else { if (serial_info->size <= netplay->state_size) { memcpy(netplay->buffer[netplay->self_ptr].state, serial_info->data_const, serial_info->size); } } } } /* We need to ignore any intervening data from the other side, and never rewind past this */ if (netplay->read_frame_count < netplay->self_frame_count) { netplay->read_ptr = netplay->self_ptr; netplay->read_frame_count = netplay->self_frame_count; } if (netplay->other_frame_count < netplay->self_frame_count) { netplay->other_ptr = netplay->self_ptr; 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)); header[2] = htonl(netplay->self_frame_count); if (!socket_send_all_blocking(netplay->fd, header, sizeof(header), false)) { hangup(netplay); return; } if (!socket_send_all_blocking(netplay->fd, serial_info->data_const, serial_info->size, false)) { hangup(netplay); return; } }
/** * 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; /* 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) || (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; } #ifndef DEBUG_NONDETERMINISTIC_CORES if (!netplay->force_rewind) { /* 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]; size_t i; for (i = 0; i < MAX_USERS; i++) { if (memcmp(ptr->simulated_input_state[i], ptr->real_input_state[i], sizeof(ptr->real_input_state[i])) != 0 && !ptr->used_real[i]) break; } if (i != MAX_USERS) break; netplay_handle_frame_hash(netplay, ptr); netplay->other_ptr = NEXT_PTR(netplay->other_ptr); netplay->other_frame_count++; } } #endif /* Now replay the real input if we've gotten ahead of it */ if (netplay->force_rewind || (netplay->other_frame_count < netplay->unread_frame_count && netplay->other_frame_count < netplay->run_frame_count)) { retro_ctx_serialize_info_t serial_info; /* Replay frames. */ netplay->is_replay = true; 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; 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 start, tm; struct delta_frame *ptr = &netplay->buffer[netplay->replay_ptr]; serial_info.data = ptr->state; serial_info.size = netplay->state_size; serial_info.data_const = NULL; start = cpu_features_get_time_usec(); /* 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_simulate_input(netplay, netplay->replay_ptr, true); autosave_lock(); core_run(); autosave_unlock(); 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 = cpu_features_get_time_usec() - 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 player; lo_frame_count = hi_frame_count = netplay->unread_frame_count; /* Look for players that are ahead of us */ for (player = 0; player < MAX_USERS; player++) { if (!(netplay->connected_players & (1<<player))) continue; if (netplay->read_frame_count[player] > hi_frame_count) hi_frame_count = netplay->read_frame_count[player]; } } 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) { retro_time_t cur_time = cpu_features_get_time_usec(); 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 = cur_time; netplay->catch_up_behind = cur_behind; } else if (cur_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 = cur_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++) { struct netplay_connection *connection = &netplay->connections[i]; int player; if (!connection->active || connection->mode != NETPLAY_CONNECTION_PLAYING) continue; player = connection->player; /* Are they ahead? */ if (netplay->self_frame_count + 3 < netplay->read_frame_count[player]) { /* 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[player] - netplay->self_frame_count + 1); } } } } else netplay->catch_up_time = 0; } else netplay->catch_up_time = 0; }
/** * netplay_sync_pre_frame * @netplay : pointer to netplay object * * Pre-frame for Netplay synchronization. */ bool netplay_sync_pre_frame(netplay_t *netplay) { 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 == NULL) { 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 == NULL) { 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); } } process: netplay->can_poll = true; input_poll_net(); return (netplay->stall != NETPLAY_STALL_NO_CONNECTION); }
/** * get_self_input_state: * @netplay : pointer to netplay object * * Grab our own input state and send this frame's input state (self and remote) * over the network * * Returns: true (1) if successful, otherwise false (0). */ static bool get_self_input_state(netplay_t *netplay) { unsigned i; struct delta_frame *ptr = &netplay->buffer[netplay->self_ptr]; netplay_input_state_t istate = NULL; uint32_t devices, used_devices = 0, devi, dev_type, local_device; if (!netplay_delta_frame_ready(netplay, ptr, netplay->self_frame_count)) return false; if (ptr->have_local) { /* We've already read this frame! */ return true; } devices = netplay->self_devices; used_devices = 0; for (devi = 0; devi < MAX_INPUT_DEVICES; devi++) { if (!(devices & (1<<devi))) continue; /* Find an appropriate local device */ dev_type = netplay->config_devices[devi]&RETRO_DEVICE_MASK; for (local_device = 0; local_device < MAX_INPUT_DEVICES; local_device++) { if (used_devices & (1<<local_device)) continue; if ((netplay->config_devices[local_device]&RETRO_DEVICE_MASK) == dev_type) break; } if (local_device == MAX_INPUT_DEVICES) local_device = 0; used_devices |= (1<<local_device); istate = netplay_input_state_for(&ptr->real_input[devi], /* If we're a slave, we write our own input to MAX_CLIENTS to keep it separate */ (netplay->self_mode==NETPLAY_CONNECTION_SLAVE)?MAX_CLIENTS:netplay->self_client_num, netplay_expected_input_size(netplay, 1 << devi), true, false); if (!istate) continue; /* FIXME: More severe? */ /* First frame we always give zero input since relying on * input from first frame screws up when we use -F 0. */ if (!input_driver_is_libretro_input_blocked() && netplay->self_frame_count > 0) { uint32_t *state = istate->data; retro_input_state_t cb = netplay->cbs.state_cb; unsigned dtype = netplay->config_devices[devi]&RETRO_DEVICE_MASK; switch (dtype) { case RETRO_DEVICE_ANALOG: for (i = 0; i < 2; i++) { int16_t tmp_x = cb(local_device, RETRO_DEVICE_ANALOG, (unsigned)i, 0); int16_t tmp_y = cb(local_device, RETRO_DEVICE_ANALOG, (unsigned)i, 1); state[1 + i] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16); } /* no break */ case RETRO_DEVICE_JOYPAD: for (i = 0; i <= RETRO_DEVICE_ID_JOYPAD_R3; i++) { int16_t tmp = cb(local_device, RETRO_DEVICE_JOYPAD, 0, (unsigned)i); state[0] |= tmp ? 1 << i : 0; } break; case RETRO_DEVICE_MOUSE: case RETRO_DEVICE_LIGHTGUN: { int16_t tmp_x = cb(local_device, dtype, 0, 0); int16_t tmp_y = cb(local_device, dtype, 0, 1); state[1] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16); for (i = 2; i <= (unsigned)((dtype == RETRO_DEVICE_MOUSE) ? RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN : RETRO_DEVICE_ID_LIGHTGUN_START); i++) { int16_t tmp = cb(local_device, dtype, 0, (unsigned) i); state[0] |= tmp ? 1 << i : 0; } break; } case RETRO_DEVICE_KEYBOARD: { unsigned key, word = 0, bit = 1; for (key = 1; key < NETPLAY_KEY_LAST; key++) { state[word] |= cb(local_device, RETRO_DEVICE_KEYBOARD, 0, netplay_key_ntoh(key)) ? (1U << bit) : 0; bit++; if (bit >= 32) { bit = 0; word++; if (word >= istate->size) break; } } break; } } } } ptr->have_local = true; if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING) { ptr->have_real[netplay->self_client_num] = true; netplay->read_ptr[netplay->self_client_num] = NEXT_PTR(netplay->self_ptr); netplay->read_frame_count[netplay->self_client_num] = netplay->self_frame_count + 1; } /* And send this input to our peers */ for (i = 0; i < netplay->connections_size; i++) { struct netplay_connection *connection = &netplay->connections[i]; if (connection->active && connection->mode >= NETPLAY_CONNECTION_CONNECTED) netplay_send_cur_input(netplay, &netplay->connections[i]); } /* Handle any delayed state changes */ if (netplay->is_server) netplay_delayed_state_change(netplay); return true; }
bool netplay_handshake(netplay_t *netplay) { size_t i; uint32_t local_pmagic, remote_pmagic; unsigned sram_size, remote_sram_size; retro_ctx_memory_info_t mem_info; char msg[512]; uint32_t *content_crc_ptr = NULL; void *sram = NULL; uint32_t header[5] = {0}; bool is_server = netplay->is_server; int compression = 0; msg[0] = '\0'; mem_info.id = RETRO_MEMORY_SAVE_RAM; core_get_memory(&mem_info); content_get_crc(&content_crc_ptr); local_pmagic = netplay_platform_magic(); header[0] = htonl(*content_crc_ptr); header[1] = htonl(netplay_impl_magic()); header[2] = htonl(mem_info.size); header[3] = htonl(local_pmagic); header[4] = htonl(NETPLAY_COMPRESSION_SUPPORTED); if (!socket_send_all_blocking(netplay->fd, header, sizeof(header), false)) return false; if (!socket_receive_all_blocking(netplay->fd, header, sizeof(header))) { strlcpy(msg, msg_hash_to_str(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT), sizeof(msg)); goto error; } if (*content_crc_ptr != ntohl(header[0])) { strlcpy(msg, msg_hash_to_str(MSG_CONTENT_CRC32S_DIFFER), sizeof(msg)); goto error; } if (netplay_impl_magic() != ntohl(header[1])) { strlcpy(msg, "Implementations differ. Make sure you're using exact same " "libretro implementations and RetroArch version.", sizeof(msg)); goto error; } /* Some cores only report the correct sram size late, so we can't actually * error out if the sram size seems wrong. */ sram_size = mem_info.size; remote_sram_size = ntohl(header[2]); if (sram_size != 0 && remote_sram_size != 0 && sram_size != remote_sram_size) { RARCH_WARN("Content SRAM sizes do not correspond.\n"); } /* We only care about platform magic if our core is quirky */ remote_pmagic = ntohl(header[3]); if ((netplay->quirks & NETPLAY_QUIRK_ENDIAN_DEPENDENT) && netplay_endian_mismatch(local_pmagic, remote_pmagic)) { RARCH_ERR("Endianness mismatch with an endian-sensitive core.\n"); strlcpy(msg, "This core does not support inter-architecture netplay " "between these systems.", sizeof(msg)); goto error; } if ((netplay->quirks & NETPLAY_QUIRK_PLATFORM_DEPENDENT) && (local_pmagic != remote_pmagic)) { RARCH_ERR("Platform mismatch with a platform-sensitive core.\n"); strlcpy(msg, "This core does not support inter-architecture netplay.", sizeof(msg)); goto error; } /* Clear any existing compression */ if (netplay->compression_stream) netplay->compression_backend->stream_free(netplay->compression_stream); if (netplay->decompression_stream) netplay->decompression_backend->stream_free(netplay->decompression_stream); /* Check what compression is supported */ compression = ntohl(header[4]); compression &= NETPLAY_COMPRESSION_SUPPORTED; if (compression & NETPLAY_COMPRESSION_ZLIB) { netplay->compression_backend = trans_stream_get_zlib_deflate_backend(); if (!netplay->compression_backend) netplay->compression_backend = trans_stream_get_pipe_backend(); } else { netplay->compression_backend = trans_stream_get_pipe_backend(); } netplay->decompression_backend = netplay->compression_backend->reverse; /* Allocate our compression stream */ netplay->compression_stream = netplay->compression_backend->stream_new(); netplay->decompression_stream = netplay->decompression_backend->stream_new(); if (!netplay->compression_stream || !netplay->decompression_stream) { RARCH_ERR("Failed to allocate compression transcoder!\n"); return false; } /* Client sends nickname first, server replies with nickname */ if (!is_server) { if (!netplay_send_nickname(netplay, netplay->fd)) { strlcpy(msg, msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_HOST), sizeof(msg)); goto error; } } if (!netplay_get_nickname(netplay, netplay->fd)) { if (is_server) strlcpy(msg, msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT), sizeof(msg)); else strlcpy(msg, msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME_FROM_HOST), sizeof(msg)); goto error; } if (is_server) { if (!netplay_send_nickname(netplay, netplay->fd)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_CLIENT)); return false; } } /* Server sends SRAM, client receives */ if (is_server) { /* Send SRAM data to the client */ sram = mem_info.data; if (!socket_send_all_blocking(netplay->fd, sram, sram_size, false)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_SRAM_DATA_TO_CLIENT)); return false; } } else { /* Get SRAM data from User 1. */ if (sram_size != 0 && sram_size == remote_sram_size) { sram = mem_info.data; if (!socket_receive_all_blocking(netplay->fd, sram, sram_size)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST)); return false; } } else if (remote_sram_size != 0) { /* We can't load this, but we still need to get rid of the data */ uint32_t quickbuf; while (remote_sram_size > 0) { if (!socket_receive_all_blocking(netplay->fd, &quickbuf, (remote_sram_size > sizeof(uint32_t)) ? sizeof(uint32_t) : remote_sram_size)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST)); return false; } if (remote_sram_size > sizeof(uint32_t)) remote_sram_size -= sizeof(uint32_t); else remote_sram_size = 0; } } } /* Reset our frame count so it's consistent with the server */ netplay->self_frame_count = netplay->other_frame_count = 0; netplay->read_frame_count = 1; for (i = 0; i < netplay->buffer_size; i++) { netplay->buffer[i].used = false; if (i == netplay->self_ptr) { netplay_delta_frame_ready(netplay, &netplay->buffer[i], 0); netplay->buffer[i].have_remote = true; netplay->other_ptr = i; netplay->read_ptr = NEXT_PTR(i); } else { netplay->buffer[i].used = false; } } if (is_server) { netplay_log_connection(&netplay->other_addr, 0, netplay->other_nick); } else { snprintf(msg, sizeof(msg), "%s: \"%s\"", msg_hash_to_str(MSG_CONNECTED_TO), netplay->other_nick); RARCH_LOG("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); } return true; error: if (msg[0]) { RARCH_ERR("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); } return false; }