mirror of
https://github.com/CTCaer/RetroArch.git
synced 2024-12-22 10:49:02 +00:00
Very first tidbits of true multiplayer support (minus actual multiple
players so far)
This commit is contained in:
parent
b5cd187077
commit
53c46530aa
@ -109,13 +109,18 @@ Command: INPUT
|
||||
Payload:
|
||||
{
|
||||
frame number: uint32
|
||||
is server data: 1 bit
|
||||
player: 31 bits
|
||||
joypad input: uint32
|
||||
analog 1 input: uint32
|
||||
analog 2 input: uint32
|
||||
}
|
||||
Description:
|
||||
Input state for each frame. Netplay must send an INPUT command for every
|
||||
frame in order to function at all.
|
||||
frame in order to function at all. Client's player value is ignored. Server
|
||||
indicates which frames are its own input data because INPUT is a
|
||||
synchronization point: No synchronization events from the given frame may
|
||||
arrive after the server's input for the frame.
|
||||
|
||||
Command: NICK
|
||||
Payload:
|
||||
@ -128,7 +133,6 @@ Description:
|
||||
Command: SYNC
|
||||
Payload:
|
||||
{
|
||||
connection number: uint32
|
||||
frame number: uint32
|
||||
sram: variable
|
||||
}
|
||||
@ -139,23 +143,32 @@ Description:
|
||||
Command: SPECTATE
|
||||
Payload: None
|
||||
Description:
|
||||
Request to enter spectate mode.
|
||||
Request to enter spectate mode. The client should immediately consider
|
||||
itself to be in spectator mode and send no further input.
|
||||
|
||||
Command: PLAY
|
||||
Payload: None
|
||||
Description:
|
||||
Request to enter player mode.
|
||||
Request to enter player mode. The client must wait for a MODE command
|
||||
before sending input.
|
||||
|
||||
Command: MODE
|
||||
Payload:
|
||||
{
|
||||
frame number: uint32
|
||||
connection number: uint32
|
||||
player number: uint32 (MAX for spectator mode)
|
||||
reserved: 14 bits
|
||||
playing: 1 bit
|
||||
you: 1 bit
|
||||
player number: uint16
|
||||
}
|
||||
Description:
|
||||
Inform of a connection mode change (possibly the connection of the
|
||||
receiving client). Only server-to-client.
|
||||
Inform of a connection mode change (possibly of the receiving client). Only
|
||||
server-to-client. Frame number is the first frame in which player data is
|
||||
expected, or the first frame in which player data is not expected. In the
|
||||
case of new players the frame number must be later than the last frame of
|
||||
the server's own input that has been sent, and in the case of leaving
|
||||
players the frame number must be later than the last frame of the relevant
|
||||
player's input that has been transmitted.
|
||||
|
||||
Command: CRC
|
||||
Payload:
|
||||
|
@ -60,6 +60,10 @@ static bool in_netplay = false;
|
||||
static void announce_nat_traversal(netplay_t *netplay);
|
||||
#endif
|
||||
|
||||
static void netplay_send_raw_cmd_all(netplay_t *netplay,
|
||||
struct netplay_connection *except, uint32_t cmd, const void *data,
|
||||
size_t size);
|
||||
|
||||
static int init_tcp_connection(const struct addrinfo *res,
|
||||
bool server,
|
||||
struct sockaddr *other_addr, socklen_t addr_size)
|
||||
@ -292,19 +296,18 @@ static void hangup(netplay_t *netplay, struct netplay_connection *connection)
|
||||
if (!netplay->is_server)
|
||||
netplay->self_mode = NETPLAY_CONNECTION_NONE;
|
||||
|
||||
/* Check if we have any more players */
|
||||
if (netplay->have_player_connections && connection->mode == NETPLAY_CONNECTION_PLAYING)
|
||||
/* Remove this player */
|
||||
if (connection->mode == NETPLAY_CONNECTION_PLAYING)
|
||||
{
|
||||
size_t i;
|
||||
netplay->have_player_connections = false;
|
||||
for (i = 0; i < netplay->connections_size; i++)
|
||||
netplay->connected_players &= ~(1<<connection->player);
|
||||
|
||||
/* FIXME: Duplication */
|
||||
if (netplay->is_server)
|
||||
{
|
||||
struct netplay_connection *connection = &netplay->connections[i];
|
||||
if (connection->active && connection->mode == NETPLAY_CONNECTION_PLAYING)
|
||||
{
|
||||
netplay->have_player_connections = true;
|
||||
break;
|
||||
}
|
||||
uint32_t payload[2];
|
||||
payload[0] = htonl(netplay->foo_read_frame_count[connection->player]);
|
||||
payload[1] = htonl(connection->player);
|
||||
netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,7 +332,7 @@ static bool netplay_should_skip(netplay_t *netplay)
|
||||
{
|
||||
if (!netplay)
|
||||
return false;
|
||||
return netplay->is_replay && (netplay->self_mode == NETPLAY_CONNECTION_PLAYING);
|
||||
return netplay->is_replay && (netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED);
|
||||
}
|
||||
|
||||
static bool netplay_can_poll(netplay_t *netplay)
|
||||
@ -339,6 +342,44 @@ static bool netplay_can_poll(netplay_t *netplay)
|
||||
return netplay->can_poll;
|
||||
}
|
||||
|
||||
/* Update the global unread_ptr and unread_frame_count to correspond to the
|
||||
* earliest unread frame count of any connected player */
|
||||
static void update_unread_ptr(netplay_t *netplay)
|
||||
{
|
||||
if (!netplay->connected_players)
|
||||
{
|
||||
/* 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 player;
|
||||
|
||||
for (player = 0; player < MAX_USERS; player++)
|
||||
{
|
||||
if (!(netplay->connected_players & (1<<player))) continue;
|
||||
if (netplay->foo_read_frame_count[player] < new_unread_frame_count)
|
||||
{
|
||||
new_unread_ptr = netplay->foo_read_ptr[player];
|
||||
new_unread_frame_count = netplay->foo_read_frame_count[player];
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
netplay->unread_ptr = new_unread_ptr;
|
||||
netplay->unread_frame_count = new_unread_frame_count;
|
||||
}
|
||||
}
|
||||
|
||||
/* Send the current input state, either immediately after receiving it or after
|
||||
* finishing the initial handshake */
|
||||
static void send_input(netplay_t *netplay, struct netplay_connection *connection)
|
||||
@ -358,6 +399,36 @@ static void send_input(netplay_t *netplay, struct netplay_connection *connection
|
||||
}
|
||||
}
|
||||
|
||||
/* Send a specified input frame */
|
||||
static void send_input_frame(netplay_t *netplay, uint32_t frame, uint32_t *state)
|
||||
{
|
||||
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING)
|
||||
{
|
||||
uint32_t buffer[2 + WORDS_PER_FRAME];
|
||||
size_t i;
|
||||
|
||||
buffer[0] = htonl(NETPLAY_CMD_INPUT);
|
||||
buffer[1] = htonl(WORDS_PER_FRAME * sizeof(uint32_t));
|
||||
buffer[2] = htonl(netplay->self_frame_count);
|
||||
buffer[3] = htonl(netplay->self_player |
|
||||
(netplay->is_server ? NETPLAY_CMD_INPUT_BIT_SERVER : 0));
|
||||
buffer[4] = htonl(state[0]);
|
||||
buffer[5] = htonl(state[1]);
|
||||
buffer[6] = htonl(state[2]);
|
||||
|
||||
for (i = 0; i < netplay->connections_size; i++)
|
||||
{
|
||||
struct netplay_connection *connection = &netplay->connections[i];
|
||||
if (connection->active && connection->mode >= NETPLAY_CONNECTION_CONNECTED)
|
||||
{
|
||||
if (!netplay_send(&connection->send_packet_buffer, connection->fd,
|
||||
buffer, sizeof(buffer)))
|
||||
hangup(netplay, connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get_self_input_state:
|
||||
* @netplay : pointer to netplay object
|
||||
@ -390,27 +461,36 @@ static bool get_self_input_state(netplay_t *netplay)
|
||||
retro_input_state_t cb = netplay->cbs.state_cb;
|
||||
for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++)
|
||||
{
|
||||
int16_t tmp = cb(settings->netplay.swap_input ?
|
||||
0 : !netplay->port,
|
||||
int16_t tmp = cb(0,
|
||||
RETRO_DEVICE_JOYPAD, 0, i);
|
||||
state[0] |= tmp ? 1 << i : 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
int16_t tmp_x = cb(settings->netplay.swap_input ?
|
||||
0 : !netplay->port,
|
||||
int16_t tmp_x = cb(0,
|
||||
RETRO_DEVICE_ANALOG, i, 0);
|
||||
int16_t tmp_y = cb(settings->netplay.swap_input ?
|
||||
0 : !netplay->port,
|
||||
int16_t tmp_y = cb(0,
|
||||
RETRO_DEVICE_ANALOG, i, 1);
|
||||
state[1 + i] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(ptr->self_state, state, sizeof(state));
|
||||
ptr->have_local = true;
|
||||
|
||||
/* If we're playing, copy it in as real input */
|
||||
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING)
|
||||
{
|
||||
memcpy(ptr->remote_input_state[netplay->self_player], state,
|
||||
sizeof(state));
|
||||
ptr->have_remote[netplay->self_player] = true;
|
||||
}
|
||||
|
||||
/* Here we construct the payload format:
|
||||
* frame {
|
||||
* uint32_t frame_number
|
||||
* uint32_t player
|
||||
* uint32_t RETRO_DEVICE_JOYPAD state (top 16 bits zero)
|
||||
* uint32_t ANALOG state[0]
|
||||
* uint32_t ANALOG state[1]
|
||||
@ -425,9 +505,11 @@ static bool get_self_input_state(netplay_t *netplay)
|
||||
netplay->input_packet_buffer[0] = htonl(NETPLAY_CMD_INPUT);
|
||||
netplay->input_packet_buffer[1] = htonl(WORDS_PER_FRAME * sizeof(uint32_t));
|
||||
netplay->input_packet_buffer[2] = htonl(netplay->self_frame_count);
|
||||
netplay->input_packet_buffer[3] = htonl(state[0]);
|
||||
netplay->input_packet_buffer[4] = htonl(state[1]);
|
||||
netplay->input_packet_buffer[5] = htonl(state[2]);
|
||||
netplay->input_packet_buffer[3] = htonl(netplay->self_player |
|
||||
(netplay->is_server ? NETPLAY_CMD_INPUT_BIT_SERVER : 0));
|
||||
netplay->input_packet_buffer[4] = htonl(state[0]);
|
||||
netplay->input_packet_buffer[5] = htonl(state[1]);
|
||||
netplay->input_packet_buffer[6] = htonl(state[2]);
|
||||
|
||||
for (i = 0; i < netplay->connections_size; i++)
|
||||
{
|
||||
@ -435,8 +517,6 @@ static bool get_self_input_state(netplay_t *netplay)
|
||||
send_input(netplay, &netplay->connections[i]);
|
||||
}
|
||||
|
||||
memcpy(ptr->self_state, state, sizeof(state));
|
||||
ptr->have_local = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -460,6 +540,24 @@ static bool netplay_send_raw_cmd(netplay_t *netplay,
|
||||
return true;
|
||||
}
|
||||
|
||||
static 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))
|
||||
hangup(netplay, connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool netplay_cmd_nak(netplay_t *netplay,
|
||||
struct netplay_connection *connection)
|
||||
{
|
||||
@ -497,6 +595,27 @@ bool netplay_cmd_request_savestate(netplay_t *netplay)
|
||||
NETPLAY_CMD_REQUEST_SAVESTATE, NULL, 0);
|
||||
}
|
||||
|
||||
bool netplay_cmd_mode(netplay_t *netplay,
|
||||
struct netplay_connection *connection,
|
||||
enum rarch_netplay_connection_mode mode)
|
||||
{
|
||||
uint32_t cmd;
|
||||
switch (mode)
|
||||
{
|
||||
case NETPLAY_CONNECTION_SPECTATING:
|
||||
cmd = NETPLAY_CMD_SPECTATE;
|
||||
break;
|
||||
|
||||
case NETPLAY_CONNECTION_PLAYING:
|
||||
cmd = NETPLAY_CMD_PLAY;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return netplay_send_raw_cmd(netplay, connection, cmd, NULL, 0);
|
||||
}
|
||||
|
||||
static bool netplay_get_cmd(netplay_t *netplay,
|
||||
struct netplay_connection *connection, bool *had_input)
|
||||
{
|
||||
@ -562,7 +681,9 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
case NETPLAY_CMD_INPUT:
|
||||
{
|
||||
uint32_t buffer[WORDS_PER_FRAME];
|
||||
uint32_t player;
|
||||
unsigned i;
|
||||
struct delta_frame *dframe;
|
||||
|
||||
if (cmd_size != WORDS_PER_FRAME * sizeof(uint32_t))
|
||||
{
|
||||
@ -579,23 +700,51 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
for (i = 0; i < WORDS_PER_FRAME; i++)
|
||||
buffer[i] = ntohl(buffer[i]);
|
||||
|
||||
if (buffer[0] < netplay->read_frame_count)
|
||||
if (netplay->is_server)
|
||||
{
|
||||
/* Ignore the claimed player #, must be this client */
|
||||
if (connection->mode != NETPLAY_CONNECTION_PLAYING)
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
player = connection->player;
|
||||
}
|
||||
else
|
||||
{
|
||||
player = buffer[1] & ~NETPLAY_CMD_INPUT_BIT_SERVER;
|
||||
}
|
||||
|
||||
if (player >= MAX_USERS || !(netplay->connected_players & (1<<player)))
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
|
||||
if (buffer[0] < netplay->foo_read_frame_count[player])
|
||||
{
|
||||
/* We already had this, so ignore the new transmission */
|
||||
break;
|
||||
}
|
||||
else if (buffer[0] > netplay->read_frame_count)
|
||||
else if (buffer[0] > netplay->foo_read_frame_count[player])
|
||||
{
|
||||
/* Out of order = out of luck */
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
/* The data's good! */
|
||||
netplay->buffer[netplay->read_ptr].have_remote = true;
|
||||
memcpy(netplay->buffer[netplay->read_ptr].real_input_state,
|
||||
buffer + 1, sizeof(buffer) - sizeof(uint32_t));
|
||||
netplay->read_ptr = NEXT_PTR(netplay->read_ptr);
|
||||
netplay->read_frame_count++;
|
||||
dframe = &netplay->buffer[netplay->foo_read_ptr[player]];
|
||||
if (!netplay_delta_frame_ready(netplay, dframe, netplay->foo_read_frame_count[player]))
|
||||
{
|
||||
/* FIXME: Catastrophe! */
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
dframe->have_remote[player] = true;
|
||||
memcpy(dframe->remote_input_state[player], buffer + 2,
|
||||
WORDS_PER_INPUT*sizeof(uint32_t));
|
||||
netplay->foo_read_ptr[player] = NEXT_PTR(netplay->foo_read_ptr[player]);
|
||||
netplay->foo_read_frame_count[player]++;
|
||||
|
||||
/* If this was server data, advance our server pointer too */
|
||||
if (!netplay->is_server && (buffer[1] & NETPLAY_CMD_INPUT_BIT_SERVER))
|
||||
{
|
||||
netplay->server_ptr = netplay->foo_read_ptr[player];
|
||||
netplay->server_frame_count = netplay->foo_read_frame_count[player];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -612,9 +761,12 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
if (netplay->is_server)
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
|
||||
flip_frame = ntohl(flip_frame);
|
||||
|
||||
if (flip_frame < netplay->read_frame_count)
|
||||
if (flip_frame < netplay->server_frame_count)
|
||||
{
|
||||
RARCH_ERR("Host asked us to flip users in the past. Not possible ...\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
@ -636,8 +788,183 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
break;
|
||||
|
||||
case NETPLAY_CMD_SPECTATE:
|
||||
RARCH_ERR("NETPLAY_CMD_SPECTATE unimplemented.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
{
|
||||
uint32_t payload[2];
|
||||
|
||||
if (!netplay->is_server)
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
|
||||
if (connection->mode == NETPLAY_CONNECTION_PLAYING)
|
||||
{
|
||||
/* The frame we haven't received is their end frame */
|
||||
payload[0] = htonl(netplay->foo_read_frame_count[connection->player]);
|
||||
|
||||
/* Mark them as not playing anymore */
|
||||
connection->mode = NETPLAY_CONNECTION_SPECTATING;
|
||||
|
||||
/* Tell everyone */
|
||||
payload[1] = htonl(connection->player);
|
||||
netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
|
||||
}
|
||||
else
|
||||
{
|
||||
payload[0] = htonl(0);
|
||||
}
|
||||
|
||||
/* Tell the player even if they were confused */
|
||||
payload[1] = htonl(NETPLAY_CMD_MODE_BIT_YOU | connection->player);
|
||||
netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
|
||||
break;
|
||||
}
|
||||
|
||||
case NETPLAY_CMD_PLAY:
|
||||
{
|
||||
uint32_t payload[2];
|
||||
uint32_t player = 1; /* FIXME */
|
||||
payload[0] = htonl(netplay->self_frame_count + 1);
|
||||
|
||||
if (!netplay->is_server)
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
|
||||
if (connection->mode != NETPLAY_CONNECTION_PLAYING)
|
||||
{
|
||||
/* Mark them as playing */
|
||||
connection->mode = NETPLAY_CONNECTION_PLAYING;
|
||||
connection->player = player;
|
||||
netplay->connected_players |= 1<<player;
|
||||
|
||||
/* Tell everyone */
|
||||
payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING | connection->player);
|
||||
netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
|
||||
}
|
||||
|
||||
/* Tell the player even if they were confused */
|
||||
payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING |
|
||||
NETPLAY_CMD_MODE_BIT_YOU | connection->player);
|
||||
netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
|
||||
|
||||
/* And expect their data */
|
||||
netplay->foo_read_ptr[player] = NEXT_PTR(netplay->self_ptr);
|
||||
netplay->foo_read_frame_count[player] = netplay->self_frame_count + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case NETPLAY_CMD_MODE:
|
||||
{
|
||||
uint32_t payload[2];
|
||||
uint32_t frame, mode, player;
|
||||
size_t ptr;
|
||||
struct delta_frame *dframe;
|
||||
#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 (cmd_size != sizeof(payload) ||
|
||||
netplay->is_server)
|
||||
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);
|
||||
}
|
||||
|
||||
if (netplay->is_server)
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
|
||||
frame = ntohl(payload[0]);
|
||||
if (frame != netplay->server_frame_count)
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
|
||||
/* We're changing past input, so must replay it */
|
||||
if (frame < netplay->self_frame_count)
|
||||
netplay->force_rewind = true;
|
||||
|
||||
mode = ntohl(payload[1]);
|
||||
player = mode & 0xFFFF;
|
||||
if (player > MAX_USERS)
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
|
||||
if (mode & NETPLAY_CMD_MODE_BIT_YOU)
|
||||
{
|
||||
/* A change to me! */
|
||||
if (mode & NETPLAY_CMD_MODE_BIT_PLAYING)
|
||||
{
|
||||
/* Hooray, I get to play now! */
|
||||
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING)
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
|
||||
netplay->self_mode = NETPLAY_CONNECTION_PLAYING;
|
||||
netplay->self_player = player;
|
||||
|
||||
/* Fix up current frame info */
|
||||
if (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)
|
||||
{
|
||||
memcpy(dframe->remote_input_state[player], dframe->self_state, sizeof(dframe->self_state));
|
||||
dframe->have_remote[player] = true;
|
||||
send_input_frame(netplay, frame, dframe->self_state);
|
||||
if (dframe->frame == netplay->self_frame_count) break;
|
||||
NEXT();
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It wants future frames, make sure we don't capture or send intermediate ones */
|
||||
START(netplay->self_ptr);
|
||||
while (dframe->used && dframe->frame < frame)
|
||||
{
|
||||
memset(dframe->self_state, 0, sizeof(dframe->self_state));
|
||||
memset(dframe->remote_input_state[player], 0, sizeof(dframe->self_state));
|
||||
dframe->have_local = true;
|
||||
NEXT();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else /* YOU && !PLAYING */
|
||||
{
|
||||
/* I'm no longer playing, but I should already know this */
|
||||
if (netplay->self_mode != NETPLAY_CONNECTION_SPECTATING)
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else /* !YOU */
|
||||
{
|
||||
/* Somebody else is joining or parting */
|
||||
if (mode & NETPLAY_CMD_MODE_BIT_PLAYING)
|
||||
{
|
||||
netplay->connected_players |= (1<<player);
|
||||
|
||||
netplay->foo_read_ptr[player] = netplay->server_ptr;
|
||||
netplay->foo_read_frame_count[player] = netplay->server_frame_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
netplay->connected_players &= ~(1<<player);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
#undef START
|
||||
#undef NEXT
|
||||
}
|
||||
|
||||
case NETPLAY_CMD_DISCONNECT:
|
||||
hangup(netplay, connection);
|
||||
@ -737,6 +1064,10 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
}
|
||||
}
|
||||
|
||||
/* Only players may load states */
|
||||
if (connection->mode != NETPLAY_CONNECTION_PLAYING)
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
|
||||
/* There is a subtlty in whether the load comes before or after the
|
||||
* current frame:
|
||||
*
|
||||
@ -760,7 +1091,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
}
|
||||
frame = ntohl(frame);
|
||||
|
||||
if (frame != netplay->read_frame_count)
|
||||
if (frame != netplay->foo_read_frame_count[connection->player])
|
||||
{
|
||||
RARCH_ERR("CMD_LOAD_SAVESTATE loading a state out of order!\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
@ -789,7 +1120,8 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
netplay->decompression_backend->set_in(netplay->decompression_stream,
|
||||
netplay->zbuffer, cmd_size - 2*sizeof(uint32_t));
|
||||
netplay->decompression_backend->set_out(netplay->decompression_stream,
|
||||
(uint8_t*)netplay->buffer[netplay->read_ptr].state, netplay->state_size);
|
||||
(uint8_t*)netplay->buffer[netplay->foo_read_ptr[connection->player]].state,
|
||||
netplay->state_size);
|
||||
netplay->decompression_backend->trans(netplay->decompression_stream,
|
||||
true, &rd, &wn, NULL);
|
||||
|
||||
@ -801,14 +1133,14 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
* load into. If we refer directly to read_ptr, then we'll end
|
||||
* up never reading the input for read_frame_count itself, which
|
||||
* will make the other side unhappy. */
|
||||
netplay->self_ptr = PREV_PTR(netplay->read_ptr);
|
||||
netplay->self_ptr = PREV_PTR(netplay->foo_read_ptr[connection->player]);
|
||||
netplay->self_frame_count = frame - 1;
|
||||
}
|
||||
|
||||
/* And force rewind to it */
|
||||
netplay->force_rewind = true;
|
||||
netplay->savestate_request_outstanding = false;
|
||||
netplay->other_ptr = netplay->read_ptr;
|
||||
netplay->other_ptr = netplay->foo_read_ptr[connection->player];
|
||||
netplay->other_frame_count = frame;
|
||||
break;
|
||||
}
|
||||
@ -865,9 +1197,10 @@ static int poll_input(netplay_t *netplay, bool block)
|
||||
|
||||
/* If we're not ready for input, wait until we are.
|
||||
* Could fill the TCP buffer, stalling the other side. */
|
||||
/* FIXME: This won't work with uneven input, need to somehow stall */
|
||||
if (netplay_delta_frame_ready(netplay,
|
||||
&netplay->buffer[netplay->read_ptr],
|
||||
netplay->read_frame_count))
|
||||
&netplay->buffer[netplay->unread_ptr],
|
||||
netplay->unread_frame_count))
|
||||
{
|
||||
for (i = 0; i < netplay->connections_size; i++)
|
||||
{
|
||||
@ -879,8 +1212,10 @@ static int poll_input(netplay_t *netplay, bool block)
|
||||
|
||||
if (block)
|
||||
{
|
||||
update_unread_ptr(netplay);
|
||||
|
||||
/* If we were blocked for input, pass if we have this frame's input */
|
||||
if (netplay->read_frame_count > netplay->self_frame_count)
|
||||
if (netplay->unread_frame_count > netplay->self_frame_count)
|
||||
break;
|
||||
|
||||
/* If we're supposed to block but we didn't have enough input, wait for it */
|
||||
@ -924,7 +1259,7 @@ static int poll_input(netplay_t *netplay, bool block)
|
||||
*/
|
||||
void netplay_simulate_input(netplay_t *netplay, uint32_t sim_ptr, bool resim)
|
||||
{
|
||||
size_t prev = PREV_PTR(netplay->read_ptr);
|
||||
size_t prev = PREV_PTR(netplay->unread_ptr);
|
||||
struct delta_frame *pframe = &netplay->buffer[prev],
|
||||
*simframe = &netplay->buffer[sim_ptr];
|
||||
if (resim)
|
||||
@ -948,15 +1283,15 @@ void netplay_simulate_input(netplay_t *netplay, uint32_t sim_ptr, bool resim)
|
||||
(1U<<RETRO_DEVICE_ID_JOYPAD_DOWN) |
|
||||
(1U<<RETRO_DEVICE_ID_JOYPAD_LEFT) |
|
||||
(1U<<RETRO_DEVICE_ID_JOYPAD_RIGHT);
|
||||
uint32_t sim_state = simframe->simulated_input_state[0] & keep;
|
||||
sim_state |= pframe->real_input_state[0] & ~keep;
|
||||
simframe->simulated_input_state[0] = sim_state;
|
||||
uint32_t sim_state = simframe->simulated_input_state[0][0] & keep;
|
||||
sim_state |= pframe->remote_input_state[0][0] & ~keep;
|
||||
simframe->simulated_input_state[0][0] = sim_state;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(simframe->simulated_input_state,
|
||||
pframe->real_input_state,
|
||||
sizeof(pframe->real_input_state));
|
||||
pframe->remote_input_state,
|
||||
sizeof(pframe->remote_input_state));
|
||||
}
|
||||
}
|
||||
|
||||
@ -982,7 +1317,7 @@ static bool netplay_poll(void)
|
||||
/* Read Netplay input, block if we're configured to stall for input every
|
||||
* frame */
|
||||
if (netplay_data->delay_frames == 0 &&
|
||||
netplay_data->read_frame_count <= netplay_data->self_frame_count)
|
||||
netplay_data->unread_frame_count <= netplay_data->self_frame_count)
|
||||
res = poll_input(netplay_data, true);
|
||||
else
|
||||
res = poll_input(netplay_data, false);
|
||||
@ -1003,7 +1338,8 @@ static bool netplay_poll(void)
|
||||
switch (netplay_data->stall)
|
||||
{
|
||||
case NETPLAY_STALL_RUNNING_FAST:
|
||||
if (netplay_data->read_frame_count >= netplay_data->self_frame_count)
|
||||
update_unread_ptr(netplay_data);
|
||||
if (netplay_data->unread_frame_count >= netplay_data->self_frame_count)
|
||||
netplay_data->stall = NETPLAY_STALL_NONE;
|
||||
break;
|
||||
|
||||
@ -1012,7 +1348,8 @@ static bool netplay_poll(void)
|
||||
break;
|
||||
|
||||
default: /* not stalling */
|
||||
if (netplay_data->read_frame_count + netplay_data->delay_frames
|
||||
update_unread_ptr(netplay_data);
|
||||
if (netplay_data->unread_frame_count + netplay_data->delay_frames
|
||||
<= netplay_data->self_frame_count)
|
||||
{
|
||||
netplay_data->stall = NETPLAY_STALL_RUNNING_FAST;
|
||||
@ -1079,42 +1416,50 @@ static bool netplay_is_alive(void)
|
||||
{
|
||||
if (!netplay_data)
|
||||
return false;
|
||||
return netplay_data->have_player_connections;
|
||||
return !!netplay_data->connected_players;
|
||||
}
|
||||
|
||||
static bool netplay_flip_port(netplay_t *netplay, bool port)
|
||||
static bool netplay_flip_port(netplay_t *netplay)
|
||||
{
|
||||
size_t frame = netplay->self_frame_count;
|
||||
|
||||
if (netplay->flip_frame == 0)
|
||||
return port;
|
||||
return false;
|
||||
|
||||
if (netplay->is_replay)
|
||||
frame = netplay->replay_frame_count;
|
||||
|
||||
return port ^ netplay->flip ^ (frame < netplay->flip_frame);
|
||||
return netplay->flip ^ (frame < netplay->flip_frame);
|
||||
}
|
||||
|
||||
static int16_t netplay_input_state(netplay_t *netplay,
|
||||
bool port, unsigned device,
|
||||
unsigned port, unsigned device,
|
||||
unsigned idx, unsigned id)
|
||||
{
|
||||
size_t ptr = netplay->is_replay ?
|
||||
netplay->replay_ptr : netplay->self_ptr;
|
||||
|
||||
const uint32_t *curr_input_state = netplay->buffer[ptr].self_state;
|
||||
const uint32_t *curr_input_state = NULL;
|
||||
|
||||
if (netplay->port == (netplay_flip_port(netplay, port) ? 1 : 0))
|
||||
if (port <= 1)
|
||||
{
|
||||
if (netplay->buffer[ptr].have_remote)
|
||||
{
|
||||
netplay->buffer[ptr].used_real = true;
|
||||
curr_input_state = netplay->buffer[ptr].real_input_state;
|
||||
}
|
||||
else
|
||||
{
|
||||
curr_input_state = netplay->buffer[ptr].simulated_input_state;
|
||||
}
|
||||
/* Possibly flip the port */
|
||||
if (netplay_flip_port(netplay))
|
||||
port ^= 1;
|
||||
}
|
||||
else if (port >= MAX_USERS)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (netplay->buffer[ptr].have_remote[port])
|
||||
{
|
||||
netplay->buffer[ptr].used_real[port] = true;
|
||||
curr_input_state = netplay->buffer[ptr].remote_input_state[port];
|
||||
}
|
||||
else
|
||||
{
|
||||
curr_input_state = netplay->buffer[ptr].simulated_input_state[port];
|
||||
}
|
||||
|
||||
switch (device)
|
||||
@ -1137,13 +1482,7 @@ int16_t input_state_net(unsigned port, unsigned device,
|
||||
unsigned idx, unsigned id)
|
||||
{
|
||||
if (netplay_is_alive())
|
||||
{
|
||||
/* Only two players for now. */
|
||||
if (port > 1)
|
||||
return 0;
|
||||
|
||||
return netplay_input_state(netplay_data, port, device, idx, id);
|
||||
}
|
||||
return netplay_data->cbs.state_cb(port, device, idx, id);
|
||||
}
|
||||
|
||||
@ -1448,7 +1787,7 @@ netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port,
|
||||
netplay->listen_fd = -1;
|
||||
netplay->tcp_port = port;
|
||||
netplay->cbs = *cb;
|
||||
netplay->port = server ? 0 : 1;
|
||||
netplay->connected_players = 0;
|
||||
netplay->is_server = server == NULL;
|
||||
netplay->nat_traversal = netplay->is_server ? nat_traversal : false;
|
||||
netplay->delay_frames = delay_frames;
|
||||
@ -1681,7 +2020,7 @@ bool netplay_pre_frame(netplay_t *netplay)
|
||||
if (!netplay_sync_pre_frame(netplay))
|
||||
return false;
|
||||
|
||||
return (!netplay->have_player_connections ||
|
||||
return (!netplay->connected_players ||
|
||||
(!netplay->stall && !netplay->remote_paused));
|
||||
}
|
||||
|
||||
@ -1695,13 +2034,19 @@ bool netplay_pre_frame(netplay_t *netplay)
|
||||
**/
|
||||
void netplay_post_frame(netplay_t *netplay)
|
||||
{
|
||||
size_t i;
|
||||
retro_assert(netplay);
|
||||
update_unread_ptr(netplay);
|
||||
netplay_sync_post_frame(netplay);
|
||||
if (netplay->connections_size > 0 &&
|
||||
netplay->connections[0].active &&
|
||||
!netplay_send_flush(&netplay->connections[0].send_packet_buffer,
|
||||
netplay->connections[0].fd, false))
|
||||
hangup(netplay, &netplay->connections[0]);
|
||||
|
||||
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))
|
||||
hangup(netplay, &netplay->connections[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1785,10 +2130,25 @@ void netplay_load_savestate(netplay_t *netplay,
|
||||
|
||||
/* We need to ignore any intervening data from the other side,
|
||||
* and never rewind past this */
|
||||
if (netplay->read_frame_count < netplay->self_frame_count)
|
||||
update_unread_ptr(netplay);
|
||||
if (netplay->unread_frame_count < netplay->self_frame_count)
|
||||
{
|
||||
netplay->read_ptr = netplay->self_ptr;
|
||||
netplay->read_frame_count = netplay->self_frame_count;
|
||||
uint32_t player;
|
||||
for (player = 0; player < MAX_USERS; player++)
|
||||
{
|
||||
if (!(netplay->connected_players & (1<<player))) continue;
|
||||
if (netplay->foo_read_frame_count[player] < netplay->self_frame_count)
|
||||
{
|
||||
netplay->foo_read_ptr[player] = netplay->self_ptr;
|
||||
netplay->foo_read_frame_count[player] = netplay->self_frame_count;
|
||||
}
|
||||
}
|
||||
if (netplay->server_frame_count < netplay->self_frame_count)
|
||||
{
|
||||
netplay->server_ptr = netplay->self_ptr;
|
||||
netplay->server_frame_count = netplay->self_frame_count;
|
||||
}
|
||||
update_unread_ptr(netplay);
|
||||
}
|
||||
if (netplay->other_frame_count < netplay->self_frame_count)
|
||||
{
|
||||
|
@ -283,9 +283,6 @@ static void netplay_handshake_ready(netplay_t *netplay, struct netplay_connectio
|
||||
/* Unstall if we were waiting for this */
|
||||
if (netplay->stall == NETPLAY_STALL_NO_CONNECTION)
|
||||
netplay->stall = 0;
|
||||
|
||||
connection->mode = NETPLAY_CONNECTION_PLAYING;
|
||||
netplay->have_player_connections = true;
|
||||
}
|
||||
|
||||
bool netplay_handshake_pre_nick(netplay_t *netplay, struct netplay_connection *connection, bool *had_input)
|
||||
@ -322,16 +319,15 @@ bool netplay_handshake_pre_nick(netplay_t *netplay, struct netplay_connection *c
|
||||
if (netplay->is_server)
|
||||
{
|
||||
/* If we're the server, now we send sync info */
|
||||
uint32_t cmd[4];
|
||||
uint32_t cmd[3];
|
||||
retro_ctx_memory_info_t mem_info;
|
||||
|
||||
mem_info.id = RETRO_MEMORY_SAVE_RAM;
|
||||
core_get_memory(&mem_info);
|
||||
|
||||
cmd[0] = htonl(NETPLAY_CMD_SYNC);
|
||||
cmd[1] = htonl(2*sizeof(uint32_t) + mem_info.size);
|
||||
cmd[1] = htonl(sizeof(uint32_t) + mem_info.size);
|
||||
cmd[2] = htonl(netplay->self_frame_count);
|
||||
cmd[3] = htonl(1);
|
||||
|
||||
if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd,
|
||||
sizeof(cmd)))
|
||||
@ -342,13 +338,8 @@ bool netplay_handshake_pre_nick(netplay_t *netplay, struct netplay_connection *c
|
||||
false))
|
||||
return false;
|
||||
|
||||
/* They start one frame after us */
|
||||
netplay->other_frame_count = netplay->read_frame_count =
|
||||
netplay->self_frame_count + 1;
|
||||
netplay->other_ptr = netplay->read_ptr =
|
||||
NEXT_PTR(netplay->self_ptr);
|
||||
|
||||
/* Now we're ready! */
|
||||
connection->mode = NETPLAY_CONNECTION_SPECTATING;
|
||||
netplay_handshake_ready(netplay, connection);
|
||||
|
||||
}
|
||||
@ -368,7 +359,7 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, struct netplay_connection *c
|
||||
{
|
||||
uint32_t cmd[2];
|
||||
uint32_t local_sram_size, remote_sram_size;
|
||||
uint32_t new_frame_count, self_connection_num;
|
||||
uint32_t new_frame_count;
|
||||
size_t i;
|
||||
ssize_t recvd;
|
||||
retro_ctx_memory_info_t mem_info;
|
||||
@ -378,7 +369,7 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, struct netplay_connection *c
|
||||
|
||||
/* Only expecting a sync command */
|
||||
if (ntohl(cmd[0]) != NETPLAY_CMD_SYNC ||
|
||||
ntohl(cmd[1]) < 2*sizeof(uint32_t))
|
||||
ntohl(cmd[1]) < sizeof(uint32_t))
|
||||
{
|
||||
RARCH_ERR("%s\n",
|
||||
msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST));
|
||||
@ -390,14 +381,11 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, struct netplay_connection *c
|
||||
return false;
|
||||
new_frame_count = ntohl(new_frame_count);
|
||||
|
||||
/* And the connection number */
|
||||
RECV(&self_connection_num, sizeof(self_connection_num))
|
||||
return false;
|
||||
netplay->self_connection_num = ntohl(self_connection_num);
|
||||
|
||||
/* Reset our frame buffer so it's consistent between server and client */
|
||||
/* FIXME: Assuming server is player 0 */
|
||||
netplay->self_frame_count = netplay->other_frame_count =
|
||||
netplay->read_frame_count = new_frame_count;
|
||||
netplay->unread_frame_count = netplay->server_frame_count =
|
||||
netplay->foo_read_frame_count[0] = new_frame_count;
|
||||
for (i = 0; i < netplay->buffer_size; i++)
|
||||
{
|
||||
struct delta_frame *ptr = &netplay->buffer[i];
|
||||
@ -409,7 +397,8 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, struct netplay_connection *c
|
||||
netplay_delta_frame_ready(netplay, ptr, 0);
|
||||
ptr->frame = new_frame_count;
|
||||
ptr->have_local = true;
|
||||
netplay->other_ptr = netplay->read_ptr = i;
|
||||
netplay->other_ptr = netplay->unread_ptr = netplay->server_ptr =
|
||||
netplay->foo_read_ptr[0] = i;
|
||||
|
||||
}
|
||||
}
|
||||
@ -419,7 +408,7 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, struct netplay_connection *c
|
||||
core_get_memory(&mem_info);
|
||||
|
||||
local_sram_size = mem_info.size;
|
||||
remote_sram_size = ntohl(cmd[1]) - 2*sizeof(uint32_t);
|
||||
remote_sram_size = ntohl(cmd[1]) - sizeof(uint32_t);
|
||||
|
||||
if (local_sram_size != 0 && local_sram_size == remote_sram_size)
|
||||
{
|
||||
@ -452,11 +441,15 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, struct netplay_connection *c
|
||||
}
|
||||
|
||||
/* We're ready! */
|
||||
netplay->self_mode = NETPLAY_CONNECTION_PLAYING;
|
||||
netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
|
||||
connection->mode = NETPLAY_CONNECTION_PLAYING;
|
||||
netplay->connected_players = 1;
|
||||
netplay_handshake_ready(netplay, connection);
|
||||
*had_input = true;
|
||||
netplay_recv_flush(&connection->recv_packet_buffer);
|
||||
return true;
|
||||
|
||||
/* Ask to go to player mode */
|
||||
return netplay_cmd_mode(netplay, connection, NETPLAY_CONNECTION_PLAYING);
|
||||
}
|
||||
|
||||
bool netplay_is_server(netplay_t* netplay)
|
||||
|
@ -247,10 +247,10 @@ void netplay_sync_post_frame(netplay_t *netplay)
|
||||
netplay->self_frame_count++;
|
||||
|
||||
/* Only relevant if we're connected */
|
||||
if (!netplay->have_player_connections)
|
||||
if (!netplay->connected_players)
|
||||
{
|
||||
netplay->read_frame_count = netplay->other_frame_count = netplay->self_frame_count;
|
||||
netplay->read_ptr = netplay->other_ptr = netplay->self_ptr;
|
||||
netplay->other_frame_count = netplay->self_frame_count;
|
||||
netplay->other_ptr = netplay->self_ptr;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -259,15 +259,20 @@ void netplay_sync_post_frame(netplay_t *netplay)
|
||||
{
|
||||
/* Skip ahead if we predicted correctly.
|
||||
* Skip until our simulation failed. */
|
||||
while (netplay->other_frame_count < netplay->read_frame_count &&
|
||||
netplay->other_frame_count < netplay->self_frame_count)
|
||||
while (netplay->other_frame_count < netplay->unread_frame_count &&
|
||||
netplay->other_frame_count < netplay->self_frame_count)
|
||||
{
|
||||
struct delta_frame *ptr = &netplay->buffer[netplay->other_ptr];
|
||||
size_t i;
|
||||
|
||||
if (memcmp(ptr->simulated_input_state, ptr->real_input_state,
|
||||
sizeof(ptr->real_input_state)) != 0
|
||||
&& !ptr->used_real)
|
||||
break;
|
||||
for (i = 0; i < MAX_USERS; i++)
|
||||
{
|
||||
if (memcmp(ptr->simulated_input_state[i], ptr->remote_input_state[i],
|
||||
sizeof(ptr->remote_input_state[i])) != 0
|
||||
&& !ptr->used_real[i])
|
||||
break;
|
||||
}
|
||||
if (i != MAX_USERS) break;
|
||||
netplay_handle_frame_hash(netplay, ptr);
|
||||
netplay->other_ptr = NEXT_PTR(netplay->other_ptr);
|
||||
netplay->other_frame_count++;
|
||||
@ -277,7 +282,7 @@ void netplay_sync_post_frame(netplay_t *netplay)
|
||||
|
||||
/* Now replay the real input if we've gotten ahead of it */
|
||||
if (netplay->force_rewind ||
|
||||
(netplay->other_frame_count < netplay->read_frame_count &&
|
||||
(netplay->other_frame_count < netplay->unread_frame_count &&
|
||||
netplay->other_frame_count < netplay->self_frame_count))
|
||||
{
|
||||
retro_ctx_serialize_info_t serial_info;
|
||||
@ -310,11 +315,11 @@ void netplay_sync_post_frame(netplay_t *netplay)
|
||||
/* Remember the current state */
|
||||
memset(serial_info.data, 0, serial_info.size);
|
||||
core_serialize(&serial_info);
|
||||
if (netplay->replay_frame_count < netplay->read_frame_count)
|
||||
if (netplay->replay_frame_count < netplay->unread_frame_count)
|
||||
netplay_handle_frame_hash(netplay, ptr);
|
||||
|
||||
/* Simulate this frame's input */
|
||||
if (netplay->replay_frame_count >= netplay->read_frame_count)
|
||||
if (netplay->replay_frame_count >= netplay->unread_frame_count)
|
||||
netplay_simulate_input(netplay, netplay->replay_ptr, true);
|
||||
|
||||
autosave_lock();
|
||||
@ -340,10 +345,10 @@ void netplay_sync_post_frame(netplay_t *netplay)
|
||||
#endif
|
||||
}
|
||||
|
||||
if (netplay->read_frame_count < netplay->self_frame_count)
|
||||
if (netplay->unread_frame_count < netplay->self_frame_count)
|
||||
{
|
||||
netplay->other_ptr = netplay->read_ptr;
|
||||
netplay->other_frame_count = netplay->read_frame_count;
|
||||
netplay->other_ptr = netplay->unread_ptr;
|
||||
netplay->other_frame_count = netplay->unread_frame_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -34,12 +34,14 @@
|
||||
#define HAVE_IPV6
|
||||
#endif
|
||||
|
||||
#define WORDS_PER_FRAME 4 /* Allows us to send 128 bits worth of state per frame. */
|
||||
#define RARCH_DEFAULT_PORT 55435
|
||||
#define RARCH_DEFAULT_NICK "Anonymous"
|
||||
#define WORDS_PER_INPUT 3 /* Buttons, left stick, right stick */
|
||||
#define WORDS_PER_FRAME (WORDS_PER_INPUT+2) /* + frameno, playerno */
|
||||
|
||||
#define NETPLAY_PROTOCOL_VERSION 4
|
||||
|
||||
#define RARCH_DEFAULT_PORT 55435
|
||||
#define RARCH_DEFAULT_NICK "Anonymous"
|
||||
|
||||
#define PREV_PTR(x) ((x) == 0 ? netplay->buffer_size - 1 : (x) - 1)
|
||||
#define NEXT_PTR(x) ((x + 1) % netplay->buffer_size)
|
||||
|
||||
@ -145,6 +147,10 @@ enum netplay_cmd
|
||||
NETPLAY_CMD_CFG_ACK = 0x0062
|
||||
};
|
||||
|
||||
#define NETPLAY_CMD_INPUT_BIT_SERVER (1U<<31)
|
||||
#define NETPLAY_CMD_MODE_BIT_PLAYING (1U<<17)
|
||||
#define NETPLAY_CMD_MODE_BIT_YOU (1U<<16)
|
||||
|
||||
/* These are the configurations sent by NETPLAY_CMD_CFG. */
|
||||
enum netplay_cmd_cfg
|
||||
{
|
||||
@ -183,6 +189,8 @@ enum rarch_netplay_stall_reason
|
||||
NETPLAY_STALL_NO_CONNECTION
|
||||
};
|
||||
|
||||
typedef uint32_t netplay_input_state_t[WORDS_PER_INPUT];
|
||||
|
||||
struct delta_frame
|
||||
{
|
||||
bool used; /* a bit derpy, but this is how we know if the delta's been used at all */
|
||||
@ -194,18 +202,18 @@ struct delta_frame
|
||||
/* The CRC-32 of the serialized state if we've calculated it, else 0 */
|
||||
uint32_t crc;
|
||||
|
||||
uint32_t real_input_state[WORDS_PER_FRAME - 1];
|
||||
uint32_t simulated_input_state[WORDS_PER_FRAME - 1];
|
||||
uint32_t self_state[WORDS_PER_FRAME - 1];
|
||||
netplay_input_state_t remote_input_state[MAX_USERS];
|
||||
netplay_input_state_t simulated_input_state[MAX_USERS];
|
||||
netplay_input_state_t self_state;
|
||||
|
||||
/* Have we read local input? */
|
||||
bool have_local;
|
||||
|
||||
/* Have we read the real remote input? */
|
||||
bool have_remote;
|
||||
bool have_remote[MAX_USERS];
|
||||
|
||||
/* Is the current state as of self_frame_count using the real remote data? */
|
||||
bool used_real;
|
||||
bool used_real[MAX_USERS];
|
||||
};
|
||||
|
||||
struct socket_buffer
|
||||
@ -237,7 +245,7 @@ struct netplay_connection
|
||||
/* Mode of the connection */
|
||||
enum rarch_netplay_connection_mode mode;
|
||||
|
||||
/* Player # of connected player, or -1 if not a player */
|
||||
/* Player # of connected player */
|
||||
int player;
|
||||
|
||||
/* Force send a savestate, to this connection only */
|
||||
@ -255,8 +263,8 @@ struct netplay
|
||||
/* TCP connection for listening (server only) */
|
||||
int listen_fd;
|
||||
|
||||
/* Our connection number */
|
||||
uint32_t self_connection_num;
|
||||
/* Our player number */
|
||||
uint32_t self_player;
|
||||
|
||||
/* Our mode and status */
|
||||
enum rarch_netplay_connection_mode self_mode;
|
||||
@ -266,9 +274,9 @@ struct netplay
|
||||
size_t connections_size;
|
||||
struct netplay_connection one_connection; /* Client only */
|
||||
|
||||
/* True if any of our connections are players (i.e., we actually need to do
|
||||
* netplay) */
|
||||
bool have_player_connections;
|
||||
/* Bitmap of players with controllers (whether local or remote) (low bit is
|
||||
* player 1) */
|
||||
int connected_players;
|
||||
|
||||
struct retro_callbacks cbs;
|
||||
|
||||
@ -279,9 +287,6 @@ struct netplay
|
||||
bool nat_traversal;
|
||||
struct natt_status nat_traversal_state;
|
||||
|
||||
/* Which port is governed by netplay (other user)? */
|
||||
unsigned port;
|
||||
|
||||
struct delta_frame *buffer;
|
||||
size_t buffer_size;
|
||||
|
||||
@ -306,11 +311,21 @@ struct netplay
|
||||
size_t other_ptr;
|
||||
uint32_t other_frame_count;
|
||||
|
||||
/* Pointer to where we are reading.
|
||||
* Generally, other_ptr <= read_ptr <= self_ptr, but read_ptr can get ahead
|
||||
/* Pointer to the first frame for which we're missing the data of at least
|
||||
* one connected player excluding ourself.
|
||||
* Generally, other_ptr <= unread_ptr <= self_ptr, but unread_ptr can get ahead
|
||||
* of self_ptr if the peer is running fast. */
|
||||
size_t read_ptr;
|
||||
uint32_t read_frame_count;
|
||||
size_t unread_ptr;
|
||||
uint32_t unread_frame_count;
|
||||
|
||||
/* Pointer to the next frame to read from each player */
|
||||
size_t foo_read_ptr[MAX_USERS];
|
||||
uint32_t foo_read_frame_count[MAX_USERS];
|
||||
|
||||
/* Pointer to the next frame to read from the server (as it might not be a
|
||||
* player but still synchronizes) */
|
||||
size_t server_ptr;
|
||||
uint32_t server_frame_count;
|
||||
|
||||
/* A pointer used temporarily for replay. */
|
||||
size_t replay_ptr;
|
||||
@ -492,6 +507,10 @@ 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);
|
||||
|
||||
/* DISCOVERY: */
|
||||
|
||||
bool netplay_lan_ad_server(netplay_t *netplay);
|
||||
|
Loading…
Reference in New Issue
Block a user