// Grab our own input state and send this over the network. static bool get_self_input_state(netplay_t *handle) { unsigned i; struct delta_frame *ptr = &handle->buffer[handle->self_ptr]; uint32_t state = 0; if (handle->frame_count > 0) // 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 = handle->cbs.state_cb; for (i = 0; i < RARCH_FIRST_META_KEY; i++) { int16_t tmp = cb(g_settings.input.netplay_client_swap_input ? 0 : !handle->port, RETRO_DEVICE_JOYPAD, 0, i); state |= tmp ? 1 << i : 0; } } memmove(handle->packet_buffer, handle->packet_buffer + 2, sizeof (handle->packet_buffer) - 2 * sizeof(uint32_t)); handle->packet_buffer[(UDP_FRAME_PACKETS - 1) * 2] = htonl(handle->frame_count); handle->packet_buffer[(UDP_FRAME_PACKETS - 1) * 2 + 1] = htonl(state); if (!send_chunk(handle)) { warn_hangup(); handle->has_connection = false; return false; } ptr->self_state = state; handle->self_ptr = NEXT_PTR(handle->self_ptr); return true; }
static bool send_chunk(netplay_t *netplay) { const struct sockaddr *addr = NULL; if (netplay->addr) addr = netplay->addr->ai_addr; else if (netplay->has_client_addr) addr = (const struct sockaddr*)&netplay->their_addr; if (addr) { if (sendto(netplay->udp_fd, (const char*)netplay->packet_buffer, sizeof(netplay->packet_buffer), 0, addr, #ifdef ANDROID sizeof(struct sockaddr_in6) #else sizeof(struct sockaddr_in) #endif ) != sizeof(netplay->packet_buffer)) { warn_hangup(); netplay->has_connection = false; return false; } } return true; }
static bool send_chunk(netplay_t *netplay) { const struct sockaddr *addr = NULL; if (netplay->addr) addr = netplay->addr->ai_addr; else if (netplay->has_client_addr) addr = (const struct sockaddr*)&netplay->their_addr; if (addr) { ssize_t bytes_sent; #ifdef HAVE_IPV6 bytes_sent = (sendto(netplay->udp_fd, (const char*)netplay->packet_buffer, sizeof(netplay->packet_buffer), 0, addr, sizeof(struct sockaddr_in6))); #else bytes_sent = (sendto(netplay->udp_fd, (const char*)netplay->packet_buffer, sizeof(netplay->packet_buffer), 0, addr, sizeof(struct sockaddr_in))); #endif if (bytes_sent != sizeof(netplay->packet_buffer)) { warn_hangup(); netplay->has_connection = false; return false; } } return true; }
static int poll_input(netplay_t *netplay, bool block) { int max_fd = (netplay->fd > netplay->udp_fd ? netplay->fd : netplay->udp_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; netplay->timeout_cnt++; FD_ZERO(&fds); FD_SET(netplay->udp_fd, &fds); FD_SET(netplay->fd, &fds); if (socket_select(max_fd, &fds, NULL, NULL, &tmp_tv) < 0) return -1; /* Somewhat hacky, * but we aren't using the TCP connection for anything useful atm. */ if (FD_ISSET(netplay->fd, &fds) && !netplay_get_cmd(netplay)) return -1; if (FD_ISSET(netplay->udp_fd, &fds)) return 1; if (!block) continue; if (!send_chunk(netplay)) { warn_hangup(); netplay->has_connection = false; return -1; } RARCH_LOG("Network is stalling, resending packet... Count %u of %d ...\n", netplay->timeout_cnt, MAX_RETRIES); } while ((netplay->timeout_cnt < MAX_RETRIES) && block); if (block) return -1; return 0; }
static int poll_input(netplay_t *handle, bool block) { int max_fd = (handle->fd > handle->udp_fd ? handle->fd : handle->udp_fd) + 1; struct timeval tv = {0}; tv.tv_sec = 0; tv.tv_usec = block ? (RETRY_MS * 1000) : 0; do { handle->timeout_cnt++; // 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; fd_set fds; FD_ZERO(&fds); FD_SET(handle->udp_fd, &fds); FD_SET(handle->fd, &fds); if (select(max_fd, &fds, NULL, NULL, &tmp_tv) < 0) return -1; // Somewhat hacky, // but we aren't using the TCP connection for anything useful atm. if (FD_ISSET(handle->fd, &fds) && !netplay_get_cmd(handle)) return -1; if (FD_ISSET(handle->udp_fd, &fds)) return 1; if (block && !send_chunk(handle)) { warn_hangup(); handle->has_connection = false; return -1; } if (block) { RARCH_LOG("Network is stalling, resending packet... Count %u of %d ...\n", handle->timeout_cnt, MAX_RETRIES); } } while ((handle->timeout_cnt < MAX_RETRIES) && block); if (block) return -1; return 0; }
/** * 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 = 0; struct delta_frame *ptr = &netplay->buffer[netplay->self_ptr]; driver_t *driver = driver_get_ptr(); settings_t *settings = config_get_ptr(); if (!driver->block_libretro_input && netplay->frame_count > 0) { unsigned i; /* 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->input.netplay_client_swap_input ? 0 : !netplay->port, RETRO_DEVICE_JOYPAD, 0, i); state |= tmp ? 1 << i : 0; } for (i = RARCH_FIRST_CUSTOM_BIND; i < RARCH_FIRST_META_KEY; i++) { int16_t tmp = cb(settings->input.netplay_client_swap_input ? 0 : !netplay->port, RETRO_DEVICE_ANALOG, 0, i); state |= tmp ? 1 << i : 0; } } memmove(netplay->packet_buffer, netplay->packet_buffer + 2, sizeof (netplay->packet_buffer) - 2 * sizeof(uint32_t)); netplay->packet_buffer[(UDP_FRAME_PACKETS - 1) * 2] = htonl(netplay->frame_count); netplay->packet_buffer[(UDP_FRAME_PACKETS - 1) * 2 + 1] = htonl(state); if (!send_chunk(netplay)) { warn_hangup(); netplay->has_connection = false; return false; } ptr->self_state = state; netplay->self_ptr = NEXT_PTR(netplay->self_ptr); return true; }
static bool send_chunk(netplay_t *handle) { const struct sockaddr *addr = NULL; if (handle->addr) addr = handle->addr->ai_addr; else if (handle->has_client_addr) addr = (const struct sockaddr*)&handle->their_addr; if (addr) { if (sendto(handle->udp_fd, CONST_CAST handle->packet_buffer, sizeof(handle->packet_buffer), 0, addr, sizeof(struct sockaddr)) != sizeof(handle->packet_buffer)) { warn_hangup(); handle->has_connection = false; return false; } } return true; }
// Poll network to see if we have anything new. If our network buffer is full, we simply have to block for new input data. static bool netplay_poll(netplay_t *handle) { if (!handle->has_connection) return false; handle->can_poll = false; if (!get_self_input_state(handle)) return false; // We skip reading the first frame so the host has a chance to grab our host info so we don't block forever :') if (handle->frame_count == 0) { handle->buffer[0].used_real = true; handle->buffer[0].is_simulated = false; handle->buffer[0].real_input_state = 0; handle->read_ptr = NEXT_PTR(handle->read_ptr); handle->read_frame_count++; return true; } // We might have reached the end of the buffer, where we simply have to block. int res = poll_input(handle, handle->other_ptr == handle->self_ptr); if (res == -1) { handle->has_connection = false; warn_hangup(); return false; } if (res == 1) { uint32_t first_read = handle->read_frame_count; do { uint32_t buffer[UDP_FRAME_PACKETS * 2]; if (!receive_data(handle, buffer, sizeof(buffer))) { warn_hangup(); handle->has_connection = false; return false; } parse_packet(handle, buffer, UDP_FRAME_PACKETS); } while ((handle->read_frame_count <= handle->frame_count) && poll_input(handle, (handle->other_ptr == handle->self_ptr) && (first_read == handle->read_frame_count)) == 1); } else { // Cannot allow this. Should not happen though. if (handle->self_ptr == handle->other_ptr) { warn_hangup(); return false; } } if (handle->read_ptr != handle->self_ptr) simulate_input(handle); else handle->buffer[PREV_PTR(handle->self_ptr)].used_real = true; return true; }
/** * netplay_poll: * @netplay : pointer to netplay object * * Polls network to see if we have anything new. If our * network buffer is full, we simply have to block * for new input data. * * Returns: true (1) if successful, otherwise false (0). **/ static bool netplay_poll(netplay_t *netplay) { int res; if (!netplay->has_connection) return false; netplay->can_poll = false; if (!get_self_input_state(netplay)) return false; /* We skip reading the first frame so the host has a chance to grab * our host info so we don't block forever :') */ if (netplay->frame_count == 0) { netplay->buffer[0].used_real = true; netplay->buffer[0].is_simulated = false; memset(netplay->buffer[0].real_input_state, 0, sizeof(netplay->buffer[0].real_input_state)); netplay->read_ptr = NEXT_PTR(netplay->read_ptr); netplay->read_frame_count++; return true; } /* We might have reached the end of the buffer, where we * simply have to block. */ res = poll_input(netplay, netplay->other_ptr == netplay->self_ptr); if (res == -1) { netplay->has_connection = false; warn_hangup(); return false; } if (res == 1) { uint32_t first_read = netplay->read_frame_count; do { uint32_t buffer[UDP_FRAME_PACKETS * UDP_WORDS_PER_FRAME]; if (!receive_data(netplay, buffer, sizeof(buffer))) { warn_hangup(); netplay->has_connection = false; return false; } parse_packet(netplay, buffer, UDP_FRAME_PACKETS); } while ((netplay->read_frame_count <= netplay->frame_count) && poll_input(netplay, (netplay->other_ptr == netplay->self_ptr) && (first_read == netplay->read_frame_count)) == 1); } else { /* Cannot allow this. Should not happen though. */ if (netplay->self_ptr == netplay->other_ptr) { warn_hangup(); return false; } } if (netplay->read_ptr != netplay->self_ptr) simulate_input(netplay); else netplay->buffer[PREV_PTR(netplay->self_ptr)].used_real = true; return true; }
static bool netplay_get_cmd(netplay_t *netplay) { uint32_t cmd; uint32_t flip_frame; size_t cmd_size; if (!socket_receive_all_blocking(netplay->fd, &cmd, sizeof(cmd))) return false; cmd = ntohl(cmd); cmd_size = cmd & 0xffff; cmd = cmd >> 16; switch (cmd) { case NETPLAY_CMD_FLIP_PLAYERS: if (cmd_size != sizeof(uint32_t)) { RARCH_ERR("CMD_FLIP_PLAYERS recieved an unexpected command size.\n"); return netplay_cmd_nak(netplay); } if (!socket_receive_all_blocking( netplay->fd, &flip_frame, sizeof(flip_frame))) { RARCH_ERR("Failed to receive CMD_FLIP_PLAYERS argument.\n"); return netplay_cmd_nak(netplay); } flip_frame = ntohl(flip_frame); if (flip_frame < netplay->flip_frame) { RARCH_ERR("Host asked us to flip users in the past. Not possible ...\n"); return netplay_cmd_nak(netplay); } netplay->flip ^= true; netplay->flip_frame = flip_frame; RARCH_LOG("Netplay users are flipped.\n"); runloop_msg_queue_push("Netplay users are flipped.", 1, 180, false); return netplay_cmd_ack(netplay); case NETPLAY_CMD_SPECTATE: RARCH_ERR("NETPLAY_CMD_SPECTATE unimplemented.\n"); return netplay_cmd_nak(netplay); case NETPLAY_CMD_DISCONNECT: warn_hangup(); return netplay_cmd_ack(netplay); case NETPLAY_CMD_LOAD_SAVESTATE: RARCH_ERR("NETPLAY_CMD_LOAD_SAVESTATE unimplemented.\n"); return netplay_cmd_nak(netplay); case NETPLAY_CMD_PAUSE: event_cmd_ctl(EVENT_CMD_PAUSE, NULL); return netplay_cmd_ack(netplay); case NETPLAY_CMD_RESUME: event_cmd_ctl(EVENT_CMD_UNPAUSE, NULL); return netplay_cmd_ack(netplay); default: break; } RARCH_ERR("Unknown netplay command received.\n"); return netplay_cmd_nak(netplay); }
/** * 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[UDP_WORDS_PER_FRAME - 1] = {0}; struct delta_frame *ptr = &netplay->buffer[netplay->self_ptr]; settings_t *settings = config_get_ptr(); if (!input_driver_ctl(RARCH_INPUT_CTL_IS_LIBRETRO_INPUT_BLOCKED, NULL) && netplay->frame_count > 0) { unsigned i; /* 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->input.netplay_client_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->input.netplay_client_swap_input ? 0 : !netplay->port, RETRO_DEVICE_ANALOG, i, 0); int16_t tmp_y = cb(settings->input.netplay_client_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 { * ; To compat packet losses, send input in a sliding window * frame redundancy_frames[UDP_FRAME_PACKETS]; * } */ memmove(netplay->packet_buffer, netplay->packet_buffer + UDP_WORDS_PER_FRAME, sizeof (netplay->packet_buffer) - UDP_WORDS_PER_FRAME * sizeof(uint32_t)); netplay->packet_buffer[(UDP_FRAME_PACKETS - 1) * UDP_WORDS_PER_FRAME] = htonl(netplay->frame_count); netplay->packet_buffer[(UDP_FRAME_PACKETS - 1) * UDP_WORDS_PER_FRAME + 1] = htonl(state[0]); netplay->packet_buffer[(UDP_FRAME_PACKETS - 1) * UDP_WORDS_PER_FRAME + 2] = htonl(state[1]); netplay->packet_buffer[(UDP_FRAME_PACKETS - 1) * UDP_WORDS_PER_FRAME + 3] = htonl(state[2]); if (!send_chunk(netplay)) { warn_hangup(); netplay->has_connection = false; return false; } memcpy(ptr->self_state, state, sizeof(state)); netplay->self_ptr = NEXT_PTR(netplay->self_ptr); return true; }