/* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2021 - 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 . */ #if defined(_MSC_VER) && !defined(_XBOX) #pragma comment(lib, "ws2_32") #endif #include #include #include #include #include #include #include #include #include "netplay_private.h" #include "netplay_discovery.h" #include "../../autosave.h" #include "../../configuration.h" #include "../../driver.h" #include "../../retroarch.h" #include "../../command.h" #include "../../tasks/tasks_internal.h" #include "../../input/input_driver.h" #if defined(AF_INET6) && !defined(HAVE_SOCKET_LEGACY) && !defined(_3DS) #define HAVE_INET6 1 #endif #ifdef HAVE_DISCORD #include "../discord.h" /* TODO/FIXME - global public variable */ extern bool discord_is_inited; #endif struct vote_count { uint16_t votes[32]; }; /* The mapping of keys from netplay (network) to libretro (host) */ const uint16_t netplay_key_ntoh_mapping[] = { (uint16_t) RETROK_UNKNOWN, #define K(k) (uint16_t) RETROK_ ## k, #define KL(k,l) (uint16_t) l, #include "netplay_keys.h" #undef KL #undef K 0 }; /* TODO/FIXME - static global variables */ static uint16_t netplay_mapping[RETROK_LAST]; /* The mapping of keys from libretro (host) to netplay (network) */ uint32_t netplay_key_hton(unsigned key) { if (key >= RETROK_LAST) return NETPLAY_KEY_UNKNOWN; return netplay_mapping[key]; } /* Because the hton keymapping has to be generated, call this before using * netplay_key_hton */ void netplay_key_hton_init(void) { static bool mapping_defined = false; if (!mapping_defined) { uint16_t i; for (i = 0; i < NETPLAY_KEY_LAST; i++) netplay_mapping[NETPLAY_KEY_NTOH(i)] = i; mapping_defined = true; } } static void clear_input(netplay_input_state_t istate) { while (istate) { istate->used = false; istate = istate->next; } } /** * netplay_delta_frame_ready * * Prepares, if possible, a delta frame for input, and reports whether it is * ready. * * Returns: True if the delta frame is ready for input at the given frame, * false otherwise. */ bool netplay_delta_frame_ready(netplay_t *netplay, struct delta_frame *delta, uint32_t frame) { size_t i; if (delta->used) { if (delta->frame == frame) return true; /* We haven't even replayed this frame yet, * so we can't overwrite it! */ if (netplay->other_frame_count <= delta->frame) return false; } delta->used = true; delta->frame = frame; delta->crc = 0; for (i = 0; i < MAX_INPUT_DEVICES; i++) { clear_input(delta->resolved_input[i]); clear_input(delta->real_input[i]); clear_input(delta->simlated_input[i]); } delta->have_local = false; for (i = 0; i < MAX_CLIENTS; i++) delta->have_real[i] = false; return true; } /** * netplay_delta_frame_crc * * Get the CRC for the serialization of this frame. */ static uint32_t netplay_delta_frame_crc(netplay_t *netplay, struct delta_frame *delta) { return encoding_crc32(0L, (const unsigned char*)delta->state, netplay->state_size); } /* * Free an input state list */ static void free_input_state(netplay_input_state_t *list) { netplay_input_state_t cur, next; cur = *list; while (cur) { next = cur->next; free(cur); cur = next; } *list = NULL; } /** * netplay_delta_frame_free * * Free a delta frame's dependencies */ static void netplay_delta_frame_free(struct delta_frame *delta) { uint32_t i; if (delta->state) { free(delta->state); delta->state = NULL; } for (i = 0; i < MAX_INPUT_DEVICES; i++) { free_input_state(&delta->resolved_input[i]); free_input_state(&delta->real_input[i]); free_input_state(&delta->simlated_input[i]); } } /** * netplay_input_state_for * * Get an input state for a particular client */ netplay_input_state_t netplay_input_state_for( netplay_input_state_t *list, uint32_t client_num, size_t size, bool must_create, bool must_not_create) { netplay_input_state_t ret; while (*list) { ret = *list; if (!ret->used && !must_not_create && ret->size == size) { ret->client_num = client_num; ret->used = true; memset(ret->data, 0, size*sizeof(uint32_t)); return ret; } else if (ret->used && ret->client_num == client_num) { if (!must_create && ret->size == size) return ret; return NULL; } list = &(ret->next); } if (must_not_create) return NULL; /* Couldn't find a slot, allocate a fresh one */ if (size > 1) ret = (netplay_input_state_t)calloc(1, sizeof(struct netplay_input_state) + (size-1) * sizeof(uint32_t)); else ret = (netplay_input_state_t)calloc(1, sizeof(struct netplay_input_state)); if (!ret) return NULL; *list = ret; ret->client_num = client_num; ret->used = true; ret->size = (uint32_t)size; return ret; } /** * netplay_expected_input_size * * Size in words for a given set of devices. */ uint32_t netplay_expected_input_size(netplay_t *netplay, uint32_t devices) { uint32_t ret = 0, device; for (device = 0; device < MAX_INPUT_DEVICES; device++) { if (!(devices & (1<config_devices[device]&RETRO_DEVICE_MASK) { /* These are all essentially magic numbers, but each device has a * fixed size, documented in network/netplay/README */ case RETRO_DEVICE_JOYPAD: ret += 1; break; case RETRO_DEVICE_MOUSE: ret += 2; break; case RETRO_DEVICE_KEYBOARD: ret += 5; break; case RETRO_DEVICE_LIGHTGUN: ret += 2; break; case RETRO_DEVICE_ANALOG: ret += 3; break; default: break; /* Unsupported */ } } return ret; } static size_t buf_used(struct socket_buffer *sbuf) { if (sbuf->end < sbuf->start) { size_t newend = sbuf->end; while (newend < sbuf->start) newend += sbuf->bufsz; return newend - sbuf->start; } return sbuf->end - sbuf->start; } static size_t buf_unread(struct socket_buffer *sbuf) { if (sbuf->end < sbuf->read) { size_t newend = sbuf->end; while (newend < sbuf->read) newend += sbuf->bufsz; return newend - sbuf->read; } return sbuf->end - sbuf->read; } static size_t buf_remaining(struct socket_buffer *sbuf) { return sbuf->bufsz - buf_used(sbuf) - 1; } /** * netplay_init_socket_buffer * * Initialize a new socket buffer. */ static bool netplay_init_socket_buffer( struct socket_buffer *sbuf, size_t size) { sbuf->data = (unsigned char*)malloc(size); if (!sbuf->data) return false; sbuf->bufsz = size; sbuf->start = sbuf->read = sbuf->end = 0; return true; } /** * netplay_resize_socket_buffer * * Resize the given socket_buffer's buffer to the requested size. */ static bool netplay_resize_socket_buffer( struct socket_buffer *sbuf, size_t newsize) { unsigned char *newdata = (unsigned char*)malloc(newsize); if (!newdata) return false; /* Copy in the old data */ if (sbuf->end < sbuf->start) { memcpy(newdata, sbuf->data + sbuf->start, sbuf->bufsz - sbuf->start); memcpy(newdata + sbuf->bufsz - sbuf->start, sbuf->data, sbuf->end); } else if (sbuf->end > sbuf->start) memcpy(newdata, sbuf->data + sbuf->start, sbuf->end - sbuf->start); /* Adjust our read offset */ if (sbuf->read < sbuf->start) sbuf->read += sbuf->bufsz - sbuf->start; else sbuf->read -= sbuf->start; /* Adjust start and end */ sbuf->end = buf_used(sbuf); sbuf->start = 0; /* Free the old one and replace it with the new one */ free(sbuf->data); sbuf->data = newdata; sbuf->bufsz = newsize; return true; } /** * netplay_deinit_socket_buffer * * Free a socket buffer. */ static void netplay_deinit_socket_buffer(struct socket_buffer *sbuf) { if (sbuf->data) free(sbuf->data); } #if 0 static void netplay_clear_socket_buffer(struct socket_buffer *sbuf) { sbuf->start = sbuf->read = sbuf->end = 0; } #endif /** * netplay_send * * Queue the given data for sending. */ bool netplay_send( struct socket_buffer *sbuf, int sockfd, const void *buf, size_t len) { if (buf_remaining(sbuf) < len) { /* Need to force a blocking send */ if (!netplay_send_flush(sbuf, sockfd, true)) return false; } if (buf_remaining(sbuf) < len) { /* Can only be that this is simply too big * for our buffer, in which case we just * need to do a blocking send */ if (!socket_send_all_blocking(sockfd, buf, len, false)) return false; return true; } /* Copy it into our buffer */ if (sbuf->bufsz - sbuf->end < len) { /* Half at a time */ size_t chunka = sbuf->bufsz - sbuf->end, chunkb = len - chunka; memcpy(sbuf->data + sbuf->end, buf, chunka); memcpy(sbuf->data, (const unsigned char *)buf + chunka, chunkb); sbuf->end = chunkb; } else { /* Straight in */ memcpy(sbuf->data + sbuf->end, buf, len); sbuf->end += len; } return true; } /** * netplay_send_flush * * Flush unsent data in the given socket buffer, blocking to do so if * requested. * * Returns false only on socket failures, true otherwise. */ bool netplay_send_flush(struct socket_buffer *sbuf, int sockfd, bool block) { ssize_t sent; if (buf_used(sbuf) == 0) return true; if (sbuf->end > sbuf->start) { /* Usual case: Everything's in order */ if (block) { if (!socket_send_all_blocking( sockfd, sbuf->data + sbuf->start, buf_used(sbuf), true)) return false; sbuf->start = sbuf->end = 0; } else { sent = socket_send_all_nonblocking( sockfd, sbuf->data + sbuf->start, buf_used(sbuf), true); if (sent < 0) return false; sbuf->start += sent; if (sbuf->start == sbuf->end) sbuf->start = sbuf->end = 0; } } else { /* Unusual case: Buffer overlaps break */ if (block) { if (!socket_send_all_blocking( sockfd, sbuf->data + sbuf->start, sbuf->bufsz - sbuf->start, true)) return false; sbuf->start = 0; return netplay_send_flush(sbuf, sockfd, true); } else { sent = socket_send_all_nonblocking( sockfd, sbuf->data + sbuf->start, sbuf->bufsz - sbuf->start, true); if (sent < 0) return false; sbuf->start += sent; if (sbuf->start >= sbuf->bufsz) { sbuf->start = 0; return netplay_send_flush(sbuf, sockfd, false); } } } return true; } /** * 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) { ssize_t recvd; bool error = false; /* Receive whatever we can into the buffer */ if (sbuf->end >= sbuf->start) { 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 { 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 < 0 || recvd < (ssize_t) len) { if (!socket_receive_all_blocking( sockfd, (unsigned char *)buf + recvd, len - recvd)) return -1; recvd = len; } } return recvd; } /** * netplay_recv_reset * * Reset our recv buffer so that future netplay_recvs * will read the same data again. */ void netplay_recv_reset(struct socket_buffer *sbuf) { sbuf->read = sbuf->start; } /** * netplay_recv_flush * * Flush our recv buffer, so a future netplay_recv_reset will reset to this * point. */ void netplay_recv_flush(struct socket_buffer *sbuf) { sbuf->start = sbuf->read; } /** * netplay_cmd_crc * * Send a CRC command to all active clients. */ static bool netplay_cmd_crc(netplay_t *netplay, struct delta_frame *delta) { size_t i; uint32_t payload[2]; bool success = true; payload[0] = htonl(delta->frame); payload[1] = htonl(delta->crc); for (i = 0; i < netplay->connections_size; i++) { if (netplay->connections[i].active && netplay->connections[i].mode >= NETPLAY_CONNECTION_CONNECTED) success = netplay_send_raw_cmd(netplay, &netplay->connections[i], NETPLAY_CMD_CRC, payload, sizeof(payload)) && success; } return success; } /** * netplay_cmd_request_savestate * * Send a savestate request command. */ static bool netplay_cmd_request_savestate(netplay_t *netplay) { if (netplay->connections_size == 0 || !netplay->connections[0].active || netplay->connections[0].mode < NETPLAY_CONNECTION_CONNECTED) return false; if (netplay->savestate_request_outstanding) return true; netplay->savestate_request_outstanding = true; return netplay_send_raw_cmd(netplay, &netplay->connections[0], NETPLAY_CMD_REQUEST_SAVESTATE, NULL, 0); } /** * netplay_cmd_stall * * Send a stall command. */ static bool netplay_cmd_stall(netplay_t *netplay, struct netplay_connection *connection, uint32_t frames) { frames = htonl(frames); return netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_STALL, &frames, sizeof(frames)); } static void handle_play_spectate(netplay_t *netplay, uint32_t client_num, struct netplay_connection *connection, uint32_t cmd, uint32_t cmd_size, uint32_t *payload); #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 << client))) continue; if ((netplay->connected_slaves & (1 << client))) continue; if (netplay->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; } } } /** * 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 << simstate->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 << client))) continue; simstate = netplay_device_client_state( netplay, simframe, device, client); if (!simstate) continue; client_count++; for (word = 0; word < resstate->size; word++) { if (!digital[word]) continue; for (bit = 0; bit < 32; bit++) { if (!(digital[word] & (1 << bit))) continue; if (simstate->data[word] & (1 << bit)) votes[word].votes[bit]++; } } } /* Now count all the bits */ client_count /= 2; for (word = 0; word < resstate->size; word++) { for (bit = 0; bit < 32; bit++) { if (votes[word].votes[bit] > client_count) resstate->data[word] |= (1 << bit); } } } else /* !VOTE */ { for (client = 0; client < MAX_CLIENTS; client++) { if (!(clients & (1 << client))) continue; simstate = netplay_device_client_state( netplay, simframe, device, client); if (!simstate) continue; for (word = 0; word < resstate->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 << bit))) continue; switch (share_mode) { case NETPLAY_SHARE_DIGITAL_XOR: resstate->data[word] ^= part & (1 << bit); break; default: resstate->data[word] |= part & (1 << bit); } } } } } } } /** * merge_analog_part * @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 * @word : word to merge * @bit : first bit to merge */ static void merge_analog_part(netplay_t *netplay, netplay_input_state_t resstate, struct delta_frame *simframe, uint32_t device, uint32_t clients, uint32_t word, uint8_t bit) { netplay_input_state_t simstate; uint32_t client, client_count = 0; uint8_t share_mode = netplay->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 << simstate->client_num; } for (client = 0; client < MAX_CLIENTS; client++) { if (!(clients & (1 << client))) continue; simstate = netplay_device_client_state( netplay, simframe, device, client); if (!simstate) continue; client_count++; new_value = (int16_t) ((simstate->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 << simstate->client_num; } for (client = 0; client < MAX_CLIENTS; client++) { if (!(clients & (1 << client))) continue; /* Resolve this client-device */ simstate = netplay_input_state_for( &simframe->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 = (UINT32_C(1) << RETRO_DEVICE_ID_JOYPAD_UP) | (UINT32_C(1) << RETRO_DEVICE_ID_JOYPAD_DOWN) | (UINT32_C(1) << RETRO_DEVICE_ID_JOYPAD_LEFT) | (UINT32_C(1) << RETRO_DEVICE_ID_JOYPAD_RIGHT); simstate->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 = digital_common; if (dtype == RETRO_DEVICE_KEYBOARD) digital = digital_keyboard; 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) { if (netplay->state_size) delta->crc = netplay_delta_frame_crc(netplay, delta); else delta->crc = 0; netplay_cmd_crc(netplay, delta); } } else if (delta->crc && netplay->crcs_valid) { /* We have a remote CRC, so check it */ uint32_t local_crc = 0; if (netplay->state_size) 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) { 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); } } process: netplay->can_poll = true; input_poll_net(); 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; /* 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 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_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->state_size ? netplay_delta_frame_crc(netplay, ptr) : 0); 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->state_size ? netplay_delta_frame_crc(netplay, ptr) : 0); } #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 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 << client))) continue; if (netplay->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) { 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++) { 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; } #if 0 #define DEBUG_NETPLAY_STEPS 1 static void print_state(netplay_t *netplay) { char msg[512]; size_t cur = 0; uint32_t client; #define APPEND(out) cur += snprintf out #define M msg + cur, sizeof(msg) - cur APPEND((M, "NETPLAY: S:%u U:%u O:%u", netplay->self_frame_count, netplay->unread_frame_count, netplay->other_frame_count)); if (!netplay->is_server) APPEND((M, " H:%u", netplay->server_frame_count)); for (client = 0; client < MAX_USERS; client++) { if ((netplay->connected_players & (1<read_frame_count[client])); } msg[sizeof(msg)-1] = '\0'; RARCH_LOG("[netplay] %s\n", msg); #undef APPEND #undef M } #endif /** * remote_unpaused * * Mark a particular remote connection as unpaused and, if relevant, inform * every one else that they may resume. */ static void remote_unpaused(netplay_t *netplay, struct netplay_connection *connection) { size_t i; connection->paused = false; netplay->remote_paused = false; for (i = 0; i < netplay->connections_size; i++) { struct netplay_connection *sc = &netplay->connections[i]; if (sc->active && sc->paused) { netplay->remote_paused = true; break; } } if (!netplay->remote_paused && !netplay->local_paused) netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_RESUME, NULL, 0); } /** * netplay_hangup: * * Disconnects an active Netplay connection due to an error */ void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection) { char msg[512]; const char *dmsg; size_t i; if (!netplay) return; if (!connection->active) return; msg[0] = msg[sizeof(msg)-1] = '\0'; dmsg = msg; /* Report this disconnection */ if (netplay->is_server) { if (connection->nick[0]) snprintf(msg, sizeof(msg)-1, msg_hash_to_str(MSG_NETPLAY_SERVER_NAMED_HANGUP), connection->nick); else dmsg = msg_hash_to_str(MSG_NETPLAY_SERVER_HANGUP); } else { dmsg = msg_hash_to_str(MSG_NETPLAY_CLIENT_HANGUP); #ifdef HAVE_DISCORD if (discord_is_inited) { discord_userdata_t userdata; userdata.status = DISCORD_PRESENCE_NETPLAY_NETPLAY_STOPPED; command_event(CMD_EVENT_DISCORD_UPDATE, &userdata); } #endif netplay->is_connected = false; } RARCH_LOG("[netplay] %s\n", dmsg); runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); socket_close(connection->fd); connection->active = false; netplay_deinit_socket_buffer(&connection->send_packet_buffer); netplay_deinit_socket_buffer(&connection->recv_packet_buffer); if (!netplay->is_server) { netplay->self_mode = NETPLAY_CONNECTION_NONE; netplay->connected_players &= (1L<self_client_num); for (i = 0; i < MAX_CLIENTS; i++) { if (i == netplay->self_client_num) continue; netplay->client_devices[i] = 0; } for (i = 0; i < MAX_INPUT_DEVICES; i++) netplay->device_clients[i] &= (1L<self_client_num); netplay->stall = NETPLAY_STALL_NONE; } else { uint32_t client_num = (uint32_t)(connection - netplay->connections + 1); /* Mark the player for removal */ if (connection->mode == NETPLAY_CONNECTION_PLAYING || connection->mode == NETPLAY_CONNECTION_SLAVE) { /* This special mode keeps the connection object alive long enough to * send the disconnection message at the correct time */ connection->mode = NETPLAY_CONNECTION_DELAYED_DISCONNECT; connection->delay_frame = netplay->read_frame_count[client_num]; /* Mark them as not playing anymore */ netplay->connected_players &= ~(1L<connected_slaves &= ~(1L<client_devices[client_num] = 0; for (i = 0; i < MAX_INPUT_DEVICES; i++) netplay->device_clients[i] &= ~(1L<paused) remote_unpaused(netplay, connection); } /** * netplay_delayed_state_change: * * Handle any pending state changes which are ready * as of the beginning of the current frame. */ void netplay_delayed_state_change(netplay_t *netplay) { unsigned i; for (i = 0; i < netplay->connections_size; i++) { uint32_t client_num = (uint32_t)(i + 1); struct netplay_connection *connection = &netplay->connections[i]; if ((connection->active || connection->mode == NETPLAY_CONNECTION_DELAYED_DISCONNECT) && connection->delay_frame && connection->delay_frame <= netplay->self_frame_count) { /* Something was delayed! Prepare the MODE command */ uint32_t payload[15] = {0}; payload[0] = htonl(connection->delay_frame); payload[1] = htonl(client_num); payload[2] = htonl(0); memcpy(payload + 3, netplay->device_share_modes, sizeof(netplay->device_share_modes)); strncpy((char *) (payload + 7), connection->nick, NETPLAY_NICK_LEN); /* Remove the connection entirely if relevant */ if (connection->mode == NETPLAY_CONNECTION_DELAYED_DISCONNECT) connection->mode = NETPLAY_CONNECTION_NONE; /* Then send the mode change packet */ netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload)); /* And forget the delay frame */ connection->delay_frame = 0; } } } /* Send the specified input data */ static bool send_input_frame(netplay_t *netplay, struct delta_frame *dframe, struct netplay_connection *only, struct netplay_connection *except, uint32_t client_num, bool slave) { #define BUFSZ 16 /* FIXME: Arbitrary restriction */ uint32_t buffer[BUFSZ], devices, device; size_t bufused, i; /* Set up the basic buffer */ bufused = 4; buffer[0] = htonl(NETPLAY_CMD_INPUT); buffer[2] = htonl(dframe->frame); buffer[3] = htonl(client_num); /* Add the device data */ devices = netplay->client_devices[client_num]; for (device = 0; device < MAX_INPUT_DEVICES; device++) { netplay_input_state_t istate; if (!(devices & (1<real_input[device]; while (istate && (!istate->used || istate->client_num != (slave?MAX_CLIENTS:client_num))) istate = istate->next; if (!istate) continue; if (bufused + istate->size >= BUFSZ) continue; /* FIXME: More severe? */ for (i = 0; i < istate->size; i++) buffer[bufused+i] = htonl(istate->data[i]); bufused += istate->size; } buffer[1] = htonl((bufused-2) * sizeof(uint32_t)); #ifdef DEBUG_NETPLAY_STEPS RARCH_LOG("[netplay] Sending input for client %u\n", (unsigned) client_num); print_state(netplay); #endif if (only) { if (!netplay_send(&only->send_packet_buffer, only->fd, buffer, bufused*sizeof(uint32_t))) { netplay_hangup(netplay, only); return false; } } else { for (i = 0; i < netplay->connections_size; i++) { struct netplay_connection *connection = &netplay->connections[i]; if (connection == except) continue; if (connection->active && connection->mode >= NETPLAY_CONNECTION_CONNECTED && (connection->mode != NETPLAY_CONNECTION_PLAYING || i+1 != client_num)) { if (!netplay_send(&connection->send_packet_buffer, connection->fd, buffer, bufused*sizeof(uint32_t))) netplay_hangup(netplay, connection); } } } return true; #undef BUFSZ } /** * netplay_send_cur_input * * Send the current input frame to a given connection. * * Returns true if successful, false otherwise. */ bool netplay_send_cur_input(netplay_t *netplay, struct netplay_connection *connection) { uint32_t from_client, to_client; struct delta_frame *dframe = &netplay->buffer[netplay->self_ptr]; if (netplay->is_server) { to_client = (uint32_t)(connection - netplay->connections + 1); /* Send the other players' input data (FIXME: This involves an * unacceptable amount of recalculating) */ for (from_client = 1; from_client < MAX_CLIENTS; from_client++) { if (from_client == to_client) continue; if ((netplay->connected_players & (1<have_real[from_client]) { if (!send_input_frame(netplay, dframe, connection, NULL, from_client, false)) return false; } } } /* If we're not playing, send a NOINPUT */ if (netplay->self_mode != NETPLAY_CONNECTION_PLAYING) { uint32_t payload = htonl(netplay->self_frame_count); if (!netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_NOINPUT, &payload, sizeof(payload))) return false; } } /* Send our own data */ if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING || netplay->self_mode == NETPLAY_CONNECTION_SLAVE) { if (!send_input_frame(netplay, dframe, connection, NULL, netplay->self_client_num, netplay->self_mode == NETPLAY_CONNECTION_SLAVE)) return false; } if (!netplay_send_flush(&connection->send_packet_buffer, connection->fd, false)) return false; return true; } /** * netplay_send_raw_cmd * * Send a raw Netplay command to the given connection. * * Returns true on success, false on failure. */ bool netplay_send_raw_cmd(netplay_t *netplay, struct netplay_connection *connection, uint32_t cmd, const void *data, size_t size) { uint32_t cmdbuf[2]; cmdbuf[0] = htonl(cmd); cmdbuf[1] = htonl(size); if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmdbuf, sizeof(cmdbuf))) return false; if (size > 0) if (!netplay_send(&connection->send_packet_buffer, connection->fd, data, size)) return false; return true; } /** * netplay_send_raw_cmd_all * * Send a raw Netplay command to all connections, optionally excluding one * (typically the client that the relevant command came from) */ void netplay_send_raw_cmd_all(netplay_t *netplay, struct netplay_connection *except, uint32_t cmd, const void *data, size_t size) { size_t i; for (i = 0; i < netplay->connections_size; i++) { struct netplay_connection *connection = &netplay->connections[i]; if (connection == except) continue; if (connection->active && connection->mode >= NETPLAY_CONNECTION_CONNECTED) { if (!netplay_send_raw_cmd(netplay, connection, cmd, data, size)) netplay_hangup(netplay, connection); } } } /** * netplay_send_flush_all * * Flush all of our output buffers */ static void netplay_send_flush_all(netplay_t *netplay, struct netplay_connection *except) { size_t i; for (i = 0; i < netplay->connections_size; i++) { struct netplay_connection *connection = &netplay->connections[i]; if (connection == except) continue; if (connection->active && connection->mode >= NETPLAY_CONNECTION_CONNECTED) { if (!netplay_send_flush(&connection->send_packet_buffer, connection->fd, true)) netplay_hangup(netplay, connection); } } } static bool netplay_cmd_nak(netplay_t *netplay, struct netplay_connection *connection) { netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_NAK, NULL, 0); return false; } /** * netplay_settings_share_mode * * Get the preferred share mode */ static uint8_t netplay_settings_share_mode( unsigned share_digital, unsigned share_analog) { if (share_digital || share_analog) { uint8_t share_mode = 0; switch (share_digital) { case RARCH_NETPLAY_SHARE_DIGITAL_OR: share_mode |= NETPLAY_SHARE_DIGITAL_OR; break; case RARCH_NETPLAY_SHARE_DIGITAL_XOR: share_mode |= NETPLAY_SHARE_DIGITAL_XOR; break; case RARCH_NETPLAY_SHARE_DIGITAL_VOTE: share_mode |= NETPLAY_SHARE_DIGITAL_VOTE; break; default: share_mode |= NETPLAY_SHARE_NO_PREFERENCE; } switch (share_analog) { case RARCH_NETPLAY_SHARE_ANALOG_MAX: share_mode |= NETPLAY_SHARE_ANALOG_MAX; break; case RARCH_NETPLAY_SHARE_ANALOG_AVERAGE: share_mode |= NETPLAY_SHARE_ANALOG_AVERAGE; break; default: share_mode |= NETPLAY_SHARE_NO_PREFERENCE; } return share_mode; } return 0; } /** * netplay_cmd_mode * * Send a mode change request. As a server, the request is to ourself, and so * honored instantly. */ bool netplay_cmd_mode(netplay_t *netplay, enum rarch_netplay_connection_mode mode) { uint32_t cmd, device; uint32_t payload_buf = 0, *payload = NULL; uint8_t share_mode = 0; struct netplay_connection *connection = NULL; if (!netplay->is_server) connection = &netplay->one_connection; switch (mode) { case NETPLAY_CONNECTION_SPECTATING: cmd = NETPLAY_CMD_SPECTATE; break; case NETPLAY_CONNECTION_SLAVE: payload_buf = NETPLAY_CMD_PLAY_BIT_SLAVE; /* no break */ case NETPLAY_CONNECTION_PLAYING: { settings_t *settings = config_get_ptr(); payload = &payload_buf; /* Add a share mode if requested */ share_mode = netplay_settings_share_mode( settings->uints.netplay_share_digital, settings->uints.netplay_share_analog ); payload_buf |= ((uint32_t) share_mode) << 16; /* Request devices */ for (device = 0; device < MAX_INPUT_DEVICES; device++) { if (settings->bools.netplay_request_devices[device]) payload_buf |= 1<is_server) { handle_play_spectate(netplay, 0, NULL, cmd, payload ? sizeof(uint32_t) : 0, payload); return true; } return netplay_send_raw_cmd(netplay, connection, cmd, payload, payload ? sizeof(uint32_t) : 0); } /** * announce_play_spectate * * Announce a play or spectate mode change */ static void announce_play_spectate(netplay_t *netplay, const char *nick, enum rarch_netplay_connection_mode mode, uint32_t devices) { char msg[512]; msg[0] = msg[sizeof(msg) - 1] = '\0'; switch (mode) { case NETPLAY_CONNECTION_SPECTATING: if (nick) snprintf(msg, sizeof(msg) - 1, msg_hash_to_str(MSG_NETPLAY_PLAYER_S_LEFT), NETPLAY_NICK_LEN, nick); else strlcpy(msg, msg_hash_to_str(MSG_NETPLAY_YOU_HAVE_LEFT_THE_GAME), sizeof(msg)); break; case NETPLAY_CONNECTION_PLAYING: case NETPLAY_CONNECTION_SLAVE: { uint32_t device; uint32_t one_device = (uint32_t) -1; char device_str[512]; size_t device_str_len; for (device = 0; device < MAX_INPUT_DEVICES; device++) { if (!(devices & (1<delay_frame = netplay->read_frame_count[client_num]; /* Mark them as not playing anymore */ if (connection) connection->mode = NETPLAY_CONNECTION_SPECTATING; else { netplay->self_devices = 0; netplay->self_mode = NETPLAY_CONNECTION_SPECTATING; } netplay->connected_players &= ~(1 << client_num); netplay->connected_slaves &= ~(1 << client_num); netplay->client_devices[client_num] = 0; for (i = 0; i < MAX_INPUT_DEVICES; i++) netplay->device_clients[i] &= ~(1 << client_num); /* Tell someone */ payload[0] = htonl(netplay->read_frame_count[client_num]); payload[2] = htonl(0); memcpy(payload + 3, netplay->device_share_modes, sizeof(netplay->device_share_modes)); if (connection) { /* Only tell the player. The others will be told at delay_frame */ payload[1] = htonl(NETPLAY_CMD_MODE_BIT_YOU | client_num); strncpy((char *) (payload + 7), connection->nick, NETPLAY_NICK_LEN); netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload)); } else { /* It was the server, so tell everyone else */ payload[1] = htonl(0); strncpy((char *) (payload + 7), netplay->nick, NETPLAY_NICK_LEN); netplay_send_raw_cmd_all(netplay, NULL, NETPLAY_CMD_MODE, payload, sizeof(payload)); } /* Announce it */ announce_play_spectate(netplay, connection ? connection->nick : NULL, NETPLAY_CONNECTION_SPECTATING, 0); break; } case NETPLAY_CMD_PLAY: { uint32_t mode, devices = 0, device; uint8_t share_mode; bool slave = false; settings_t *settings = config_get_ptr(); if (cmd_size != sizeof(uint32_t) || !in_payload) return; mode = ntohl(in_payload[0]); /* Check the requested mode */ slave = (mode&NETPLAY_CMD_PLAY_BIT_SLAVE)?true:false; share_mode = (mode>>16)&0xFF; /* And the requested devices */ devices = mode&0xFFFF; /* Check if their slave mode request corresponds with what we allow */ if (connection) { if (settings->bools.netplay_require_slaves) slave = true; else if (!settings->bools.netplay_allow_slaves) slave = false; } else slave = false; /* Fix our share mode */ if (share_mode) { if ((share_mode & NETPLAY_SHARE_DIGITAL_BITS) == 0) share_mode |= NETPLAY_SHARE_DIGITAL_OR; if ((share_mode & NETPLAY_SHARE_ANALOG_BITS) == 0) share_mode |= NETPLAY_SHARE_ANALOG_MAX; share_mode &= ~NETPLAY_SHARE_NO_PREFERENCE; } /* They start at the next frame, but we start immediately */ if (connection) { netplay->read_ptr[client_num] = NEXT_PTR(netplay->self_ptr); netplay->read_frame_count[client_num] = netplay->self_frame_count + 1; } else { netplay->read_ptr[client_num] = netplay->self_ptr; netplay->read_frame_count[client_num] = netplay->self_frame_count; } payload[0] = htonl(netplay->read_frame_count[client_num]); if (devices) { /* Make sure the devices are available and/or shareable */ for (device = 0; device < MAX_INPUT_DEVICES; device++) { if (!(devices & (1<device_clients[device]) continue; if (netplay->device_share_modes[device] && share_mode) continue; /* Device already taken and unshareable */ payload[0] = htonl(NETPLAY_CMD_MODE_REFUSED_REASON_NOT_AVAILABLE); /* FIXME: Refusal message for the server */ if (connection) netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE_REFUSED, payload, sizeof(uint32_t)); devices = 0; break; } if (devices == 0) break; /* Set the share mode on any new devices */ for (device = 0; device < MAX_INPUT_DEVICES; device++) { if (!(devices & (1<device_clients[device]) netplay->device_share_modes[device] = share_mode; } } else { /* Find an available device */ for (device = 0; device < MAX_INPUT_DEVICES; device++) { if (netplay->config_devices[device] == RETRO_DEVICE_NONE) { device = MAX_INPUT_DEVICES; break; } if (!netplay->device_clients[device]) break; } if (device >= MAX_INPUT_DEVICES && netplay->config_devices[1] == RETRO_DEVICE_NONE && share_mode) { /* No device free and no device specifically asked for, but only * one device, so share it */ if (netplay->device_share_modes[0]) { device = 0; share_mode = netplay->device_share_modes[0]; break; } } if (device >= MAX_INPUT_DEVICES) { /* No slots free! */ payload[0] = htonl(NETPLAY_CMD_MODE_REFUSED_REASON_NO_SLOTS); /* FIXME: Message for the server */ if (connection) netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE_REFUSED, payload, sizeof(uint32_t)); break; } devices = 1<device_share_modes[device] = share_mode; } payload[2] = htonl(devices); /* Mark them as playing */ if (connection) connection->mode = slave ? NETPLAY_CONNECTION_SLAVE : NETPLAY_CONNECTION_PLAYING; else { netplay->self_devices = devices; netplay->self_mode = NETPLAY_CONNECTION_PLAYING; } netplay->connected_players |= 1 << client_num; if (slave) netplay->connected_slaves |= 1 << client_num; netplay->client_devices[client_num] = devices; for (device = 0; device < MAX_INPUT_DEVICES; device++) { if (!(devices & (1<device_clients[device] |= 1 << client_num; } /* Tell everyone */ payload[1] = htonl( NETPLAY_CMD_MODE_BIT_PLAYING | (slave ? NETPLAY_CMD_MODE_BIT_SLAVE : 0) | client_num); memcpy(payload + 3, netplay->device_share_modes, sizeof(netplay->device_share_modes)); if (connection) strncpy((char *) (payload + 7), connection->nick, NETPLAY_NICK_LEN); else strncpy((char *) (payload + 7), netplay->nick, NETPLAY_NICK_LEN); netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload)); /* Tell the player */ if (connection) { payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING | ((connection->mode == NETPLAY_CONNECTION_SLAVE)? NETPLAY_CMD_MODE_BIT_SLAVE:0) | NETPLAY_CMD_MODE_BIT_YOU | client_num); netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload)); } /* Announce it */ announce_play_spectate(netplay, connection ? connection->nick : NULL, NETPLAY_CONNECTION_PLAYING, devices); break; } } } #undef RECV #define RECV(buf, sz) \ recvd = netplay_recv(&connection->recv_packet_buffer, connection->fd, (buf), \ (sz), false); \ if (recvd >= 0 && recvd < (ssize_t) (sz)) goto shrt; \ else if (recvd < 0) static bool netplay_get_cmd(netplay_t *netplay, struct netplay_connection *connection, bool *had_input) { uint32_t cmd; uint32_t cmd_size; ssize_t recvd; /* We don't handle the initial handshake here */ if (connection->mode < NETPLAY_CONNECTION_CONNECTED) return netplay_handshake(netplay, connection, had_input); RECV(&cmd, sizeof(cmd)) return false; cmd = ntohl(cmd); RECV(&cmd_size, sizeof(cmd_size)) return false; cmd_size = ntohl(cmd_size); #ifdef DEBUG_NETPLAY_STEPS RARCH_LOG("[netplay] Received netplay command %X (%u) from %u\n", cmd, cmd_size, (unsigned) (connection - netplay->connections)); #endif netplay->timeout_cnt = 0; switch (cmd) { case NETPLAY_CMD_ACK: /* Why are we even bothering? */ break; case NETPLAY_CMD_NAK: /* Disconnect now! */ return false; case NETPLAY_CMD_INPUT: { uint32_t frame_num, client_num, input_size, devices, device; struct delta_frame *dframe; if (cmd_size < 2*sizeof(uint32_t)) { RARCH_ERR("NETPLAY_CMD_INPUT too short, no frame/client number."); return netplay_cmd_nak(netplay, connection); } RECV(&frame_num, sizeof(frame_num)) return false; RECV(&client_num, sizeof(client_num)) return false; frame_num = ntohl(frame_num); client_num = ntohl(client_num); client_num &= 0xFFFF; if (netplay->is_server) { /* Ignore the claimed client #, must be this client */ if (connection->mode != NETPLAY_CONNECTION_PLAYING && connection->mode != NETPLAY_CONNECTION_SLAVE) { RARCH_ERR("Netplay input from non-participating player.\n"); return netplay_cmd_nak(netplay, connection); } client_num = (uint32_t)(connection - netplay->connections + 1); } if (client_num > MAX_CLIENTS) { RARCH_ERR("NETPLAY_CMD_INPUT received data for an unsupported client.\n"); return netplay_cmd_nak(netplay, connection); } /* Figure out how much input is expected */ devices = netplay->client_devices[client_num]; input_size = netplay_expected_input_size(netplay, devices); if (cmd_size != (2+input_size) * sizeof(uint32_t)) { RARCH_ERR("NETPLAY_CMD_INPUT received an unexpected payload size.\n"); return netplay_cmd_nak(netplay, connection); } if (client_num >= MAX_CLIENTS || !(netplay->connected_players & (1<mode == NETPLAY_CONNECTION_PLAYING) { if (frame_num < netplay->read_frame_count[client_num]) { uint32_t buf; /* We already had this, so ignore the new transmission */ for (; input_size; input_size--) { RECV(&buf, sizeof(uint32_t)) return netplay_cmd_nak(netplay, connection); } break; } else if (frame_num > netplay->read_frame_count[client_num]) { /* Out of order = out of luck */ RARCH_ERR("Netplay input out of order.\n"); return netplay_cmd_nak(netplay, connection); } } /* The data's good! */ dframe = &netplay->buffer[netplay->read_ptr[client_num]]; if (!netplay_delta_frame_ready(netplay, dframe, netplay->read_frame_count[client_num])) { /* Hopefully we'll be ready after another round of input */ goto shrt; } /* Copy in the input */ for (device = 0; device < MAX_INPUT_DEVICES; device++) { netplay_input_state_t istate; uint32_t dsize, di; if (!(devices & (1<real_input[device], client_num, dsize, false /* Must be false because of slave-mode clients */, false); if (!istate) { /* Catastrophe! */ return netplay_cmd_nak(netplay, connection); } RECV(istate->data, dsize*sizeof(uint32_t)) return false; for (di = 0; di < dsize; di++) istate->data[di] = ntohl(istate->data[di]); } dframe->have_real[client_num] = true; /* Slaves may go through several packets of data in the same frame * if latency is choppy, so we advance and send their data after * handling all network data this frame */ if (connection->mode == NETPLAY_CONNECTION_PLAYING) { netplay->read_ptr[client_num] = NEXT_PTR(netplay->read_ptr[client_num]); netplay->read_frame_count[client_num]++; if (netplay->is_server) { /* Forward it on if it's past data */ if (dframe->frame <= netplay->self_frame_count) send_input_frame(netplay, dframe, NULL, connection, client_num, false); } } /* If this was server data, advance our server pointer too */ if (!netplay->is_server && client_num == 0) { netplay->server_ptr = netplay->read_ptr[0]; netplay->server_frame_count = netplay->read_frame_count[0]; } #ifdef DEBUG_NETPLAY_STEPS RARCH_LOG("[netplay] Received input from %u\n", client_num); print_state(netplay); #endif break; } case NETPLAY_CMD_NOINPUT: { uint32_t frame; if (netplay->is_server) { RARCH_ERR("NETPLAY_CMD_NOINPUT from a client.\n"); return netplay_cmd_nak(netplay, connection); } RECV(&frame, sizeof(frame)) { RARCH_ERR("Failed to receive NETPLAY_CMD_NOINPUT payload.\n"); return netplay_cmd_nak(netplay, connection); } frame = ntohl(frame); /* We already had this, so ignore the new transmission */ if (frame < netplay->server_frame_count) break; if (frame != netplay->server_frame_count) { RARCH_ERR("NETPLAY_CMD_NOINPUT for invalid frame.\n"); return netplay_cmd_nak(netplay, connection); } netplay->server_ptr = NEXT_PTR(netplay->server_ptr); netplay->server_frame_count++; #ifdef DEBUG_NETPLAY_STEPS RARCH_LOG("[netplay] Received server noinput\n"); print_state(netplay); #endif break; } case NETPLAY_CMD_SPECTATE: { uint32_t client_num; if (!netplay->is_server) { RARCH_ERR("NETPLAY_CMD_SPECTATE from a server.\n"); return netplay_cmd_nak(netplay, connection); } if (cmd_size != 0) { RARCH_ERR("Unexpected payload in NETPLAY_CMD_SPECTATE.\n"); return netplay_cmd_nak(netplay, connection); } if (connection->mode != NETPLAY_CONNECTION_PLAYING && connection->mode != NETPLAY_CONNECTION_SLAVE) { /* They were confused */ return netplay_cmd_nak(netplay, connection); } client_num = (uint32_t)(connection - netplay->connections + 1); handle_play_spectate(netplay, client_num, connection, cmd, 0, NULL); break; } case NETPLAY_CMD_PLAY: { uint32_t client_num; uint32_t payload[1]; if (cmd_size != sizeof(uint32_t)) { RARCH_ERR("Incorrect NETPLAY_CMD_PLAY payload size.\n"); return netplay_cmd_nak(netplay, connection); } RECV(payload, sizeof(uint32_t)) { RARCH_ERR("Failed to receive NETPLAY_CMD_PLAY payload.\n"); return netplay_cmd_nak(netplay, connection); } if (!netplay->is_server) { RARCH_ERR("NETPLAY_CMD_PLAY from a server.\n"); return netplay_cmd_nak(netplay, connection); } if (connection->delay_frame) { /* Can't switch modes while a mode switch is already in progress. */ payload[0] = htonl(NETPLAY_CMD_MODE_REFUSED_REASON_TOO_FAST); netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE_REFUSED, payload, sizeof(uint32_t)); break; } if (!connection->can_play) { /* Not allowed to play */ payload[0] = htonl(NETPLAY_CMD_MODE_REFUSED_REASON_UNPRIVILEGED); netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE_REFUSED, payload, sizeof(uint32_t)); break; } /* They were obviously confused */ if ( connection->mode == NETPLAY_CONNECTION_PLAYING || connection->mode == NETPLAY_CONNECTION_SLAVE) return netplay_cmd_nak(netplay, connection); client_num = (unsigned)(connection - netplay->connections + 1); handle_play_spectate(netplay, client_num, connection, cmd, cmd_size, payload); break; } case NETPLAY_CMD_MODE: { uint32_t payload[15]; uint32_t frame, mode, client_num, devices, device; size_t ptr; struct delta_frame *dframe; const char *nick; #define START(which) \ do { \ ptr = which; \ dframe = &netplay->buffer[ptr]; \ } while (0) #define NEXT() \ do { \ ptr = NEXT_PTR(ptr); \ dframe = &netplay->buffer[ptr]; \ } while (0) if (netplay->is_server) { RARCH_ERR("NETPLAY_CMD_MODE from client.\n"); return netplay_cmd_nak(netplay, connection); } if (cmd_size != sizeof(payload)) { RARCH_ERR("Invalid payload size for NETPLAY_CMD_MODE.\n"); return netplay_cmd_nak(netplay, connection); } RECV(payload, sizeof(payload)) { RARCH_ERR("NETPLAY_CMD_MODE failed to receive payload.\n"); return netplay_cmd_nak(netplay, connection); } frame = ntohl(payload[0]); /* We're changing past input, so must replay it */ if (frame < netplay->self_frame_count) netplay->force_rewind = true; mode = ntohl(payload[1]); client_num = mode & 0xFFFF; if (client_num >= MAX_CLIENTS) { RARCH_ERR("Received NETPLAY_CMD_MODE for a higher player number than we support.\n"); return netplay_cmd_nak(netplay, connection); } devices = ntohl(payload[2]); memcpy(netplay->device_share_modes, payload + 3, sizeof(netplay->device_share_modes)); nick = (const char *) (payload + 7); if (mode & NETPLAY_CMD_MODE_BIT_YOU) { /* A change to me! */ if (mode & NETPLAY_CMD_MODE_BIT_PLAYING) { if (frame != netplay->server_frame_count) { RARCH_ERR("Received mode change out of order.\n"); return netplay_cmd_nak(netplay, connection); } /* Hooray, I get to play now! */ if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING) { RARCH_ERR("Received player mode change even though I'm already a player.\n"); return netplay_cmd_nak(netplay, connection); } /* Our mode is based on whether we have the slave bit set */ if (mode & NETPLAY_CMD_MODE_BIT_SLAVE) netplay->self_mode = NETPLAY_CONNECTION_SLAVE; else netplay->self_mode = NETPLAY_CONNECTION_PLAYING; netplay->connected_players |= (1<client_devices[client_num] = devices; for (device = 0; device < MAX_INPUT_DEVICES; device++) if (devices & (1<device_clients[device] |= (1<self_devices = devices; netplay->read_ptr[client_num] = netplay->server_ptr; netplay->read_frame_count[client_num] = netplay->server_frame_count; /* Fix up current frame info */ if (!(mode & NETPLAY_CMD_MODE_BIT_SLAVE) && frame <= netplay->self_frame_count) { /* It wanted past frames, better send 'em! */ START(netplay->server_ptr); while (dframe->used && dframe->frame <= netplay->self_frame_count) { for (device = 0; device < MAX_INPUT_DEVICES; device++) { uint32_t dsize; netplay_input_state_t istate; if (!(devices & (1<real_input[device], client_num, dsize, false, false); if (!istate) continue; memset(istate->data, 0, dsize*sizeof(uint32_t)); } dframe->have_local = true; dframe->have_real[client_num] = true; send_input_frame(netplay, dframe, connection, NULL, client_num, false); if (dframe->frame == netplay->self_frame_count) break; NEXT(); } } else { uint32_t frame_count; /* It wants future frames, make sure we don't capture or send intermediate ones */ START(netplay->self_ptr); frame_count = netplay->self_frame_count; for (;;) { if (!dframe->used) { /* Make sure it's ready */ if (!netplay_delta_frame_ready(netplay, dframe, frame_count)) { RARCH_ERR("Received mode change but delta frame isn't ready!\n"); return netplay_cmd_nak(netplay, connection); } } dframe->have_local = true; /* Go on to the next delta frame */ NEXT(); frame_count++; if (frame_count >= frame) break; } } /* Announce it */ announce_play_spectate(netplay, NULL, NETPLAY_CONNECTION_PLAYING, devices); #ifdef DEBUG_NETPLAY_STEPS RARCH_LOG("[netplay] Received mode change self->%X\n", devices); print_state(netplay); #endif } else /* YOU && !PLAYING */ { /* I'm no longer playing, but I should already know this */ if (netplay->self_mode != NETPLAY_CONNECTION_SPECTATING) { RARCH_ERR("Received mode change to spectator unprompted.\n"); return netplay_cmd_nak(netplay, connection); } /* Unmark ourself, in case we were in slave mode */ netplay->connected_players &= ~(1<client_devices[client_num] = 0; for (device = 0; device < MAX_INPUT_DEVICES; device++) netplay->device_clients[device] &= ~(1<spectating\n"); print_state(netplay); #endif } } else /* !YOU */ { /* Somebody else is joining or parting */ if (mode & NETPLAY_CMD_MODE_BIT_PLAYING) { if (frame != netplay->server_frame_count) { RARCH_ERR("Received mode change out of order.\n"); return netplay_cmd_nak(netplay, connection); } netplay->connected_players |= (1<client_devices[client_num] = devices; for (device = 0; device < MAX_INPUT_DEVICES; device++) if (devices & (1<device_clients[device] |= (1<read_ptr[client_num] = netplay->server_ptr; netplay->read_frame_count[client_num] = netplay->server_frame_count; /* Announce it */ announce_play_spectate(netplay, nick, NETPLAY_CONNECTION_PLAYING, devices); #ifdef DEBUG_NETPLAY_STEPS RARCH_LOG("[netplay] Received mode change %u->%u\n", client_num, devices); print_state(netplay); #endif } else { netplay->connected_players &= ~(1<client_devices[client_num] = 0; for (device = 0; device < MAX_INPUT_DEVICES; device++) netplay->device_clients[device] &= ~(1<spectator\n", client_num); print_state(netplay); #endif } } break; #undef START #undef NEXT } case NETPLAY_CMD_MODE_REFUSED: { uint32_t reason; const char *dmsg = NULL; if (netplay->is_server) { RARCH_ERR("NETPLAY_CMD_MODE_REFUSED from client.\n"); return netplay_cmd_nak(netplay, connection); } if (cmd_size != sizeof(uint32_t)) { RARCH_ERR("Received invalid payload size for NETPLAY_CMD_MODE_REFUSED.\n"); return netplay_cmd_nak(netplay, connection); } RECV(&reason, sizeof(reason)) { RARCH_ERR("Failed to receive NETPLAY_CMD_MODE_REFUSED payload.\n"); return netplay_cmd_nak(netplay, connection); } reason = ntohl(reason); switch (reason) { case NETPLAY_CMD_MODE_REFUSED_REASON_UNPRIVILEGED: dmsg = msg_hash_to_str(MSG_NETPLAY_CANNOT_PLAY_UNPRIVILEGED); break; case NETPLAY_CMD_MODE_REFUSED_REASON_NO_SLOTS: dmsg = msg_hash_to_str(MSG_NETPLAY_CANNOT_PLAY_NO_SLOTS); break; case NETPLAY_CMD_MODE_REFUSED_REASON_NOT_AVAILABLE: dmsg = msg_hash_to_str(MSG_NETPLAY_CANNOT_PLAY_NOT_AVAILABLE); break; default: dmsg = msg_hash_to_str(MSG_NETPLAY_CANNOT_PLAY); } if (dmsg) { RARCH_LOG("[netplay] %s\n", dmsg); runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); } break; } case NETPLAY_CMD_DISCONNECT: netplay_hangup(netplay, connection); return true; case NETPLAY_CMD_CRC: { uint32_t buffer[2]; size_t tmp_ptr = netplay->run_ptr; bool found = false; if (cmd_size != sizeof(buffer)) { RARCH_ERR("NETPLAY_CMD_CRC received unexpected payload size.\n"); return netplay_cmd_nak(netplay, connection); } RECV(buffer, sizeof(buffer)) { RARCH_ERR("NETPLAY_CMD_CRC failed to receive payload.\n"); return netplay_cmd_nak(netplay, connection); } 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->run_ptr); /* Oh well, we got rid of it! */ if (!found) break; 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 = 0; if (netplay->state_size) local_crc = netplay_delta_frame_crc( netplay, &netplay->buffer[tmp_ptr]); /* Problem! */ if (buffer[1] != local_crc) netplay_cmd_request_savestate(netplay); } else { /* We'll have to check it when we catch up */ netplay->buffer[tmp_ptr].crc = buffer[1]; } break; } case NETPLAY_CMD_REQUEST_SAVESTATE: /* Delay until next frame so we don't send the savestate after the * input */ netplay->force_send_savestate = true; break; case NETPLAY_CMD_LOAD_SAVESTATE: case NETPLAY_CMD_RESET: { uint32_t frame; uint32_t isize; uint32_t rd, wn; uint32_t client; uint32_t load_frame_count; size_t load_ptr; struct compression_transcoder *ctrans = NULL; uint32_t client_num = (uint32_t) (connection - netplay->connections + 1); /* 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->run_ptr; netplay->replay_frame_count = netplay->run_frame_count; netplay_wait_and_init_serialization(netplay); netplay->is_replay = false; } else netplay_wait_and_init_serialization(netplay); } /* Only players may load states */ if (connection->mode != NETPLAY_CONNECTION_PLAYING && connection->mode != NETPLAY_CONNECTION_SLAVE) { RARCH_ERR("Netplay state load from a spectator.\n"); return netplay_cmd_nak(netplay, connection); } /* We only allow players to load state if we're in a simple * two-player situation */ if (netplay->is_server && netplay->connections_size > 1) { RARCH_ERR("Netplay state load from a client with other clients connected disallowed.\n"); return netplay_cmd_nak(netplay, connection); } /* 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. */ /* Check the payload size */ if ((cmd == NETPLAY_CMD_LOAD_SAVESTATE && (cmd_size < 2*sizeof(uint32_t) || cmd_size > netplay->zbuffer_size + 2*sizeof(uint32_t))) || (cmd == NETPLAY_CMD_RESET && cmd_size != sizeof(uint32_t))) { RARCH_ERR("CMD_LOAD_SAVESTATE received an unexpected payload size.\n"); return netplay_cmd_nak(netplay, connection); } RECV(&frame, sizeof(frame)) { RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive savestate frame.\n"); return netplay_cmd_nak(netplay, connection); } frame = ntohl(frame); if (netplay->is_server) { load_ptr = netplay->read_ptr[client_num]; load_frame_count = netplay->read_frame_count[client_num]; } else { load_ptr = netplay->server_ptr; load_frame_count = netplay->server_frame_count; } if (frame != load_frame_count) { RARCH_ERR("CMD_LOAD_SAVESTATE loading a state out of order!\n"); return netplay_cmd_nak(netplay, connection); } if (!netplay_delta_frame_ready(netplay, &netplay->buffer[load_ptr], load_frame_count)) { /* Hopefully it will be after another round of input */ goto shrt; } /* Now we switch based on whether we're loading a state or resetting */ if (cmd == NETPLAY_CMD_LOAD_SAVESTATE) { RECV(&isize, sizeof(isize)) { RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive inflated size.\n"); return netplay_cmd_nak(netplay, connection); } isize = ntohl(isize); if (isize != netplay->state_size) { RARCH_ERR("CMD_LOAD_SAVESTATE received an unexpected save state size.\n"); return netplay_cmd_nak(netplay, connection); } RECV(netplay->zbuffer, cmd_size - 2*sizeof(uint32_t)) { RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive savestate.\n"); return netplay_cmd_nak(netplay, connection); } /* And decompress it */ switch (connection->compression_supported) { case NETPLAY_COMPRESSION_ZLIB: ctrans = &netplay->compress_zlib; break; default: ctrans = &netplay->compress_nil; } ctrans->decompression_backend->set_in(ctrans->decompression_stream, netplay->zbuffer, cmd_size - 2*sizeof(uint32_t)); ctrans->decompression_backend->set_out(ctrans->decompression_stream, (uint8_t*)netplay->buffer[load_ptr].state, (unsigned)netplay->state_size); ctrans->decompression_backend->trans(ctrans->decompression_stream, true, &rd, &wn, NULL); /* Force a rewind to the relevant frame */ netplay->force_rewind = true; } else { /* Resetting */ netplay->force_reset = true; } /* Skip ahead if it's past where we are */ if (load_frame_count > netplay->run_frame_count || cmd == NETPLAY_CMD_RESET) { /* 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->run_ptr = PREV_PTR(load_ptr); netplay->run_frame_count = load_frame_count - 1; if (frame > netplay->self_frame_count) { netplay->self_ptr = netplay->run_ptr; netplay->self_frame_count = netplay->run_frame_count; } } /* Don't expect earlier data from other clients */ for (client = 0; client < MAX_CLIENTS; client++) { if (!(netplay->connected_players & (1< netplay->read_frame_count[client]) { netplay->read_ptr[client] = load_ptr; netplay->read_frame_count[client] = load_frame_count; } } /* Make sure our states are correct */ netplay->savestate_request_outstanding = false; netplay->other_ptr = load_ptr; netplay->other_frame_count = load_frame_count; #ifdef DEBUG_NETPLAY_STEPS RARCH_LOG("[netplay] Loading state at %u\n", load_frame_count); print_state(netplay); #endif break; } case NETPLAY_CMD_PAUSE: { char msg[512], nick[NETPLAY_NICK_LEN]; msg[sizeof(msg)-1] = '\0'; /* Read in the paused nick */ if (cmd_size != sizeof(nick)) { RARCH_ERR("NETPLAY_CMD_PAUSE received invalid payload size.\n"); return netplay_cmd_nak(netplay, connection); } RECV(nick, sizeof(nick)) { RARCH_ERR("Failed to receive paused nickname.\n"); return netplay_cmd_nak(netplay, connection); } nick[sizeof(nick)-1] = '\0'; /* We outright ignore pausing from spectators and slaves */ if (connection->mode != NETPLAY_CONNECTION_PLAYING) break; connection->paused = true; netplay->remote_paused = true; if (netplay->is_server) { /* Inform peers */ snprintf(msg, sizeof(msg)-1, msg_hash_to_str(MSG_NETPLAY_PEER_PAUSED), connection->nick); netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_PAUSE, connection->nick, NETPLAY_NICK_LEN); /* We may not reach post_frame soon, so flush the pause message * immediately. */ netplay_send_flush_all(netplay, connection); } else { snprintf(msg, sizeof(msg)-1, msg_hash_to_str(MSG_NETPLAY_PEER_PAUSED), nick); } RARCH_LOG("[netplay] %s\n", msg); runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); break; } case NETPLAY_CMD_RESUME: remote_unpaused(netplay, connection); break; case NETPLAY_CMD_STALL: { uint32_t frames; if (cmd_size != sizeof(uint32_t)) { RARCH_ERR("NETPLAY_CMD_STALL with incorrect payload size.\n"); return netplay_cmd_nak(netplay, connection); } RECV(&frames, sizeof(frames)) { RARCH_ERR("Failed to receive NETPLAY_CMD_STALL payload.\n"); return netplay_cmd_nak(netplay, connection); } frames = ntohl(frames); if (frames > NETPLAY_MAX_REQ_STALL_TIME) frames = NETPLAY_MAX_REQ_STALL_TIME; if (netplay->is_server) { /* Only servers can request a stall! */ RARCH_ERR("Netplay client requested a stall?\n"); return netplay_cmd_nak(netplay, connection); } /* We can only stall for one reason at a time */ if (!netplay->stall) { connection->stall = netplay->stall = NETPLAY_STALL_SERVER_REQUESTED; netplay->stall_time = 0; connection->stall_frame = frames; } break; } default: RARCH_ERR("%s.\n", msg_hash_to_str(MSG_UNKNOWN_NETPLAY_COMMAND_RECEIVED)); return netplay_cmd_nak(netplay, connection); } netplay_recv_flush(&connection->recv_packet_buffer); netplay->timeout_cnt = 0; if (had_input) *had_input = true; return true; shrt: /* No more data, reset and try again */ netplay_recv_reset(&connection->recv_packet_buffer); return true; #undef RECV } /** * netplay_poll_net_input * * Poll input from the network */ int netplay_poll_net_input(netplay_t *netplay, bool block) { bool had_input = false; int max_fd = 0; size_t i; for (i = 0; i < netplay->connections_size; i++) { struct netplay_connection *connection = &netplay->connections[i]; if (connection->active && connection->fd >= max_fd) max_fd = connection->fd + 1; } if (max_fd == 0) return 0; netplay->timeout_cnt = 0; do { had_input = false; netplay->timeout_cnt++; /* Read input from each connection */ for (i = 0; i < netplay->connections_size; i++) { struct netplay_connection *connection = &netplay->connections[i]; if (connection->active && !netplay_get_cmd(netplay, connection, &had_input)) netplay_hangup(netplay, connection); } if (block) { netplay_update_unread_ptr(netplay); /* If we were blocked for input, pass if we have this frame's input */ if (netplay->unread_frame_count > netplay->run_frame_count) break; /* If we're supposed to block but we didn't have enough input, wait for it */ if (!had_input) { fd_set fds; struct timeval tv = {0}; tv.tv_usec = RETRY_MS * 1000; FD_ZERO(&fds); for (i = 0; i < netplay->connections_size; i++) { struct netplay_connection *connection = &netplay->connections[i]; if (connection->active) FD_SET(connection->fd, &fds); } if (socket_select(max_fd, &fds, NULL, NULL, &tv) < 0) return -1; RARCH_LOG("[netplay] Network is stalling at frame %u, count %u of %d ...\n", netplay->run_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_handle_slaves * * Handle any slave connections */ void netplay_handle_slaves(netplay_t *netplay) { struct delta_frame *oframe, *frame = &netplay->buffer[netplay->self_ptr]; size_t i; for (i = 0; i < netplay->connections_size; i++) { struct netplay_connection *connection = &netplay->connections[i]; if (connection->active && connection->mode == NETPLAY_CONNECTION_SLAVE) { uint32_t devices, device; uint32_t client_num = (uint32_t)(i + 1); /* This is a slave connection. First, should we do anything at all? If * we've already "read" this data, then we can just ignore it */ if (netplay->read_frame_count[client_num] > netplay->self_frame_count) continue; /* Alright, we have to send something. Do we need to generate it first? */ if (!frame->have_real[client_num]) { devices = netplay->client_devices[client_num]; /* Copy the previous frame's data */ oframe = &netplay->buffer[PREV_PTR(netplay->self_ptr)]; for (device = 0; device < MAX_INPUT_DEVICES; device++) { netplay_input_state_t istate_out, istate_in; if (!(devices & (1<real_input[device]; while (istate_in && istate_in->client_num != client_num) istate_in = istate_in->next; if (!istate_in) { /* Start with blank input */ netplay_input_state_for(&frame->real_input[device], client_num, netplay_expected_input_size(netplay, 1 << device), true, false); } else { /* Copy the previous input */ istate_out = netplay_input_state_for(&frame->real_input[device], client_num, istate_in->size, true, false); memcpy(istate_out->data, istate_in->data, istate_in->size * sizeof(uint32_t)); } } frame->have_real[client_num] = true; } /* Send it along */ send_input_frame(netplay, frame, NULL, NULL, client_num, false); /* And mark it as "read" */ netplay->read_ptr[client_num] = NEXT_PTR(netplay->self_ptr); netplay->read_frame_count[client_num] = netplay->self_frame_count + 1; } } } /** * netplay_announce_nat_traversal * * Announce successful NAT traversal. */ void netplay_announce_nat_traversal(netplay_t *netplay) { #ifndef HAVE_SOCKET_LEGACY char msg[4200], host[PATH_MAX_LENGTH], port[6]; if (netplay->nat_traversal_state.have_inet4) { if (getnameinfo((const struct sockaddr *) &netplay->nat_traversal_state.ext_inet4_addr, sizeof(struct sockaddr_in), host, PATH_MAX_LENGTH, port, 6, NI_NUMERICHOST|NI_NUMERICSERV) != 0) return; } #ifdef HAVE_INET6 else if (netplay->nat_traversal_state.have_inet6) { if (getnameinfo((const struct sockaddr *) &netplay->nat_traversal_state.ext_inet6_addr, sizeof(struct sockaddr_in6), host, PATH_MAX_LENGTH, port, 6, NI_NUMERICHOST|NI_NUMERICSERV) != 0) return; } #endif else { snprintf(msg, sizeof(msg), "%s\n", msg_hash_to_str(MSG_UPNP_FAILED)); runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); RARCH_LOG("[netplay] %s\n", msg); return; } snprintf(msg, sizeof(msg), "%s: %s:%s\n", msg_hash_to_str(MSG_PUBLIC_ADDRESS), host, port); runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); RARCH_LOG("[netplay] %s\n", msg); #endif } /** * netplay_init_nat_traversal * * Initialize the NAT traversal library and try to open a port */ void netplay_init_nat_traversal(netplay_t *netplay) { memset(&netplay->nat_traversal_state, 0, sizeof(netplay->nat_traversal_state)); netplay->nat_traversal_task_oustanding = true; task_push_netplay_nat_traversal(&netplay->nat_traversal_state, netplay->tcp_port); } static int init_tcp_connection(const struct addrinfo *res, bool server, struct sockaddr *other_addr, socklen_t addr_size) { bool ret = true; int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (fd < 0) { ret = false; goto end; } #if defined(IPPROTO_TCP) && defined(TCP_NODELAY) { int flag = 1; if (setsockopt(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(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 if (server) { if (socket_connect(fd, (void*)res, false) < 0) { ret = false; goto end; } } else { #if defined(HAVE_INET6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) /* Make sure we accept connections on both IPv6 and IPv4 */ int on = 0; if (res->ai_family == AF_INET6) { if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(on)) < 0) RARCH_WARN("Failed to listen on both IPv6 and IPv4\n"); } #endif if ( !socket_bind(fd, (void*)res) || listen(fd, 1024) < 0) { ret = false; goto end; } } end: if (!ret && fd >= 0) { socket_close(fd); fd = -1; } return fd; } static bool init_tcp_socket(netplay_t *netplay, void *direct_host, const char *server, uint16_t port) { char port_buf[16]; bool ret = false; const struct addrinfo *tmp_info = NULL; struct addrinfo *res = NULL; struct addrinfo hints = {0}; port_buf[0] = '\0'; if (!direct_host) { #ifdef HAVE_INET6 /* Default to hosting on IPv6 and IPv4 */ if (!server) hints.ai_family = AF_INET6; #endif hints.ai_socktype = SOCK_STREAM; if (!server) hints.ai_flags = AI_PASSIVE; snprintf(port_buf, sizeof(port_buf), "%hu", (unsigned short)port); if (getaddrinfo_retro(server, port_buf, &hints, &res) != 0) { #ifdef HAVE_INET6 try_wildcard: if (!server) { /* Didn't work with IPv6, try wildcard */ hints.ai_family = 0; if (getaddrinfo_retro(server, port_buf, &hints, &res) != 0) return false; } else #endif return false; } if (!res) return false; } else { /* I'll build my own addrinfo! */ struct netplay_host *host = (struct netplay_host *)direct_host; hints.ai_family = host->addr.sa_family; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = host->addrlen; hints.ai_addr = &host->addr; res = &hints; } /* If we're serving on IPv6, make sure we accept all connections, including * IPv4 */ #ifdef HAVE_INET6 if (!direct_host && !server && res->ai_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) res->ai_addr; #if defined(_MSC_VER) && _MSC_VER <= 1200 IN6ADDR_SETANY(sin6); #else sin6->sin6_addr = in6addr_any; #endif } #endif /* If "localhost" is used, it is important to check every possible * address for IPv4/IPv6. */ tmp_info = res; while (tmp_info) { struct sockaddr_storage sad = {0}; int fd = init_tcp_connection( tmp_info, direct_host || server, (struct sockaddr*)&sad, sizeof(sad)); if (fd >= 0) { ret = true; if (direct_host || server) { netplay->connections[0].active = true; netplay->connections[0].fd = fd; netplay->connections[0].addr = sad; } else { netplay->listen_fd = fd; } break; } tmp_info = tmp_info->ai_next; } if (res && !direct_host) freeaddrinfo_retro(res); if (!ret) { #ifdef HAVE_INET6 if (!direct_host && (hints.ai_family == AF_INET6)) goto try_wildcard; #endif RARCH_ERR("Failed to set up netplay sockets.\n"); } return ret; } static bool init_socket(netplay_t *netplay, void *direct_host, const char *server, uint16_t port) { if (!network_init()) return false; if (!init_tcp_socket(netplay, direct_host, server, port)) return false; if (netplay->is_server && netplay->nat_traversal) netplay_init_nat_traversal(netplay); return true; } static bool netplay_init_socket_buffers(netplay_t *netplay) { /* Make our packet buffer big enough for a save state and stall-frames-many * frames of input data, plus the headers for each of them */ size_t i; size_t packet_buffer_size = netplay->zbuffer_size + NETPLAY_MAX_STALL_FRAMES * 16; netplay->packet_buffer_size = packet_buffer_size; for (i = 0; i < netplay->connections_size; i++) { struct netplay_connection *connection = &netplay->connections[i]; if (connection->active) { if (connection->send_packet_buffer.data) { if (!netplay_resize_socket_buffer(&connection->send_packet_buffer, packet_buffer_size) || !netplay_resize_socket_buffer(&connection->recv_packet_buffer, packet_buffer_size)) return false; } else { if (!netplay_init_socket_buffer(&connection->send_packet_buffer, packet_buffer_size) || !netplay_init_socket_buffer(&connection->recv_packet_buffer, packet_buffer_size)) return false; } } } return true; } static bool netplay_init_serialization(netplay_t *netplay) { unsigned i; retro_ctx_size_info_t info; if (netplay->state_size) return true; core_serialize_size(&info); if (!info.size) return false; netplay->state_size = info.size; for (i = 0; i < netplay->buffer_size; i++) { netplay->buffer[i].state = calloc(netplay->state_size, 1); if (!netplay->buffer[i].state) { netplay->quirks |= NETPLAY_QUIRK_NO_SAVESTATES; return false; } } netplay->zbuffer_size = netplay->state_size * 2; netplay->zbuffer = (uint8_t *) calloc(netplay->zbuffer_size, 1); if (!netplay->zbuffer) { netplay->quirks |= NETPLAY_QUIRK_NO_TRANSMISSION; netplay->zbuffer_size = 0; return false; } return true; } /** * netplay_try_init_serialization * * Try to initialize serialization. For quirky cores. * * Returns true if serialization is now ready, false otherwise. */ bool netplay_try_init_serialization(netplay_t *netplay) { retro_ctx_serialize_info_t serial_info; if (netplay->state_size) return true; if (!netplay_init_serialization(netplay)) return false; /* Check if we can actually save */ serial_info.data_const = NULL; serial_info.data = netplay->buffer[netplay->run_ptr].state; serial_info.size = netplay->state_size; if (!core_serialize(&serial_info)) return false; /* Once initialized, we no longer exhibit this quirk */ netplay->quirks &= ~((uint64_t) NETPLAY_QUIRK_INITIALIZATION); return netplay_init_socket_buffers(netplay); } /** * netplay_wait_and_init_serialization * * Try very hard to initialize serialization, simulating multiple frames if * necessary. For quirky cores. * * Returns true if serialization is now ready, false otherwise. */ bool netplay_wait_and_init_serialization(netplay_t *netplay) { int frame; if (netplay->state_size) return true; /* Wait a maximum of 60 frames */ for (frame = 0; frame < 60; frame++) { if (netplay_try_init_serialization(netplay)) return true; #if defined(HAVE_THREADS) autosave_lock(); #endif core_run(); #if defined(HAVE_THREADS) autosave_unlock(); #endif } return false; } static bool netplay_init_buffers(netplay_t *netplay) { struct delta_frame *delta_frames = NULL; /* Enough to get ahead or behind by MAX_STALL_FRAMES frames, plus one for * other remote clients, plus one to send the stall message */ netplay->buffer_size = NETPLAY_MAX_STALL_FRAMES + 2; /* If we're the server, we need enough to get ahead AND behind by * MAX_STALL_FRAMES frame */ if (netplay->is_server) netplay->buffer_size *= 2; delta_frames = (struct delta_frame*)calloc(netplay->buffer_size, sizeof(*delta_frames)); if (!delta_frames) return false; netplay->buffer = delta_frames; if (!(netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_INITIALIZATION))) netplay_init_serialization(netplay); return netplay_init_socket_buffers(netplay); } /** * netplay_new: * @direct_host : Netplay host discovered from scanning. * @server : IP address of server. * @port : Port of server. * @stateless_mode : Shall we use stateless mode? * @check_frames : Frequency with which to check CRCs. * @cb : Libretro callbacks. * @nat_traversal : If true, attempt NAT traversal. * @nick : Nickname of user. * @quirks : Netplay quirks required for this session. * * Creates a new netplay handle. A NULL server means we're * hosting. * * Returns: new netplay data. */ netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port, bool stateless_mode, int check_frames, const struct retro_callbacks *cb, bool nat_traversal, const char *nick, uint64_t quirks) { netplay_t *netplay = (netplay_t*)calloc(1, sizeof(*netplay)); if (!netplay) return NULL; netplay->listen_fd = -1; netplay->tcp_port = port; netplay->cbs = *cb; netplay->is_server = (direct_host == NULL && server == NULL); netplay->is_connected = false; netplay->nat_traversal = netplay->is_server ? nat_traversal : false; netplay->stateless_mode = stateless_mode; netplay->check_frames = check_frames; netplay->crc_validity_checked = false; netplay->crcs_valid = true; netplay->quirks = quirks; netplay->self_mode = netplay->is_server ? NETPLAY_CONNECTION_SPECTATING : NETPLAY_CONNECTION_NONE; if (netplay->is_server) { netplay->connections = NULL; netplay->connections_size = 0; } else { netplay->connections = &netplay->one_connection; netplay->connections_size = 1; netplay->connections[0].fd = -1; } strlcpy(netplay->nick, nick[0] ? nick : RARCH_DEFAULT_NICK, sizeof(netplay->nick)); if (!init_socket(netplay, direct_host, server, port)) { free(netplay); return NULL; } if (!netplay_init_buffers(netplay)) { free(netplay); return NULL; } if (netplay->is_server) { /* Clients get device info from the server */ unsigned i; for (i = 0; i < MAX_INPUT_DEVICES; i++) { uint32_t dtype = input_config_get_device(i); netplay->config_devices[i] = dtype; if ((dtype&RETRO_DEVICE_MASK) == RETRO_DEVICE_KEYBOARD) { netplay->have_updown_device = true; netplay_key_hton_init(); } if (dtype != RETRO_DEVICE_NONE && !netplay_expected_input_size(netplay, 1<connections[0]); netplay->connections[0].mode = NETPLAY_CONNECTION_INIT; netplay->self_mode = NETPLAY_CONNECTION_INIT; } /* FIXME: Not really the right place to do this, * socket initialization needs to be fixed in general. */ if (netplay->is_server) { if (!socket_nonblock(netplay->listen_fd)) goto error; } else { if (!socket_nonblock(netplay->connections[0].fd)) goto error; } return netplay; error: if (netplay->listen_fd >= 0) socket_close(netplay->listen_fd); if (netplay->connections && netplay->connections[0].fd >= 0) socket_close(netplay->connections[0].fd); free(netplay); return NULL; } /** * netplay_free * @netplay : pointer to netplay object * * Frees netplay data/ */ void netplay_free(netplay_t *netplay) { size_t i; if (netplay->listen_fd >= 0) socket_close(netplay->listen_fd); for (i = 0; i < netplay->connections_size; i++) { struct netplay_connection *connection = &netplay->connections[i]; if (connection->active) { socket_close(connection->fd); netplay_deinit_socket_buffer(&connection->send_packet_buffer); netplay_deinit_socket_buffer(&connection->recv_packet_buffer); } } if (netplay->connections && netplay->connections != &netplay->one_connection) free(netplay->connections); if (netplay->nat_traversal) natt_free(&netplay->nat_traversal_state); if (netplay->buffer) { for (i = 0; i < netplay->buffer_size; i++) netplay_delta_frame_free(&netplay->buffer[i]); free(netplay->buffer); } if (netplay->zbuffer) free(netplay->zbuffer); if (netplay->compress_nil.compression_stream) { netplay->compress_nil.compression_backend->stream_free(netplay->compress_nil.compression_stream); netplay->compress_nil.decompression_backend->stream_free(netplay->compress_nil.decompression_stream); } if (netplay->compress_zlib.compression_stream) { netplay->compress_zlib.compression_backend->stream_free(netplay->compress_zlib.compression_stream); netplay->compress_zlib.decompression_backend->stream_free(netplay->compress_zlib.decompression_stream); } if (netplay->addr) freeaddrinfo_retro(netplay->addr); free(netplay); }