bool netplay_get_nickname(netplay_t *netplay, int fd) { uint8_t nick_size; if (!socket_receive_all_blocking(fd, &nick_size, sizeof(nick_size))) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME_SIZE_FROM_HOST)); return false; } if (nick_size >= sizeof(netplay->other_nick)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_INVALID_NICKNAME_SIZE)); return false; } if (!socket_receive_all_blocking(fd, netplay->other_nick, nick_size)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME)); return false; } return true; }
static bool get_info_spectate(netplay_t *netplay) { size_t save_state_size, size; void *buf = NULL; uint32_t header[4] = {0}; char msg[512] = {0}; bool ret = true; if (!send_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to send nickname to host.\n"); return false; } if (!get_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to receive nickname from host.\n"); return false; } snprintf(msg, sizeof(msg), "Connected to \"%s\"", netplay->other_nick); rarch_main_msg_queue_push(msg, 1, 180, false); RARCH_LOG("%s\n", msg); if (!socket_receive_all_blocking(netplay->fd, header, sizeof(header))) { RARCH_ERR("Cannot get header from host.\n"); return false; } save_state_size = core.retro_serialize_size(); if (!bsv_parse_header(header, implementation_magic_value())) { RARCH_ERR("Received invalid BSV header from host.\n"); return false; } buf = malloc(save_state_size); if (!buf) return false; size = save_state_size; if (!socket_receive_all_blocking(netplay->fd, buf, size)) { RARCH_ERR("Failed to receive save state from host.\n"); free(buf); return false; } if (save_state_size) ret = core.retro_unserialize(buf, save_state_size); free(buf); return ret; }
static bool netplay_get_cmd(netplay_t *netplay) { uint32_t cmd, 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 has 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"); rarch_main_msg_queue_push("Netplay users are flipped.", 1, 180, false); return netplay_cmd_ack(netplay); default: break; } RARCH_ERR("Unknown netplay command received.\n"); return netplay_cmd_nak(netplay); }
bool np_get_info(netplay_t *netplay) { unsigned sram_size; uint32_t header[3]; const void *sram = NULL; global_t *global = global_get_ptr(); if (!socket_receive_all_blocking(netplay->fd, header, sizeof(header))) { RARCH_ERR("Failed to receive header from client.\n"); return false; } if (global->content_crc != ntohl(header[0])) { RARCH_ERR("Content CRC32s differ. Cannot use different games.\n"); return false; } if (np_impl_magic() != ntohl(header[1])) { RARCH_ERR("Implementations differ, make sure you're using exact same libretro implementations and RetroArch version.\n"); return false; } if (core.retro_get_memory_size(RETRO_MEMORY_SAVE_RAM) != ntohl(header[2])) { RARCH_ERR("Content SRAM sizes do not correspond.\n"); return false; } if (!np_get_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to get nickname from client.\n"); return false; } /* Send SRAM data to our User 2. */ sram = core.retro_get_memory_data(RETRO_MEMORY_SAVE_RAM); sram_size = core.retro_get_memory_size(RETRO_MEMORY_SAVE_RAM); if (!socket_send_all_blocking(netplay->fd, sram, sram_size)) { RARCH_ERR("Failed to send SRAM data to client.\n"); return false; } if (!np_send_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to send nickname to client.\n"); return false; } #ifndef HAVE_SOCKET_LEGACY np_log_connection(&netplay->other_addr, 0, netplay->other_nick); #endif return true; }
static bool netplay_get_response(netplay_t *netplay) { uint32_t response; if (!socket_receive_all_blocking(netplay->fd, &response, sizeof(response))) return false; return ntohl(response) == NETPLAY_CMD_ACK; }
bool netplay_send_info(netplay_t *netplay) { unsigned sram_size; retro_ctx_memory_info_t mem_info; char msg[512] = {0}; uint32_t *content_crc_ptr = NULL; void *sram = NULL; uint32_t header[3] = {0}; mem_info.id = RETRO_MEMORY_SAVE_RAM; core_get_memory(&mem_info); content_get_crc(&content_crc_ptr); header[0] = htonl(*content_crc_ptr); header[1] = htonl(netplay_impl_magic()); header[2] = htonl(mem_info.size); if (!socket_send_all_blocking(netplay->fd, header, sizeof(header), false)) return false; if (!netplay_send_nickname(netplay, netplay->fd)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_HOST)); return false; } /* Get SRAM data from User 1. */ sram = mem_info.data; sram_size = mem_info.size; 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; } if (!netplay_get_nickname(netplay, netplay->fd)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME_FROM_HOST)); return false; } 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; }
static int16_t netplay_get_spectate_input(netplay_t *netplay, bool port, unsigned device, unsigned idx, unsigned id) { int16_t inp; if (socket_receive_all_blocking(netplay->fd, (char*)&inp, sizeof(inp))) return swap_if_big16(inp); RARCH_ERR("Connection with host was cut.\n"); rarch_main_msg_queue_push("Connection with host was cut.", 1, 180, true); core.retro_set_input_state(netplay->cbs.state_cb); return netplay->cbs.state_cb(port, device, idx, id); }
bool np_send_info(netplay_t *netplay) { unsigned sram_size; retro_ctx_memory_info_t mem_info; char msg[512] = {0}; uint32_t *content_crc_ptr = NULL; void *sram = NULL; uint32_t header[3] = {0}; mem_info.id = RETRO_MEMORY_SAVE_RAM; core_ctl(CORE_CTL_RETRO_GET_MEMORY, &mem_info); content_ctl(CONTENT_CTL_GET_CRC, &content_crc_ptr); header[0] = htonl(*content_crc_ptr); header[1] = htonl(np_impl_magic()); header[2] = htonl(mem_info.size); if (!socket_send_all_blocking(netplay->fd, header, sizeof(header))) return false; if (!np_send_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to send nick to host.\n"); return false; } /* Get SRAM data from User 1. */ sram = mem_info.data; sram_size = mem_info.size; if (!socket_receive_all_blocking(netplay->fd, sram, sram_size)) { RARCH_ERR("Failed to receive SRAM data from host.\n"); return false; } if (!np_get_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to receive nick from host.\n"); return false; } snprintf(msg, sizeof(msg), "Connected to: \"%s\"", netplay->other_nick); RARCH_LOG("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); return true; }
bool np_get_nickname(netplay_t *netplay, int fd) { uint8_t nick_size; if (!socket_receive_all_blocking(fd, &nick_size, sizeof(nick_size))) { RARCH_ERR("Failed to receive nick size from host.\n"); return false; } if (nick_size >= sizeof(netplay->other_nick)) { RARCH_ERR("Invalid nick size.\n"); return false; } if (!socket_receive_all_blocking(fd, netplay->other_nick, nick_size)) { RARCH_ERR("Failed to receive nick.\n"); return false; } return true; }
static int16_t netplay_get_spectate_input(netplay_t *netplay, bool port, unsigned device, unsigned idx, unsigned id) { int16_t inp; retro_ctx_input_state_info_t input_info; if (socket_receive_all_blocking(netplay->fd, (char*)&inp, sizeof(inp))) return swap_if_big16(inp); RARCH_ERR("Connection with host was cut.\n"); runloop_msg_queue_push("Connection with host was cut.", 1, 180, true); input_info.cb = netplay->cbs.state_cb; core_ctl(CORE_CTL_RETRO_SET_INPUT_STATE, &input_info); return netplay->cbs.state_cb(port, device, idx, id); }
bool np_send_info(netplay_t *netplay) { unsigned sram_size; char msg[512] = {0}; void *sram = NULL; uint32_t header[3] = {0}; global_t *global = global_get_ptr(); header[0] = htonl(global->content_crc); header[1] = htonl(np_impl_magic()); header[2] = htonl(core.retro_get_memory_size(RETRO_MEMORY_SAVE_RAM)); if (!socket_send_all_blocking(netplay->fd, header, sizeof(header))) return false; if (!np_send_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to send nick to host.\n"); return false; } /* Get SRAM data from User 1. */ sram = core.retro_get_memory_data(RETRO_MEMORY_SAVE_RAM); sram_size = core.retro_get_memory_size(RETRO_MEMORY_SAVE_RAM); if (!socket_receive_all_blocking(netplay->fd, sram, sram_size)) { RARCH_ERR("Failed to receive SRAM data from host.\n"); return false; } if (!np_get_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to receive nick from host.\n"); return false; } snprintf(msg, sizeof(msg), "Connected to: \"%s\"", netplay->other_nick); RARCH_LOG("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); return true; }
static bool netplay_get_cmd(netplay_t *netplay) { uint32_t cmd; uint32_t flip_frame; uint32_t cmd_size; /* FIXME: This depends on delta_frame_ready */ netplay->timeout_cnt = 0; if (!socket_receive_all_blocking(netplay->fd, &cmd, sizeof(cmd))) return false; cmd = ntohl(cmd); if (!socket_receive_all_blocking(netplay->fd, &cmd_size, sizeof(cmd))) return false; cmd_size = ntohl(cmd_size); switch (cmd) { case NETPLAY_CMD_ACK: /* Why are we even bothering? */ return true; case NETPLAY_CMD_NAK: /* Disconnect now! */ return false; case NETPLAY_CMD_INPUT: { uint32_t buffer[WORDS_PER_FRAME]; unsigned i; if (cmd_size != WORDS_PER_FRAME * sizeof(uint32_t)) { RARCH_ERR("NETPLAY_CMD_INPUT received an unexpected payload size.\n"); return netplay_cmd_nak(netplay); } if (!socket_receive_all_blocking(netplay->fd, buffer, sizeof(buffer))) { RARCH_ERR("Failed to receive NETPLAY_CMD_INPUT input.\n"); return netplay_cmd_nak(netplay); } for (i = 0; i < WORDS_PER_FRAME; i++) buffer[i] = ntohl(buffer[i]); if (buffer[0] < netplay->read_frame_count) { /* We already had this, so ignore the new transmission */ return true; } else if (buffer[0] > netplay->read_frame_count) { /* Out of order = out of luck */ return netplay_cmd_nak(netplay); } /* The data's good! */ netplay->buffer[netplay->read_ptr].have_remote = true; memcpy(netplay->buffer[netplay->read_ptr].real_input_state, buffer + 1, sizeof(buffer) - sizeof(uint32_t)); netplay->read_ptr = NEXT_PTR(netplay->read_ptr); netplay->read_frame_count++; return true; } case NETPLAY_CMD_FLIP_PLAYERS: if (cmd_size != sizeof(uint32_t)) { RARCH_ERR("CMD_FLIP_PLAYERS received 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->read_frame_count) { 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; /* Force a rewind to assure the flip happens: This just prevents us * from skipping other past the flip because our prediction was * correct */ if (flip_frame < netplay->self_frame_count) netplay->force_rewind = true; RARCH_LOG("Netplay users are flipped.\n"); runloop_msg_queue_push("Netplay users are flipped.", 1, 180, false); return true; case NETPLAY_CMD_SPECTATE: RARCH_ERR("NETPLAY_CMD_SPECTATE unimplemented.\n"); return netplay_cmd_nak(netplay); case NETPLAY_CMD_DISCONNECT: hangup(netplay); return true; case NETPLAY_CMD_CRC: { uint32_t buffer[2]; size_t tmp_ptr = netplay->self_ptr; bool found = false; if (cmd_size != sizeof(buffer)) { RARCH_ERR("NETPLAY_CMD_CRC received unexpected payload size.\n"); return netplay_cmd_nak(netplay); } if (!socket_receive_all_blocking(netplay->fd, buffer, sizeof(buffer))) { RARCH_ERR("NETPLAY_CMD_CRC failed to receive payload.\n"); return netplay_cmd_nak(netplay); } buffer[0] = ntohl(buffer[0]); buffer[1] = ntohl(buffer[1]); /* Received a CRC for some frame. If we still have it, check if it * matched. This approach could be improved with some quick modular * arithmetic. */ do { if ( netplay->buffer[tmp_ptr].used && netplay->buffer[tmp_ptr].frame == buffer[0]) { found = true; break; } tmp_ptr = PREV_PTR(tmp_ptr); } while (tmp_ptr != netplay->self_ptr); if (!found) { /* Oh well, we got rid of it! */ return true; } if (buffer[0] <= netplay->other_frame_count) { /* We've already replayed up to this frame, so we can check it * directly */ uint32_t local_crc = netplay_delta_frame_crc( netplay, &netplay->buffer[tmp_ptr]); if (buffer[1] != local_crc) { /* Problem! */ netplay_cmd_request_savestate(netplay); } } else { /* We'll have to check it when we catch up */ netplay->buffer[tmp_ptr].crc = buffer[1]; } return true; } case NETPLAY_CMD_REQUEST_SAVESTATE: /* Delay until next frame so we don't send the savestate after the * input */ netplay->force_send_savestate = true; return true; case NETPLAY_CMD_LOAD_SAVESTATE: { uint32_t frame; /* Make sure we're ready for it */ if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) { if (!netplay->is_replay) { netplay->is_replay = true; netplay->replay_ptr = netplay->self_ptr; netplay->replay_frame_count = netplay->self_frame_count; netplay_wait_and_init_serialization(netplay); netplay->is_replay = false; } else { netplay_wait_and_init_serialization(netplay); } } /* There is a subtlty in whether the load comes before or after the * current frame: * * If it comes before the current frame, then we need to force a * rewind to that point. * * If it comes after the current frame, we need to jump ahead, then * (strangely) force a rewind to the frame we're already on, so it * gets loaded. This is just to avoid having reloading implemented in * too many places. */ if (cmd_size > netplay->state_size + sizeof(uint32_t)) { RARCH_ERR("CMD_LOAD_SAVESTATE received an unexpected save state size.\n"); return netplay_cmd_nak(netplay); } if (!socket_receive_all_blocking(netplay->fd, &frame, sizeof(frame))) { RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive savestate frame.\n"); return netplay_cmd_nak(netplay); } frame = ntohl(frame); if (frame != netplay->read_frame_count) { RARCH_ERR("CMD_LOAD_SAVESTATE loading a state out of order!\n"); return netplay_cmd_nak(netplay); } if (!socket_receive_all_blocking(netplay->fd, netplay->buffer[netplay->read_ptr].state, cmd_size - sizeof(uint32_t))) { RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive savestate.\n"); return netplay_cmd_nak(netplay); } /* Skip ahead if it's past where we are */ if (frame > netplay->self_frame_count) { /* This is squirrely: We need to assure that when we advance the * frame in post_frame, THEN we're referring to the frame to * load into. If we refer directly to read_ptr, then we'll end * up never reading the input for read_frame_count itself, which * will make the other side unhappy. */ netplay->self_ptr = PREV_PTR(netplay->read_ptr); netplay->self_frame_count = frame - 1; } /* And force rewind to it */ netplay->force_rewind = true; netplay->savestate_request_outstanding = false; netplay->other_ptr = netplay->read_ptr; netplay->other_frame_count = frame; return true; } case NETPLAY_CMD_PAUSE: netplay->remote_paused = true; return true; case NETPLAY_CMD_RESUME: netplay->remote_paused = false; return true; default: break; } RARCH_ERR("Unknown netplay command received.\n"); return netplay_cmd_nak(netplay); }
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); }
/** * netplay_recv * * Receive buffered or fresh data. * * Returns number of bytes returned, which may be short or 0, or -1 on error. */ ssize_t netplay_recv(struct socket_buffer *sbuf, int sockfd, void *buf, size_t len, bool block) { bool error; ssize_t recvd; /* Receive whatever we can into the buffer */ if (sbuf->end >= sbuf->start) { error = false; recvd = socket_receive_all_nonblocking(sockfd, &error, sbuf->data + sbuf->end, sbuf->bufsz - sbuf->end - ((sbuf->start == 0) ? 1 : 0)); if (recvd < 0 || error) return -1; sbuf->end += recvd; if (sbuf->end >= sbuf->bufsz) { sbuf->end = 0; error = false; recvd = socket_receive_all_nonblocking(sockfd, &error, sbuf->data, sbuf->start - 1); if (recvd < 0 || error) return -1; sbuf->end += recvd; } } else { error = false; recvd = socket_receive_all_nonblocking(sockfd, &error, sbuf->data + sbuf->end, sbuf->start - sbuf->end - 1); if (recvd < 0 || error) return -1; sbuf->end += recvd; } /* Now copy it into the reader */ if (sbuf->end >= sbuf->read || (sbuf->bufsz - sbuf->read) >= len) { size_t unread = buf_unread(sbuf); if (len <= unread) { memcpy(buf, sbuf->data + sbuf->read, len); sbuf->read += len; if (sbuf->read >= sbuf->bufsz) sbuf->read = 0; recvd = len; } else { memcpy(buf, sbuf->data + sbuf->read, unread); sbuf->read += unread; if (sbuf->read >= sbuf->bufsz) sbuf->read = 0; recvd = unread; } } else { /* Our read goes around the edge */ size_t chunka = sbuf->bufsz - sbuf->read, pchunklen = len - chunka, chunkb = (pchunklen >= sbuf->end) ? sbuf->end : pchunklen; memcpy(buf, sbuf->data + sbuf->read, chunka); memcpy((unsigned char *) buf + chunka, sbuf->data, chunkb); sbuf->read = chunkb; recvd = chunka + chunkb; } /* Perhaps block for more data */ if (block) { sbuf->start = sbuf->read; if (recvd < len) { if (!socket_receive_all_blocking(sockfd, (unsigned char *) buf + recvd, len - recvd)) return -1; recvd = len; } } return recvd; }
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; }
bool netplay_get_info(netplay_t *netplay) { unsigned sram_size; uint32_t header[3]; retro_ctx_memory_info_t mem_info; uint32_t *content_crc_ptr = NULL; const void *sram = NULL; if (!socket_receive_all_blocking(netplay->fd, header, sizeof(header))) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT)); return false; } content_get_crc(&content_crc_ptr); if (*content_crc_ptr != ntohl(header[0])) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_CONTENT_CRC32S_DIFFER)); return false; } if (netplay_impl_magic() != ntohl(header[1])) { RARCH_ERR("Implementations differ, make sure you're using exact same " "libretro implementations and RetroArch version.\n"); return false; } mem_info.id = RETRO_MEMORY_SAVE_RAM; core_get_memory(&mem_info); if (mem_info.size != ntohl(header[2])) { RARCH_ERR("Content SRAM sizes do not correspond.\n"); return false; } if (!netplay_get_nickname(netplay, netplay->fd)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT)); return false; } /* Send SRAM data to our User 2. */ sram = mem_info.data; sram_size = mem_info.size; 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; } if (!netplay_send_nickname(netplay, netplay->fd)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_CLIENT)); return false; } #ifndef HAVE_SOCKET_LEGACY netplay_log_connection(&netplay->other_addr, 0, netplay->other_nick); #endif return true; }
bool np_get_info(netplay_t *netplay) { unsigned sram_size; uint32_t header[3]; retro_ctx_memory_info_t mem_info; uint32_t *content_crc_ptr = NULL; const void *sram = NULL; if (!socket_receive_all_blocking(netplay->fd, header, sizeof(header))) { RARCH_ERR("Failed to receive header from client.\n"); return false; } content_ctl(CONTENT_CTL_GET_CRC, &content_crc_ptr); if (*content_crc_ptr != ntohl(header[0])) { RARCH_ERR("Content CRC32s differ. Cannot use different games.\n"); return false; } if (np_impl_magic() != ntohl(header[1])) { RARCH_ERR("Implementations differ, make sure you're using exact same " "libretro implementations and RetroArch version.\n"); return false; } mem_info.id = RETRO_MEMORY_SAVE_RAM; core_ctl(CORE_CTL_RETRO_GET_MEMORY, &mem_info); if (mem_info.size != ntohl(header[2])) { RARCH_ERR("Content SRAM sizes do not correspond.\n"); return false; } if (!np_get_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to get nickname from client.\n"); return false; } /* Send SRAM data to our User 2. */ sram = mem_info.data; sram_size = mem_info.size; if (!socket_send_all_blocking(netplay->fd, sram, sram_size)) { RARCH_ERR("Failed to send SRAM data to client.\n"); return false; } if (!np_send_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to send nickname to client.\n"); return false; } #ifndef HAVE_SOCKET_LEGACY np_log_connection(&netplay->other_addr, 0, netplay->other_nick); #endif return true; }