/* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2017 - Daniel De Matteis * Copyright (C) 2016-2017 - Gregor Richards * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with RetroArch. * If not, see . */ #include #include #include #include #include #include "netplay_private.h" #include "../../configuration.h" #include "../../retroarch.h" #include "../../tasks/tasks_internal.h" 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_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("%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); netplay->is_connected = false; } RARCH_LOG("%s\n", dmsg); runloop_msg_queue_push(dmsg, 1, 180, false); 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 = 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) { struct netplay_connection *connection; size_t i; for (i = 0; i < netplay->connections_size; i++) { uint32_t client_num = i+1; 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("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) { struct delta_frame *dframe = &netplay->buffer[netplay->self_ptr]; uint32_t from_client, to_client; size_t i; netplay_input_state_t istate; if (netplay->is_server) { to_client = 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_cmd_crc * * Send a CRC command to all active clients. */ bool netplay_cmd_crc(netplay_t *netplay, struct delta_frame *delta) { uint32_t payload[2]; bool success = true; size_t i; 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. */ 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_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 payloadBuf = 0, *payload = NULL; uint8_t share_mode; settings_t *settings = config_get_ptr(); 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: payloadBuf = NETPLAY_CMD_PLAY_BIT_SLAVE; /* no break */ case NETPLAY_CONNECTION_PLAYING: payload = &payloadBuf; /* Add a share mode if requested */ share_mode = netplay_settings_share_mode(); payloadBuf |= ((uint32_t) share_mode) << 16; /* Request devices */ for (device = 0; device < MAX_INPUT_DEVICES; device++) { if (settings->bools.netplay_request_devices[device]) payloadBuf |= 1<is_server) { handle_play_spectate(netplay, 0, NULL, cmd, payload ? sizeof(uint32_t) : 0, payload); return true; } else { return netplay_send_raw_cmd(netplay, connection, cmd, payload, payload ? sizeof(uint32_t) : 0); } } /** * netplay_cmd_stall * * Send a stall command. */ 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)); } /** * 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_p(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[client_num] &= ~(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 && share_mode) { /* No device was totally free, maybe one is shareable? */ for (device = 0; device < MAX_INPUT_DEVICES; device++) { if (netplay->device_clients[device] && netplay->device_share_modes[device]) { share_mode = netplay->device_share_modes[device]; 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 flip_frame; uint32_t cmd_size; ssize_t recvd; char msg[512]; /* 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("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; unsigned i; 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 = 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("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); if (frame < netplay->server_frame_count) { /* We already had this, so ignore the new transmission */ 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("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 = 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]; settings_t *settings = config_get_ptr(); 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; } if (connection->mode == NETPLAY_CONNECTION_PLAYING || connection->mode == NETPLAY_CONNECTION_SLAVE) { /* They were obviously confused */ return netplay_cmd_nak(netplay, connection); } client_num = 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; netplay_input_state_t istate; /* It wants future frames, make sure we don't capture or send intermediate ones */ START(netplay->self_ptr); frame_count = netplay->self_frame_count; while (true) { 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("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("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("%s\n", dmsg); runloop_msg_queue_push(dmsg, 1, 180, false); } 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); if (!found) { /* Oh well, we got rid of it! */ 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 = netplay_delta_frame_crc( netplay, &netplay->buffer[tmp_ptr]); if (buffer[1] != local_crc) { /* Problem! */ netplay_cmd_request_savestate(netplay); } } else { /* We'll have to check it when we catch up */ netplay->buffer[tmp_ptr].crc = buffer[1]; } 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, client_num; uint32_t load_frame_count; size_t load_ptr; struct compression_transcoder *ctrans; client_num = 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("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("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); 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("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 client_num = i + 1; uint32_t devices, device; /* 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[512], 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 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); RARCH_LOG("%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); }