mirror of
https://github.com/libretro/RetroArch
synced 2025-01-30 12:32:52 +00:00
Netplay mode change improvements
(1) All mode change code unified, so server mode changes and client mode changes and announcements go through the same functions (2) New messages which are translateable and work with multiple input devices
This commit is contained in:
parent
507c6dc09f
commit
f7dba84c67
@ -52,7 +52,23 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N,
|
||||
"You have joined as player %d"
|
||||
"You have joined as player %u"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_YOU_HAVE_JOINED_WITH_INPUT_DEVICES_S,
|
||||
"You have joined with input devices %.*s"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_PLAYER_S_LEFT,
|
||||
"Player %.*s has left the game"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_S_HAS_JOINED_AS_PLAYER_N,
|
||||
"%2$.*1$s has joined as player %3$u"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_S_HAS_JOINED_WITH_INPUT_DEVICES_S,
|
||||
"%2$.*1$s has joined with input devices %4$.*3$s"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_IMPLEMENTATIONS_DIFFER,
|
||||
|
@ -161,6 +161,10 @@ enum msg_hash_enums
|
||||
MSG_WAITING_FOR_CLIENT,
|
||||
MSG_NETPLAY_YOU_HAVE_LEFT_THE_GAME,
|
||||
MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N,
|
||||
MSG_NETPLAY_YOU_HAVE_JOINED_WITH_INPUT_DEVICES_S,
|
||||
MSG_NETPLAY_PLAYER_S_LEFT,
|
||||
MSG_NETPLAY_S_HAS_JOINED_AS_PLAYER_N,
|
||||
MSG_NETPLAY_S_HAS_JOINED_WITH_INPUT_DEVICES_S,
|
||||
MSG_NETPLAY_IMPLEMENTATIONS_DIFFER,
|
||||
MSG_NETPLAY_ENDIAN_DEPENDENT,
|
||||
MSG_NETPLAY_PLATFORM_DEPENDENT,
|
||||
|
@ -1181,107 +1181,23 @@ uint8_t netplay_settings_share_mode(void)
|
||||
static void netplay_toggle_play_spectate(netplay_t *netplay)
|
||||
{
|
||||
size_t i;
|
||||
enum rarch_netplay_connection_mode mode;
|
||||
|
||||
if (netplay->is_server)
|
||||
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING ||
|
||||
netplay->self_mode == NETPLAY_CONNECTION_SLAVE)
|
||||
{
|
||||
/* FIXME: Duplication */
|
||||
uint32_t payload[7];
|
||||
char msg[512];
|
||||
const char *dmsg = NULL;
|
||||
payload[0] = htonl(netplay->self_frame_count);
|
||||
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING ||
|
||||
netplay->self_mode == NETPLAY_CONNECTION_SLAVE)
|
||||
{
|
||||
/* Mark us as no longer playing */
|
||||
/* FIXME: Refactor this */
|
||||
payload[1] = htonl(netplay->self_client_num);
|
||||
payload[2] = htonl(0);
|
||||
netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
|
||||
netplay->connected_players &= ~(1L);
|
||||
netplay->connected_slaves &= ~(1L);
|
||||
netplay->client_devices[0] = 0;
|
||||
for (i = 0; i < MAX_INPUT_DEVICES; i++)
|
||||
netplay->device_clients[i] &= ~(1L);
|
||||
netplay->self_devices = 0;
|
||||
|
||||
dmsg = msg_hash_to_str(MSG_NETPLAY_YOU_HAVE_LEFT_THE_GAME);
|
||||
|
||||
}
|
||||
else if (netplay->self_mode == NETPLAY_CONNECTION_SPECTATING)
|
||||
{
|
||||
/* FIXME: Own device request */
|
||||
uint32_t device;
|
||||
uint8_t share_mode = netplay_settings_share_mode();
|
||||
|
||||
/* Take an input device */
|
||||
for (device = 0; device < MAX_INPUT_DEVICES; device++)
|
||||
{
|
||||
if (input_config_get_device(device) == RETRO_DEVICE_NONE)
|
||||
{
|
||||
device = MAX_INPUT_DEVICES;
|
||||
break;
|
||||
}
|
||||
if (!netplay->device_clients[device])
|
||||
break;
|
||||
}
|
||||
if (device == MAX_INPUT_DEVICES && share_mode)
|
||||
{
|
||||
/* Share one */
|
||||
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)
|
||||
return; /* Failure! */
|
||||
|
||||
payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING);
|
||||
payload[2] = htonl(1<<device);
|
||||
netplay->self_mode = NETPLAY_CONNECTION_PLAYING;
|
||||
netplay->connected_players |= 1;
|
||||
netplay->client_devices[0] = netplay->self_devices = (1<<device);
|
||||
netplay->device_clients[device] |= 1;
|
||||
netplay->device_share_modes[device] = share_mode;
|
||||
netplay->read_ptr[0] = netplay->self_ptr;
|
||||
netplay->read_frame_count[0] = netplay->self_frame_count;
|
||||
|
||||
dmsg = msg;
|
||||
msg[sizeof(msg)-1] = '\0';
|
||||
snprintf(msg, sizeof(msg)-1, msg_hash_to_str(MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N), device+1);
|
||||
}
|
||||
|
||||
memcpy(payload + 3, netplay->device_share_modes, sizeof(netplay->device_share_modes));
|
||||
|
||||
RARCH_LOG("[netplay] %s\n", dmsg);
|
||||
runloop_msg_queue_push(dmsg, 1, 180, false);
|
||||
|
||||
netplay_send_raw_cmd_all(netplay, NULL, NETPLAY_CMD_MODE, payload, sizeof(payload));
|
||||
|
||||
/* Switch to spectator mode immediately */
|
||||
netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
|
||||
mode = NETPLAY_CONNECTION_SPECTATING;
|
||||
}
|
||||
else
|
||||
else if (netplay->self_mode == NETPLAY_CONNECTION_SPECTATING)
|
||||
{
|
||||
enum rarch_netplay_connection_mode mode;
|
||||
|
||||
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING ||
|
||||
netplay->self_mode == NETPLAY_CONNECTION_SLAVE)
|
||||
{
|
||||
/* Switch to spectator mode immediately */
|
||||
netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
|
||||
mode = NETPLAY_CONNECTION_SPECTATING;
|
||||
}
|
||||
else if (netplay->self_mode == NETPLAY_CONNECTION_SPECTATING)
|
||||
{
|
||||
/* Switch only after getting permission */
|
||||
mode = NETPLAY_CONNECTION_PLAYING;
|
||||
}
|
||||
else return;
|
||||
|
||||
netplay_cmd_mode(netplay, &netplay->one_connection, mode);
|
||||
/* Switch only after getting permission */
|
||||
mode = NETPLAY_CONNECTION_PLAYING;
|
||||
}
|
||||
else return;
|
||||
|
||||
netplay_cmd_mode(netplay, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1118,7 +1118,7 @@ bool netplay_handshake_pre_sync(netplay_t *netplay,
|
||||
|
||||
/* Ask to switch to playing mode if we should */
|
||||
if (!settings->bools.netplay_start_as_spectator)
|
||||
return netplay_cmd_mode(netplay, connection, NETPLAY_CONNECTION_PLAYING);
|
||||
return netplay_cmd_mode(netplay, NETPLAY_CONNECTION_PLAYING);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -28,6 +28,10 @@
|
||||
#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
|
||||
|
||||
@ -185,11 +189,12 @@ void netplay_delayed_state_change(netplay_t *netplay)
|
||||
connection->delay_frame <= netplay->self_frame_count)
|
||||
{
|
||||
/* Something was delayed! Prepare the MODE command */
|
||||
uint32_t payload[7];
|
||||
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)
|
||||
@ -360,8 +365,6 @@ bool netplay_send_raw_cmd(netplay_t *netplay,
|
||||
if (!netplay_send(&connection->send_packet_buffer, connection->fd, data, size))
|
||||
return false;
|
||||
|
||||
netplay_send_flush(&connection->send_packet_buffer, connection->fd, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -462,16 +465,21 @@ bool netplay_cmd_request_savestate(netplay_t *netplay)
|
||||
/**
|
||||
* netplay_cmd_mode
|
||||
*
|
||||
* Send a mode request command to either play or spectate.
|
||||
* Send a mode change request. As a server, the request is to ourself, and so
|
||||
* honored instantly.
|
||||
*/
|
||||
bool netplay_cmd_mode(netplay_t *netplay,
|
||||
struct netplay_connection *connection,
|
||||
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:
|
||||
@ -503,8 +511,18 @@ bool netplay_cmd_mode(netplay_t *netplay,
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return netplay_send_raw_cmd(netplay, connection, cmd, payload,
|
||||
payload ? sizeof(uint32_t) : 0);
|
||||
|
||||
if (netplay->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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -520,6 +538,366 @@ bool netplay_cmd_stall(netplay_t *netplay,
|
||||
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<<device))) continue;
|
||||
if (one_device == (uint32_t) -1)
|
||||
{
|
||||
one_device = device;
|
||||
}
|
||||
else
|
||||
{
|
||||
one_device = (uint32_t) -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (one_device != (uint32_t) -1)
|
||||
{
|
||||
/* Only have one device, simpler message */
|
||||
if (nick)
|
||||
{
|
||||
snprintf_p(msg, sizeof(msg) - 1,
|
||||
msg_hash_to_str(MSG_NETPLAY_S_HAS_JOINED_AS_PLAYER_N),
|
||||
NETPLAY_NICK_LEN, nick, one_device + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(msg, sizeof(msg) - 1,
|
||||
msg_hash_to_str(MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N),
|
||||
one_device + 1);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Multiple devices, so step one is to make the device string listing them all */
|
||||
device_str[0] = 0;
|
||||
device_str_len = 0;
|
||||
for (device = 0; device < MAX_INPUT_DEVICES; device++)
|
||||
{
|
||||
if (!(devices & (1<<device))) continue;
|
||||
if (device_str_len)
|
||||
device_str_len += snprintf(device_str + device_str_len,
|
||||
sizeof(device_str) - 1 - device_str_len, ", ");
|
||||
device_str_len += snprintf(device_str + device_str_len,
|
||||
sizeof(device_str) - 1 - device_str_len, "%u",
|
||||
(unsigned) device);
|
||||
}
|
||||
|
||||
/* Then we make the final string */
|
||||
if (nick)
|
||||
{
|
||||
snprintf_p(msg, sizeof(msg) - 1,
|
||||
msg_hash_to_str(
|
||||
MSG_NETPLAY_S_HAS_JOINED_WITH_INPUT_DEVICES_S),
|
||||
NETPLAY_NICK_LEN, nick, sizeof(device_str),
|
||||
device_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(msg, sizeof(msg) - 1,
|
||||
msg_hash_to_str(
|
||||
MSG_NETPLAY_YOU_HAVE_JOINED_WITH_INPUT_DEVICES_S),
|
||||
sizeof(device_str), device_str);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: /* wrong usage */
|
||||
break;
|
||||
}
|
||||
|
||||
if (msg[0])
|
||||
{
|
||||
RARCH_LOG("%s\n", msg);
|
||||
runloop_msg_queue_push(msg, 1, 180, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* handle_play_spectate
|
||||
*
|
||||
* Handle a play or spectate request
|
||||
*/
|
||||
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 *in_payload)
|
||||
{
|
||||
/*
|
||||
* MODE payload:
|
||||
* word 0: frame number
|
||||
* word 1: mode info (playing, slave, client number)
|
||||
* word 2: device bitmap
|
||||
* words 3-6: share modes for all devices
|
||||
* words 7-14: client nick
|
||||
*/
|
||||
uint32_t payload[15] = {0};
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case NETPLAY_CMD_SPECTATE:
|
||||
{
|
||||
size_t i;
|
||||
|
||||
/* The frame we haven't received is their end frame */
|
||||
if (connection)
|
||||
connection->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))) continue;
|
||||
if (!netplay->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))) continue;
|
||||
if (!netplay->device_clients[device])
|
||||
netplay->device_share_modes[device] = share_mode;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Find an available device */
|
||||
for (device = 0; device < MAX_INPUT_DEVICES; device++)
|
||||
{
|
||||
if (input_config_get_device(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;
|
||||
netplay->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))) continue;
|
||||
netplay->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), \
|
||||
@ -787,9 +1165,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
|
||||
case NETPLAY_CMD_SPECTATE:
|
||||
{
|
||||
uint32_t payload[7];
|
||||
uint32_t client_num;
|
||||
size_t i;
|
||||
|
||||
if (!netplay->is_server)
|
||||
{
|
||||
@ -797,7 +1173,11 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
client_num = connection - netplay->connections + 1;
|
||||
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)
|
||||
@ -806,38 +1186,16 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
/* The frame we haven't received is their end frame */
|
||||
connection->delay_frame = netplay->read_frame_count[client_num];
|
||||
client_num = connection - netplay->connections + 1;
|
||||
|
||||
/* Mark them as not playing anymore */
|
||||
connection->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 the player */
|
||||
payload[0] = htonl(connection->delay_frame);
|
||||
payload[1] = htonl(NETPLAY_CMD_MODE_BIT_YOU | client_num);
|
||||
payload[2] = htonl(0);
|
||||
memcpy(payload + 3, netplay->device_share_modes, sizeof(netplay->device_share_modes));
|
||||
netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
|
||||
|
||||
/* Announce it */
|
||||
msg[sizeof(msg) - 1] = '\0';
|
||||
snprintf(msg, sizeof(msg) - 1, "Player %d has left", client_num + 1);
|
||||
RARCH_LOG("%s\n", msg);
|
||||
runloop_msg_queue_push(msg, 1, 180, false);
|
||||
handle_play_spectate(netplay, client_num, connection, cmd, 0, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
case NETPLAY_CMD_PLAY:
|
||||
{
|
||||
uint32_t mode, devices = 0, device, client_num;
|
||||
uint8_t share_mode;
|
||||
uint32_t payload[7];
|
||||
bool slave = false;
|
||||
uint32_t client_num;
|
||||
uint32_t payload[1];
|
||||
settings_t *settings = config_get_ptr();
|
||||
|
||||
if (cmd_size != sizeof(uint32_t))
|
||||
@ -845,35 +1203,17 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
RARCH_ERR("Incorrect NETPLAY_CMD_PLAY payload size.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
RECV(&mode, sizeof(uint32_t))
|
||||
RECV(payload, sizeof(uint32_t))
|
||||
{
|
||||
RARCH_ERR("Failed to receive NETPLAY_CMD_PLAY payload.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
mode = ntohl(mode);
|
||||
|
||||
/* 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 (settings->bools.netplay_require_slaves)
|
||||
slave = true;
|
||||
else if (!settings->bools.netplay_allow_slaves)
|
||||
slave = false;
|
||||
|
||||
/* Start forming our response */
|
||||
payload[0] = htonl(netplay->self_frame_count + 1);
|
||||
|
||||
if (!netplay->is_server)
|
||||
{
|
||||
RARCH_ERR("NETPLAY_CMD_PLAY from a server.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
client_num = connection - netplay->connections + 1;
|
||||
|
||||
if (connection->delay_frame)
|
||||
{
|
||||
@ -898,131 +1238,19 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
if (devices)
|
||||
{
|
||||
/* Make sure the devices are available and/or shareable */
|
||||
for (device = 0; device < MAX_INPUT_DEVICES; device++)
|
||||
{
|
||||
if (!(devices & (1<<device))) continue;
|
||||
if (!netplay->device_clients[device]) continue;
|
||||
if (netplay->device_share_modes[device] && share_mode) continue;
|
||||
client_num = connection - netplay->connections + 1;
|
||||
|
||||
/* Device already taken and unshareable */
|
||||
payload[0] = htonl(NETPLAY_CMD_MODE_REFUSED_REASON_NOT_AVAILABLE);
|
||||
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))) continue;
|
||||
if (!netplay->device_clients[device])
|
||||
netplay->device_share_modes[device] = share_mode;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Find an available device */
|
||||
for (device = 0; device < MAX_INPUT_DEVICES; device++)
|
||||
{
|
||||
if (input_config_get_device(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);
|
||||
netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE_REFUSED, payload, sizeof(uint32_t));
|
||||
break;
|
||||
}
|
||||
devices = 1<<device;
|
||||
netplay->device_share_modes[device] = share_mode;
|
||||
|
||||
}
|
||||
|
||||
payload[2] = htonl(devices);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
||||
/* Mark them as playing */
|
||||
connection->mode =
|
||||
slave ? NETPLAY_CONNECTION_SLAVE : 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))) continue;
|
||||
netplay->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));
|
||||
netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_MODE,
|
||||
payload, sizeof(payload));
|
||||
|
||||
/* Announce it */
|
||||
msg[sizeof(msg) - 1] = '\0';
|
||||
snprintf(msg, sizeof(msg) - 1, "Player %d has joined", client_num + 1);
|
||||
RARCH_LOG("%s\n", msg);
|
||||
runloop_msg_queue_push(msg, 1, 180, false);
|
||||
|
||||
/* Tell the player */
|
||||
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));
|
||||
|
||||
/* And expect their data */
|
||||
netplay->read_ptr[client_num] = NEXT_PTR(netplay->self_ptr);
|
||||
netplay->read_frame_count[client_num] = netplay->self_frame_count + 1;
|
||||
handle_play_spectate(netplay, client_num, connection, cmd, cmd_size, payload);
|
||||
break;
|
||||
}
|
||||
|
||||
case NETPLAY_CMD_MODE:
|
||||
{
|
||||
uint32_t payload[7];
|
||||
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 { \
|
||||
@ -1069,6 +1297,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
|
||||
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)
|
||||
{
|
||||
@ -1157,10 +1386,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
}
|
||||
|
||||
/* Announce it */
|
||||
msg[sizeof(msg)-1] = '\0';
|
||||
snprintf(msg, sizeof(msg)-1, "You have joined as player %d", client_num+1);
|
||||
RARCH_LOG("%s\n", msg);
|
||||
runloop_msg_queue_push(msg, 1, 180, false);
|
||||
announce_play_spectate(netplay, NULL, NETPLAY_CONNECTION_PLAYING, devices);
|
||||
|
||||
#ifdef DEBUG_NETPLAY_STEPS
|
||||
RARCH_LOG("Received mode change self->%X\n", devices);
|
||||
@ -1184,9 +1410,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
netplay->device_clients[device] &= ~(1<<client_num);
|
||||
|
||||
/* Announce it */
|
||||
strlcpy(msg, "You have left the game", sizeof(msg));
|
||||
RARCH_LOG("%s\n", msg);
|
||||
runloop_msg_queue_push(msg, 1, 180, false);
|
||||
announce_play_spectate(netplay, NULL, NETPLAY_CONNECTION_SPECTATING, 0);
|
||||
|
||||
#ifdef DEBUG_NETPLAY_STEPS
|
||||
RARCH_LOG("Received mode change self->spectating\n");
|
||||
@ -1217,10 +1441,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
netplay->read_frame_count[client_num] = netplay->server_frame_count;
|
||||
|
||||
/* Announce it */
|
||||
msg[sizeof(msg)-1] = '\0';
|
||||
snprintf(msg, sizeof(msg)-1, "Player %d has joined", client_num+1);
|
||||
RARCH_LOG("%s\n", msg);
|
||||
runloop_msg_queue_push(msg, 1, 180, false);
|
||||
announce_play_spectate(netplay, nick, NETPLAY_CONNECTION_PLAYING, devices);
|
||||
|
||||
#ifdef DEBUG_NETPLAY_STEPS
|
||||
RARCH_LOG("Received mode change %u->%u\n", client_num, devices);
|
||||
@ -1236,10 +1457,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
netplay->device_clients[device] &= ~(1<<client_num);
|
||||
|
||||
/* Announce it */
|
||||
msg[sizeof(msg)-1] = '\0';
|
||||
snprintf(msg, sizeof(msg)-1, "Player %d has left", client_num+1);
|
||||
RARCH_LOG("%s\n", msg);
|
||||
runloop_msg_queue_push(msg, 1, 180, false);
|
||||
announce_play_spectate(netplay, nick, NETPLAY_CONNECTION_SPECTATING, 0);
|
||||
|
||||
#ifdef DEBUG_NETPLAY_STEPS
|
||||
RARCH_LOG("Received mode change %u->spectator\n", client_num);
|
||||
|
@ -96,6 +96,13 @@ typedef uint32_t client_bitmap_t;
|
||||
#define NETPLAY_COMPRESSION_SUPPORTED 0
|
||||
#endif
|
||||
|
||||
/* Positional snprintf */
|
||||
#ifdef _MSC_VER
|
||||
#define snprintf_p _snprintf_p
|
||||
#else /* sensible systems */
|
||||
#define snprintf_p snprintf
|
||||
#endif
|
||||
|
||||
enum netplay_cmd
|
||||
{
|
||||
/* Basic commands */
|
||||
@ -870,10 +877,10 @@ bool netplay_cmd_request_savestate(netplay_t *netplay);
|
||||
/**
|
||||
* netplay_cmd_mode
|
||||
*
|
||||
* Send a mode request command to either play or spectate.
|
||||
* Send a mode change request. As a server, the request is to ourself, and so
|
||||
* honored instantly.
|
||||
*/
|
||||
bool netplay_cmd_mode(netplay_t *netplay,
|
||||
struct netplay_connection *connection,
|
||||
enum rarch_netplay_connection_mode mode);
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user