/** * 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; } /* Flush what we can immediately */ return netplay_send_flush(sbuf, sockfd, false); }
/** * netplay_frontend_paused * @netplay : pointer to netplay object * @paused : true if frontend is paused * * Inform Netplay of the frontend's pause state (paused or otherwise) */ static void netplay_frontend_paused(netplay_t *netplay, bool paused) { size_t i; uint32_t paused_ct; /* Nothing to do if we already knew this */ if (netplay->local_paused == paused) return; netplay->local_paused = paused; /* Communicating this is a bit odd: If exactly one other connection is * paused, then we must tell them that we're unpaused, as from their * perspective we are. If more than one other connection is paused, then our * status as proxy means we are NOT unpaused to either of them. */ paused_ct = 0; for (i = 0; i < netplay->connections_size; i++) { struct netplay_connection *connection = &netplay->connections[i]; if (connection->active && connection->paused) paused_ct++; } if (paused_ct > 1) return; /* Send our unpaused status. Must send manually because we must immediately * flush the buffer: If we're paused, we won't be polled. */ for (i = 0; i < netplay->connections_size; i++) { struct netplay_connection *connection = &netplay->connections[i]; if (connection->active && connection->mode >= NETPLAY_CONNECTION_CONNECTED) { if (paused) netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_PAUSE, netplay->nick, NETPLAY_NICK_LEN); else netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_RESUME, NULL, 0); /* We're not going to be polled, so we need to flush this command now */ netplay_send_flush(&connection->send_packet_buffer, connection->fd, true); } } }
/** * netplay_post_frame: * @netplay : pointer to netplay object * * Post-frame for Netplay. * We check if we have new input and replay from recorded input. * Call this after running retro_run(). **/ void netplay_post_frame(netplay_t *netplay) { size_t i; retro_assert(netplay); netplay_update_unread_ptr(netplay); netplay_sync_post_frame(netplay, false); for (i = 0; i < netplay->connections_size; i++) { struct netplay_connection *connection = &netplay->connections[i]; if (connection->active && !netplay_send_flush(&connection->send_packet_buffer, connection->fd, false)) netplay_hangup(netplay, connection); } /* If we're disconnected, deinitialize */ if (!netplay->is_server && !netplay->connections[0].active) netplay_disconnect(netplay); }
/** * 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), false)) return false; sbuf->start = sbuf->end = 0; } else { sent = socket_send_all_nonblocking(sockfd, sbuf->data + sbuf->start, buf_used(sbuf), false); 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, false)) 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, false); 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; }