Cleaning up netplay headers.

This commit is contained in:
Gregor Richards 2016-12-15 08:42:03 -05:00
parent 9c6ac2b934
commit da7efcb939
9 changed files with 646 additions and 310 deletions

View File

@ -13,6 +13,8 @@
* If not, see <http://www.gnu.org/licenses/>. * If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <stdlib.h>
#include <net/net_compat.h> #include <net/net_compat.h>
#include <net/net_socket.h> #include <net/net_socket.h>
@ -47,6 +49,11 @@ static size_t buf_remaining(struct socket_buffer *sbuf)
return sbuf->bufsz - buf_used(sbuf) - 1; return sbuf->bufsz - buf_used(sbuf) - 1;
} }
/**
* netplay_init_socket_buffer
*
* Initialize a new socket buffer.
*/
bool netplay_init_socket_buffer(struct socket_buffer *sbuf, size_t size) bool netplay_init_socket_buffer(struct socket_buffer *sbuf, size_t size)
{ {
sbuf->data = malloc(size); sbuf->data = malloc(size);
@ -57,6 +64,11 @@ bool netplay_init_socket_buffer(struct socket_buffer *sbuf, size_t size)
return true; return true;
} }
/**
* netplay_resize_socket_buffer
*
* Resize the given socket_buffer's buffer to the requested size.
*/
bool netplay_resize_socket_buffer(struct socket_buffer *sbuf, size_t newsize) bool netplay_resize_socket_buffer(struct socket_buffer *sbuf, size_t newsize)
{ {
unsigned char *newdata = malloc(newsize); unsigned char *newdata = malloc(newsize);
@ -91,6 +103,11 @@ bool netplay_resize_socket_buffer(struct socket_buffer *sbuf, size_t newsize)
return true; return true;
} }
/**
* netplay_deinit_socket_buffer
*
* Free a socket buffer.
*/
void netplay_deinit_socket_buffer(struct socket_buffer *sbuf) void netplay_deinit_socket_buffer(struct socket_buffer *sbuf)
{ {
if (sbuf->data) if (sbuf->data)
@ -102,7 +119,13 @@ void netplay_clear_socket_buffer(struct socket_buffer *sbuf)
sbuf->start = sbuf->read = sbuf->end = 0; sbuf->start = sbuf->read = sbuf->end = 0;
} }
bool netplay_send(struct socket_buffer *sbuf, int sockfd, const void *buf, size_t len) /**
* 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) if (buf_remaining(sbuf) < len)
{ {
@ -143,6 +166,14 @@ bool netplay_send(struct socket_buffer *sbuf, int sockfd, const void *buf, size_
return netplay_send_flush(sbuf, sockfd, false); return netplay_send_flush(sbuf, sockfd, false);
} }
/**
* 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) bool netplay_send_flush(struct socket_buffer *sbuf, int sockfd, bool block)
{ {
ssize_t sent; ssize_t sent;
@ -205,7 +236,15 @@ bool netplay_send_flush(struct socket_buffer *sbuf, int sockfd, bool block)
return true; return true;
} }
ssize_t netplay_recv(struct socket_buffer *sbuf, int sockfd, void *buf, size_t len, bool block) /**
* netplay_recv
*
* Receive buffered or fresh data.
*
* Returns number of bytes returned, which may be short or 0, or -1 on error.
*/
ssize_t netplay_recv(struct socket_buffer *sbuf, int sockfd, void *buf,
size_t len, bool block)
{ {
bool error; bool error;
ssize_t recvd; ssize_t recvd;
@ -295,11 +334,23 @@ ssize_t netplay_recv(struct socket_buffer *sbuf, int sockfd, void *buf, size_t l
return recvd; 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) void netplay_recv_reset(struct socket_buffer *sbuf)
{ {
sbuf->read = sbuf->start; 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) void netplay_recv_flush(struct socket_buffer *sbuf)
{ {
sbuf->start = sbuf->read; sbuf->start = sbuf->read;

View File

@ -23,7 +23,17 @@
#include "netplay_private.h" #include "netplay_private.h"
bool netplay_delta_frame_ready(netplay_t *netplay, struct delta_frame *delta, uint32_t frame) /**
* 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)
{ {
void *remember_state; void *remember_state;
if (delta->used) if (delta->used)
@ -43,6 +53,11 @@ bool netplay_delta_frame_ready(netplay_t *netplay, struct delta_frame *delta, ui
return true; return true;
} }
/**
* netplay_delta_frame_crc
*
* Get the CRC for the serialization of this frame.
*/
uint32_t netplay_delta_frame_crc(netplay_t *netplay, struct delta_frame *delta) uint32_t netplay_delta_frame_crc(netplay_t *netplay, struct delta_frame *delta)
{ {
if (!netplay->state_size) if (!netplay->state_size)

View File

@ -32,6 +32,7 @@
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <compat/strl.h> #include <compat/strl.h>
@ -191,6 +192,11 @@ error:
return false; return false;
} }
/**
* netplay_lan_ad_server
*
* Respond to any LAN ad queries that the netplay server has received.
*/
bool netplay_lan_ad_server(netplay_t *netplay) bool netplay_lan_ad_server(netplay_t *netplay)
{ {
fd_set fds; fd_set fds;

View File

@ -28,10 +28,6 @@
#include "../../input/input_driver.h" #include "../../input/input_driver.h"
#include "../../runloop.h" #include "../../runloop.h"
#ifndef HAVE_SOCKET_LEGACY
static void announce_nat_traversal(netplay_t *netplay);
#endif
/* Only used before init_netplay */ /* Only used before init_netplay */
static bool netplay_enabled = false; static bool netplay_enabled = false;
static bool netplay_is_client = false; static bool netplay_is_client = false;
@ -286,13 +282,18 @@ static bool netplay_poll(void)
return true; return true;
} }
/* Netplay polling callbacks */ /**
* input_poll_net
*
* Poll the network if necessary.
*/
void input_poll_net(void) void input_poll_net(void)
{ {
if (!netplay_should_skip(netplay_data) && netplay_can_poll(netplay_data)) if (!netplay_should_skip(netplay_data) && netplay_can_poll(netplay_data))
netplay_poll(); netplay_poll();
} }
/* Netplay polling callbacks */
void video_frame_net(const void *data, unsigned width, void video_frame_net(const void *data, unsigned width,
unsigned height, size_t pitch) unsigned height, size_t pitch)
{ {
@ -436,6 +437,42 @@ static void netplay_flip_users(netplay_t *netplay)
netplay->flip_frame = flip_frame; netplay->flip_frame = flip_frame;
} }
/**
* 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;
/* Nothing to do if we already knew this */
if (netplay->local_paused == paused)
return;
netplay->local_paused = paused;
/* If other connections are paused, nothing to say */
if (netplay->remote_paused)
return;
/* Have to send manually because every buffer must be flushed immediately */
for (i = 0; i < netplay->connections_size; i++)
{
struct netplay_connection *connection = &netplay->connections[i];
if (connection->active && connection->mode >= NETPLAY_CONNECTION_CONNECTED)
{
netplay_send_raw_cmd(netplay, connection,
paused ? NETPLAY_CMD_PAUSE : 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_pre_frame: * netplay_pre_frame:
* @netplay : pointer to netplay object * @netplay : pointer to netplay object
@ -478,7 +515,7 @@ bool netplay_pre_frame(netplay_t *netplay)
#ifndef HAVE_SOCKET_LEGACY #ifndef HAVE_SOCKET_LEGACY
if (!netplay->nat_traversal_state.request_outstanding || if (!netplay->nat_traversal_state.request_outstanding ||
netplay->nat_traversal_state.have_inet4) netplay->nat_traversal_state.have_inet4)
announce_nat_traversal(netplay); netplay_announce_nat_traversal(netplay);
#endif #endif
} }
} }
@ -515,42 +552,6 @@ void netplay_post_frame(netplay_t *netplay)
} }
} }
/**
* 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)
**/
void netplay_frontend_paused(netplay_t *netplay, bool paused)
{
size_t i;
/* Nothing to do if we already knew this */
if (netplay->local_paused == paused)
return;
netplay->local_paused = paused;
/* If other connections are paused, nothing to say */
if (netplay->remote_paused)
return;
/* Have to send manually because every buffer must be flushed immediately */
for (i = 0; i < netplay->connections_size; i++)
{
struct netplay_connection *connection = &netplay->connections[i];
if (connection->active && connection->mode >= NETPLAY_CONNECTION_CONNECTED)
{
netplay_send_raw_cmd(netplay, connection,
paused ? NETPLAY_CMD_PAUSE : 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_load_savestate * netplay_load_savestate
* @netplay : pointer to netplay object * @netplay : pointer to netplay object
@ -669,63 +670,6 @@ void netplay_load_savestate(netplay_t *netplay,
} }
} }
/**
* netplay_init_nat_traversal
*
* Initialize the NAT traversal library and try to open a port
*/
void netplay_init_nat_traversal(netplay_t *netplay)
{
natt_init();
if (!natt_new(&netplay->nat_traversal_state))
{
netplay->nat_traversal = false;
return;
}
natt_open_port_any(&netplay->nat_traversal_state, netplay->tcp_port, SOCKET_PROTOCOL_TCP);
#ifndef HAVE_SOCKET_LEGACY
if (!netplay->nat_traversal_state.request_outstanding)
announce_nat_traversal(netplay);
#endif
}
#ifndef HAVE_SOCKET_LEGACY
static void announce_nat_traversal(netplay_t *netplay)
{
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_toggle_play_spectate * netplay_toggle_play_spectate
* *

View File

@ -229,7 +229,13 @@ static uint32_t simple_rand_uint32()
parts[2]); parts[2]);
} }
bool netplay_handshake_init_send(netplay_t *netplay, struct netplay_connection *connection) /**
* netplay_handshake_init_send
*
* Initialize our handshake and send the first part of the handshake protocol.
*/
bool netplay_handshake_init_send(netplay_t *netplay,
struct netplay_connection *connection)
{ {
uint32_t *content_crc_ptr = NULL; uint32_t *content_crc_ptr = NULL;
uint32_t header[5] = {0}; uint32_t header[5] = {0};
@ -307,7 +313,14 @@ static void handshake_password(void *ignore, const char *line)
rarch_ctl(RARCH_CTL_MENU_RUNNING_FINISHED, NULL); rarch_ctl(RARCH_CTL_MENU_RUNNING_FINISHED, NULL);
} }
bool netplay_handshake_init(netplay_t *netplay, struct netplay_connection *connection, bool *had_input) /**
* netplay_handshake_init
*
* Data receiver for the initial part of the handshake, i.e., waiting for the
* netplay header.
*/
bool netplay_handshake_init(netplay_t *netplay,
struct netplay_connection *connection, bool *had_input)
{ {
uint32_t header[5] = {0}; uint32_t header[5] = {0};
ssize_t recvd; ssize_t recvd;
@ -509,7 +522,14 @@ bool netplay_handshake_sync(netplay_t *netplay, struct netplay_connection *conne
return true; return true;
} }
bool netplay_handshake_pre_nick(netplay_t *netplay, struct netplay_connection *connection, bool *had_input) /**
* netplay_handshake_pre_nick
*
* Data receiver for the second stage of handshake, receiving the other side's
* nickname.
*/
bool netplay_handshake_pre_nick(netplay_t *netplay,
struct netplay_connection *connection, bool *had_input)
{ {
struct nick_buf_s nick_buf; struct nick_buf_s nick_buf;
ssize_t recvd; ssize_t recvd;
@ -567,7 +587,14 @@ bool netplay_handshake_pre_nick(netplay_t *netplay, struct netplay_connection *c
return true; return true;
} }
bool netplay_handshake_pre_password(netplay_t *netplay, struct netplay_connection *connection, bool *had_input) /**
* netplay_handshake_pre_password
*
* Data receiver for the third, optional stage of server handshake, receiving
* the password.
*/
bool netplay_handshake_pre_password(netplay_t *netplay,
struct netplay_connection *connection, bool *had_input)
{ {
struct password_buf_s password_buf, corr_password_buf; struct password_buf_s password_buf, corr_password_buf;
char password[8+NETPLAY_PASS_LEN]; /* 8 for salt */ char password[8+NETPLAY_PASS_LEN]; /* 8 for salt */
@ -630,7 +657,14 @@ bool netplay_handshake_pre_password(netplay_t *netplay, struct netplay_connectio
return true; return true;
} }
bool netplay_handshake_pre_sync(netplay_t *netplay, struct netplay_connection *connection, bool *had_input) /**
* netplay_handshake_pre_sync
*
* Data receiver for the client's third handshake stage, receiving the
* synchronization information.
*/
bool netplay_handshake_pre_sync(netplay_t *netplay,
struct netplay_connection *connection, bool *had_input)
{ {
uint32_t cmd[2]; uint32_t cmd[2];
uint32_t new_frame_count, connected_players, flip_frame; uint32_t new_frame_count, connected_players, flip_frame;

View File

@ -264,62 +264,6 @@ static bool netplay_init_socket_buffers(netplay_t *netplay)
return true; 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;
size_t packet_buffer_size;
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->self_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);
}
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;
}
bool netplay_init_serialization(netplay_t *netplay) bool netplay_init_serialization(netplay_t *netplay)
{ {
unsigned i; unsigned i;
@ -358,6 +302,70 @@ bool netplay_init_serialization(netplay_t *netplay)
return true; 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;
size_t packet_buffer_size;
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->self_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, unsigned frames) static bool netplay_init_buffers(netplay_t *netplay, unsigned frames)
{ {
size_t packet_buffer_size; size_t packet_buffer_size;
@ -397,15 +405,16 @@ static bool netplay_init_buffers(netplay_t *netplay, unsigned frames)
* @nick : Nickname of user. * @nick : Nickname of user.
* @quirks : Netplay quirks required for this session. * @quirks : Netplay quirks required for this session.
* *
* Creates a new netplay handle. A NULL host means we're * Creates a new netplay handle. A NULL server means we're
* hosting (user 1). * hosting.
* *
* Returns: new netplay handle. * Returns: new netplay data.
**/ */
netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port, netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port,
const char *play_password, const char *spectate_password, const char *play_password, const char *spectate_password,
unsigned delay_frames, unsigned check_frames, const struct retro_callbacks unsigned delay_frames, unsigned check_frames,
*cb, bool nat_traversal, const char *nick, uint64_t quirks) const struct retro_callbacks *cb, bool nat_traversal, const char *nick,
uint64_t quirks)
{ {
netplay_t *netplay = (netplay_t*)calloc(1, sizeof(*netplay)); netplay_t *netplay = (netplay_t*)calloc(1, sizeof(*netplay));
if (!netplay) if (!netplay)
@ -486,11 +495,11 @@ error:
} }
/** /**
* netplay_free: * netplay_free
* @netplay : pointer to netplay object * @netplay : pointer to netplay object
* *
* Frees netplay handle. * Frees netplay data/
**/ */
void netplay_free(netplay_t *netplay) void netplay_free(netplay_t *netplay)
{ {
size_t i; size_t i;

View File

@ -54,7 +54,7 @@ static void remote_unpaused(netplay_t *netplay, struct netplay_connection *conne
* netplay_hangup: * netplay_hangup:
* *
* Disconnects an active Netplay connection due to an error * Disconnects an active Netplay connection due to an error
**/ */
void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection) void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection)
{ {
if (!netplay) if (!netplay)
@ -145,7 +145,13 @@ static bool send_input_frame(netplay_t *netplay,
return true; return true;
} }
/* Send the current input frame */ /**
* 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, bool netplay_send_cur_input(netplay_t *netplay,
struct netplay_connection *connection) struct netplay_connection *connection)
{ {
@ -203,9 +209,9 @@ bool netplay_send_cur_input(netplay_t *netplay,
/** /**
* netplay_send_raw_cmd * netplay_send_raw_cmd
* *
* Send a raw Netplay command to the given connection * Send a raw Netplay command to the given connection.
* *
* Returns true on success, false on failure * Returns true on success, false on failure.
*/ */
bool netplay_send_raw_cmd(netplay_t *netplay, bool netplay_send_raw_cmd(netplay_t *netplay,
struct netplay_connection *connection, uint32_t cmd, const void *data, struct netplay_connection *connection, uint32_t cmd, const void *data,
@ -258,6 +264,11 @@ static bool netplay_cmd_nak(netplay_t *netplay,
return false; return false;
} }
/**
* netplay_cmd_crc
*
* Send a CRC command to all active clients.
*/
bool netplay_cmd_crc(netplay_t *netplay, struct delta_frame *delta) bool netplay_cmd_crc(netplay_t *netplay, struct delta_frame *delta)
{ {
uint32_t payload[2]; uint32_t payload[2];
@ -275,6 +286,11 @@ bool netplay_cmd_crc(netplay_t *netplay, struct delta_frame *delta)
return success; return success;
} }
/**
* netplay_cmd_request_savestate
*
* Send a savestate request command.
*/
bool netplay_cmd_request_savestate(netplay_t *netplay) bool netplay_cmd_request_savestate(netplay_t *netplay)
{ {
if (netplay->connections_size == 0 || if (netplay->connections_size == 0 ||
@ -288,6 +304,11 @@ bool netplay_cmd_request_savestate(netplay_t *netplay)
NETPLAY_CMD_REQUEST_SAVESTATE, NULL, 0); NETPLAY_CMD_REQUEST_SAVESTATE, NULL, 0);
} }
/**
* netplay_cmd_mode
*
* Send a mode request command to either play or spectate.
*/
bool netplay_cmd_mode(netplay_t *netplay, bool netplay_cmd_mode(netplay_t *netplay,
struct netplay_connection *connection, struct netplay_connection *connection,
enum rarch_netplay_connection_mode mode) enum rarch_netplay_connection_mode mode)
@ -1079,3 +1100,65 @@ bool netplay_flip_port(netplay_t *netplay)
return netplay->flip ^ (frame < netplay->flip_frame); return netplay->flip ^ (frame < netplay->flip_frame);
} }
/**
* 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)
{
natt_init();
if (!natt_new(&netplay->nat_traversal_state))
{
netplay->nat_traversal = false;
return;
}
natt_open_port_any(&netplay->nat_traversal_state, netplay->tcp_port, SOCKET_PROTOCOL_TCP);
#ifndef HAVE_SOCKET_LEGACY
if (!netplay->nat_traversal_state.request_outstanding)
netplay_announce_nat_traversal(netplay);
#endif
}

View File

@ -24,16 +24,10 @@
#include <net/net_natt.h> #include <net/net_natt.h>
#include <features/features_cpu.h> #include <features/features_cpu.h>
#include <streams/trans_stream.h> #include <streams/trans_stream.h>
#include <retro_endianness.h>
#include "../../core.h"
#include "../../msg_hash.h" #include "../../msg_hash.h"
#include "../../verbosity.h" #include "../../verbosity.h"
#ifdef ANDROID
#define HAVE_IPV6
#endif
#define WORDS_PER_INPUT 3 /* Buttons, left stick, right stick */ #define WORDS_PER_INPUT 3 /* Buttons, left stick, right stick */
#define WORDS_PER_FRAME (WORDS_PER_INPUT+2) /* + frameno, playerno */ #define WORDS_PER_FRAME (WORDS_PER_INPUT+2) /* + frameno, playerno */
@ -419,8 +413,209 @@ struct netplay
uint32_t check_frames; uint32_t check_frames;
}; };
/***************************************************************
* NETPLAY-BUF.C
**************************************************************/
/**
* netplay_init_socket_buffer
*
* Initialize a new socket buffer.
*/
bool netplay_init_socket_buffer(struct socket_buffer *sbuf, size_t size);
/**
* netplay_resize_socket_buffer
*
* Resize the given socket_buffer's buffer to the requested size.
*/
bool netplay_resize_socket_buffer(struct socket_buffer *sbuf, size_t newsize);
/**
* netplay_deinit_socket_buffer
*
* Free a socket buffer.
*/
void netplay_deinit_socket_buffer(struct socket_buffer *sbuf);
/**
* netplay_send
*
* Queue the given data for sending.
*/
bool netplay_send(struct socket_buffer *sbuf, int sockfd, const void *buf,
size_t len);
/**
* 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);
/**
* 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);
/**
* 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);
/**
* 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);
/***************************************************************
* NETPLAY-DELTA.C
**************************************************************/
/**
* 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);
/**
* netplay_delta_frame_crc
*
* Get the CRC for the serialization of this frame.
*/
uint32_t netplay_delta_frame_crc(netplay_t *netplay, struct delta_frame *delta);
/***************************************************************
* NETPLAY-DISCOVERY.C
**************************************************************/
/**
* netplay_lan_ad_server
*
* Respond to any LAN ad queries that the netplay server has received.
*/
bool netplay_lan_ad_server(netplay_t *netplay);
/***************************************************************
* NETPLAY-FRONTEND.C
**************************************************************/
/**
* netplay_load_savestate
* @netplay : pointer to netplay object
* @serial_info : the savestate being loaded, NULL means
* "load it yourself"
* @save : Whether to save the provided serial_info
* into the frame buffer
*
* Inform Netplay of a savestate load and send it to the other side
**/
void netplay_load_savestate(netplay_t *netplay,
retro_ctx_serialize_info_t *serial_info, bool save);
/**
* input_poll_net
*
* Poll the network if necessary.
*/
void input_poll_net(void); void input_poll_net(void);
/***************************************************************
* NETPLAY-HANDSHAKE.C
**************************************************************/
/**
* netplay_handshake_init_send
*
* Initialize our handshake and send the first part of the handshake protocol.
*/
bool netplay_handshake_init_send(netplay_t *netplay,
struct netplay_connection *connection);
/**
* netplay_handshake_init
*
* Data receiver for the initial part of the handshake, i.e., waiting for the
* netplay header.
*/
bool netplay_handshake_init(netplay_t *netplay,
struct netplay_connection *connection, bool *had_input);
/**
* netplay_handshake_pre_nick
*
* Data receiver for the second stage of handshake, receiving the other side's
* nickname.
*/
bool netplay_handshake_pre_nick(netplay_t *netplay,
struct netplay_connection *connection, bool *had_input);
/**
* netplay_handshake_pre_password
*
* Data receiver for the third, optional stage of server handshake, receiving
* the password.
*/
bool netplay_handshake_pre_password(netplay_t *netplay,
struct netplay_connection *connection, bool *had_input);
/**
* netplay_handshake_pre_sync
*
* Data receiver for the client's third handshake stage, receiving the
* synchronization information.
*/
bool netplay_handshake_pre_sync(netplay_t *netplay,
struct netplay_connection *connection, bool *had_input);
/***************************************************************
* NETPLAY-INIT.C
**************************************************************/
/**
* 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);
/**
* 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);
/** /**
* netplay_new: * netplay_new:
* @direct_host : Netplay host discovered from scanning. * @direct_host : Netplay host discovered from scanning.
@ -435,160 +630,158 @@ void input_poll_net(void);
* @nick : Nickname of user. * @nick : Nickname of user.
* @quirks : Netplay quirks required for this session. * @quirks : Netplay quirks required for this session.
* *
* Creates a new netplay handle. A NULL host means we're * Creates a new netplay handle. A NULL server means we're
* hosting (user 1). * hosting.
* *
* Returns: new netplay handle. * Returns: new netplay data.
**/ */
netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port, netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port,
const char *play_password, const char *spectate_password, const char *play_password, const char *spectate_password,
unsigned delay_frames, unsigned check_frames, const struct retro_callbacks unsigned delay_frames, unsigned check_frames,
*cb, bool nat_traversal, const char *nick, uint64_t quirks); const struct retro_callbacks *cb, bool nat_traversal, const char *nick,
uint64_t quirks);
/** /**
* netplay_free: * netplay_free
* @netplay : pointer to netplay object * @netplay : pointer to netplay object
* *
* Frees netplay handle. * Frees netplay data/
**/ */
void netplay_free(netplay_t *handle); void netplay_free(netplay_t *netplay);
/***************************************************************
* NETPLAY-IO.C
**************************************************************/
/** /**
* netplay_pre_frame: * netplay_hangup:
* @netplay : pointer to netplay object
* *
* Pre-frame for Netplay. * Disconnects an active Netplay connection due to an error
* Call this before running retro_run(). */
* void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection);
* Returns: true (1) if the frontend is clear to emulate the frame, false (0)
* if we're stalled or paused
**/
bool netplay_pre_frame(netplay_t *handle);
/** /**
* netplay_post_frame: * netplay_send_cur_input
* @netplay : pointer to netplay object
* *
* Post-frame for Netplay. * Send the current input frame to a given connection.
* We check if we have new input and replay from recorded input.
* Call this after running retro_run().
**/
void netplay_post_frame(netplay_t *handle);
/**
* 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) * Returns true if successful, false otherwise.
**/ */
void netplay_frontend_paused(netplay_t *netplay, bool paused);
/**
* netplay_load_savestate
* @netplay : pointer to netplay object
* @serial_info : the savestate being loaded, NULL means "load it yourself"
* @save : whether to save the provided serial_info into the frame buffer
*
* Inform Netplay of a savestate load and send it to the other side
**/
void netplay_load_savestate(netplay_t *netplay, retro_ctx_serialize_info_t *serial_info, bool save);
/**
* netplay_disconnect
* @netplay : pointer to netplay object
*
* Disconnect netplay.
*
* Returns: true (1) if successful. At present, cannot fail.
**/
bool netplay_disconnect(netplay_t *netplay);
bool netplay_sync_pre_frame(netplay_t *netplay);
void netplay_sync_post_frame(netplay_t *netplay);
/* Normally called at init time, unless the INITIALIZATION quirk is set */
bool netplay_init_serialization(netplay_t *netplay);
/* Force serialization to be ready by fast-forwarding the core */
bool netplay_wait_and_init_serialization(netplay_t *netplay);
void netplay_simulate_input(netplay_t *netplay, size_t sim_ptr, bool resim);
void netplay_log_connection(const struct sockaddr_storage *their_addr,
unsigned slot, const char *nick);
bool netplay_get_nickname(netplay_t *netplay, int fd);
bool netplay_send_nickname(netplay_t *netplay, int fd);
/* Various netplay initialization modes: */
bool netplay_handshake_init_send(netplay_t *netplay, struct netplay_connection *connection);
bool netplay_handshake_init(netplay_t *netplay, struct netplay_connection *connection, bool *had_input);
bool netplay_handshake_pre_nick(netplay_t *netplay, struct netplay_connection *connection, bool *had_input);
bool netplay_handshake_pre_password(netplay_t *netplay, struct netplay_connection *connection, bool *had_input);
bool netplay_handshake_pre_sync(netplay_t *netplay, struct netplay_connection *connection, bool *had_input);
uint32_t netplay_impl_magic(void);
bool netplay_is_server(netplay_t* netplay);
bool netplay_delta_frame_ready(netplay_t *netplay, struct delta_frame *delta, uint32_t frame);
uint32_t netplay_delta_frame_crc(netplay_t *netplay, struct delta_frame *delta);
bool netplay_cmd_crc(netplay_t *netplay, struct delta_frame *delta);
bool netplay_cmd_request_savestate(netplay_t *netplay);
bool netplay_cmd_mode(netplay_t *netplay,
struct netplay_connection *connection,
enum rarch_netplay_connection_mode mode);
bool netplay_send_cur_input(netplay_t *netplay, bool netplay_send_cur_input(netplay_t *netplay,
struct netplay_connection *connection); struct netplay_connection *connection);
int netplay_poll_net_input(netplay_t *netplay, bool block); /**
* netplay_send_raw_cmd
void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection); *
* Send a raw Netplay command to the given connection.
void netplay_update_unread_ptr(netplay_t *netplay); *
* Returns true on success, false on failure.
bool netplay_flip_port(netplay_t *netplay); */
bool netplay_send_raw_cmd(netplay_t *netplay, bool netplay_send_raw_cmd(netplay_t *netplay,
struct netplay_connection *connection, uint32_t cmd, const void *data, struct netplay_connection *connection, uint32_t cmd, const void *data,
size_t size); size_t size);
/**
* 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, void netplay_send_raw_cmd_all(netplay_t *netplay,
struct netplay_connection *except, uint32_t cmd, const void *data, struct netplay_connection *except, uint32_t cmd, const void *data,
size_t size); size_t size);
bool netplay_try_init_serialization(netplay_t *netplay); /**
* netplay_cmd_crc
*
* Send a CRC command to all active clients.
*/
bool netplay_cmd_crc(netplay_t *netplay, struct delta_frame *delta);
/**
* netplay_cmd_request_savestate
*
* Send a savestate request command.
*/
bool netplay_cmd_request_savestate(netplay_t *netplay);
/**
* netplay_cmd_mode
*
* Send a mode request command to either play or spectate.
*/
bool netplay_cmd_mode(netplay_t *netplay,
struct netplay_connection *connection,
enum rarch_netplay_connection_mode mode);
/**
* netplay_poll_net_input
*
* Poll input from the network
*/
int netplay_poll_net_input(netplay_t *netplay, bool block);
/**
* netplay_flip_port
*
* Should we flip ports 0 and 1?
*/
bool netplay_flip_port(netplay_t *netplay);
/**
* netplay_announce_nat_traversal
*
* Announce successful NAT traversal.
*/
void netplay_announce_nat_traversal(netplay_t *netplay);
/**
* netplay_init_nat_traversal
*
* Initialize the NAT traversal library and try to open a port
*/
void netplay_init_nat_traversal(netplay_t *netplay); void netplay_init_nat_traversal(netplay_t *netplay);
/* DISCOVERY: */
bool netplay_lan_ad_server(netplay_t *netplay); /***************************************************************
* NETPLAY-SYNC.C
**************************************************************/
bool netplay_init_socket_buffer(struct socket_buffer *sbuf, size_t size); /**
* 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);
bool netplay_resize_socket_buffer(struct socket_buffer *sbuf, size_t newsize); /**
* netplay_simulate_input
* @netplay : pointer to netplay object
* @sim_ptr : frame index for which to simulate 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.
*/
void netplay_simulate_input(netplay_t *netplay, size_t sim_ptr, bool resim);
void netplay_deinit_socket_buffer(struct socket_buffer *sbuf); /**
* netplay_sync_pre_frame
* @netplay : pointer to netplay object
*
* Pre-frame for Netplay synchronization.
*/
bool netplay_sync_pre_frame(netplay_t *netplay);
void netplay_clear_socket_buffer(struct socket_buffer *sbuf); /**
* netplay_sync_post_frame
bool netplay_send(struct socket_buffer *sbuf, int sockfd, const void *buf, size_t len); * @netplay : pointer to netplay object
*
bool netplay_send_flush(struct socket_buffer *sbuf, int sockfd, bool block); * Post-frame for Netplay synchronization.
* We check if we have new input and replay from recorded input.
ssize_t netplay_recv(struct socket_buffer *sbuf, int sockfd, void *buf, size_t len, bool block); */
void netplay_sync_post_frame(netplay_t *netplay);
void netplay_recv_reset(struct socket_buffer *sbuf);
void netplay_recv_flush(struct socket_buffer *sbuf);
#endif #endif

View File

@ -32,7 +32,8 @@
* netplay_update_unread_ptr * netplay_update_unread_ptr
* *
* Update the global unread_ptr and unread_frame_count to correspond to the * Update the global unread_ptr and unread_frame_count to correspond to the
* earliest unread frame count of any connected player */ * earliest unread frame count of any connected player
*/
void netplay_update_unread_ptr(netplay_t *netplay) void netplay_update_unread_ptr(netplay_t *netplay)
{ {
if (netplay->is_server && !netplay->connected_players) if (netplay->is_server && !netplay->connected_players)
@ -70,7 +71,7 @@ void netplay_update_unread_ptr(netplay_t *netplay)
} }
/** /**
* netplay_simulate_input: * netplay_simulate_input
* @netplay : pointer to netplay object * @netplay : pointer to netplay object
* @sim_ptr : frame index for which to simulate input * @sim_ptr : frame index for which to simulate input
* @resim : are we resimulating, or simulating this frame for the * @resim : are we resimulating, or simulating this frame for the
@ -162,11 +163,11 @@ static void netplay_handle_frame_hash(netplay_t *netplay, struct delta_frame *de
} }
/** /**
* netplay_sync_pre_frame: * netplay_sync_pre_frame
* @netplay : pointer to netplay object * @netplay : pointer to netplay object
* *
* Pre-frame for Netplay synchronization. * Pre-frame for Netplay synchronization.
**/ */
bool netplay_sync_pre_frame(netplay_t *netplay) bool netplay_sync_pre_frame(netplay_t *netplay)
{ {
retro_ctx_serialize_info_t serial_info; retro_ctx_serialize_info_t serial_info;
@ -329,12 +330,12 @@ process:
} }
/** /**
* netplay_sync_post_frame: * netplay_sync_post_frame
* @netplay : pointer to netplay object * @netplay : pointer to netplay object
* *
* Post-frame for Netplay synchronization. * Post-frame for Netplay synchronization.
* We check if we have new input and replay from recorded input. * We check if we have new input and replay from recorded input.
**/ */
void netplay_sync_post_frame(netplay_t *netplay) void netplay_sync_post_frame(netplay_t *netplay)
{ {
netplay->self_ptr = NEXT_PTR(netplay->self_ptr); netplay->self_ptr = NEXT_PTR(netplay->self_ptr);