mirror of
https://github.com/CTCaer/RetroArch.git
synced 2024-12-21 02:08:16 +00:00
Merge pull request #3747 from GregorR/netplay-platform-quirks
Netplay platform quirks
This commit is contained in:
commit
151046db5a
@ -1471,6 +1471,10 @@ bool init_netplay(bool is_spectate, const char *server, unsigned port)
|
||||
quirks |= NETPLAY_QUIRK_NO_TRANSMISSION;
|
||||
if (serialization_quirks & NETPLAY_QUIRK_MAP_INITIALIZATION)
|
||||
quirks |= NETPLAY_QUIRK_INITIALIZATION;
|
||||
if (serialization_quirks & NETPLAY_QUIRK_MAP_ENDIAN_DEPENDENT)
|
||||
quirks |= NETPLAY_QUIRK_ENDIAN_DEPENDENT;
|
||||
if (serialization_quirks & NETPLAY_QUIRK_MAP_PLATFORM_DEPENDENT)
|
||||
quirks |= NETPLAY_QUIRK_PLATFORM_DEPENDENT;
|
||||
|
||||
if (netplay_is_client)
|
||||
{
|
||||
|
@ -125,24 +125,61 @@ uint32_t netplay_impl_magic(void)
|
||||
return res;
|
||||
}
|
||||
|
||||
bool netplay_send_info(netplay_t *netplay)
|
||||
/**
|
||||
* netplay_platform_magic
|
||||
*
|
||||
* Just enough info to tell us if our platforms mismatch: Endianness and a
|
||||
* couple of type sizes.
|
||||
*
|
||||
* Format:
|
||||
* bit 31: Reserved
|
||||
* bit 30: 1 for big endian
|
||||
* bits 29-15: sizeof(size_t)
|
||||
* bits 14-0: sizeof(long)
|
||||
*/
|
||||
static uint32_t netplay_platform_magic(void)
|
||||
{
|
||||
uint32_t ret =
|
||||
((1 == htonl(1)) << 30)
|
||||
|(sizeof(size_t) << 15)
|
||||
|(sizeof(long));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_endian_mismatch
|
||||
*
|
||||
* Do the platform magics mismatch on endianness?
|
||||
*/
|
||||
static bool netplay_endian_mismatch(uint32_t pma, uint32_t pmb)
|
||||
{
|
||||
uint32_t ebit = (1<<30);
|
||||
return (pma & ebit) != (pmb & ebit);
|
||||
}
|
||||
|
||||
bool netplay_handshake(netplay_t *netplay)
|
||||
{
|
||||
unsigned sram_size, remote_sram_size;
|
||||
retro_ctx_memory_info_t mem_info;
|
||||
char msg[512] = {0};
|
||||
uint32_t *content_crc_ptr = NULL;
|
||||
void *sram = NULL;
|
||||
uint32_t header[3] = {0};
|
||||
uint32_t header[4] = {0};
|
||||
size_t i;
|
||||
uint32_t local_pmagic, remote_pmagic;
|
||||
bool is_server = netplay->is_server;
|
||||
|
||||
mem_info.id = RETRO_MEMORY_SAVE_RAM;
|
||||
|
||||
core_get_memory(&mem_info);
|
||||
content_get_crc(&content_crc_ptr);
|
||||
|
||||
local_pmagic = netplay_platform_magic();
|
||||
|
||||
header[0] = htonl(*content_crc_ptr);
|
||||
header[1] = htonl(netplay_impl_magic());
|
||||
header[2] = htonl(mem_info.size);
|
||||
header[3] = htonl(local_pmagic);
|
||||
|
||||
if (!socket_send_all_blocking(netplay->fd, header, sizeof(header), false))
|
||||
return false;
|
||||
@ -176,62 +213,116 @@ bool netplay_send_info(netplay_t *netplay)
|
||||
RARCH_WARN("Content SRAM sizes do not correspond.\n");
|
||||
}
|
||||
|
||||
if (!netplay_send_nickname(netplay, netplay->fd))
|
||||
/* We only care about platform magic if our core is quirky */
|
||||
remote_pmagic = ntohl(header[3]);
|
||||
if ((netplay->quirks & NETPLAY_QUIRK_ENDIAN_DEPENDENT) &&
|
||||
netplay_endian_mismatch(local_pmagic, remote_pmagic))
|
||||
{
|
||||
RARCH_ERR("%s\n",
|
||||
msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_HOST));
|
||||
RARCH_ERR("Endianness mismatch with an endian-sensitive core.\n");
|
||||
return false;
|
||||
}
|
||||
if ((netplay->quirks & NETPLAY_QUIRK_PLATFORM_DEPENDENT) &&
|
||||
(local_pmagic != remote_pmagic))
|
||||
{
|
||||
RARCH_ERR("Platform mismatch with a platform-sensitive core.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get SRAM data from User 1. */
|
||||
if (sram_size != 0 && sram_size == remote_sram_size)
|
||||
/* Client sends nickname first, server replies with nickname */
|
||||
if (!is_server)
|
||||
{
|
||||
sram = mem_info.data;
|
||||
|
||||
if (!socket_receive_all_blocking(netplay->fd, sram, sram_size))
|
||||
if (!netplay_send_nickname(netplay, netplay->fd))
|
||||
{
|
||||
RARCH_ERR("%s\n",
|
||||
msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST));
|
||||
msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_HOST));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!netplay_get_nickname(netplay, netplay->fd))
|
||||
{
|
||||
if (is_server)
|
||||
RARCH_ERR("%s\n",
|
||||
msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT));
|
||||
else
|
||||
RARCH_ERR("%s\n",
|
||||
msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME_FROM_HOST));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_server)
|
||||
{
|
||||
if (!netplay_send_nickname(netplay, netplay->fd))
|
||||
{
|
||||
RARCH_ERR("%s\n",
|
||||
msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_CLIENT));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Server sends SRAM, client receives */
|
||||
if (is_server)
|
||||
{
|
||||
/* Send SRAM data to the client */
|
||||
sram = mem_info.data;
|
||||
|
||||
if (!socket_send_all_blocking(netplay->fd, sram, sram_size, false))
|
||||
{
|
||||
RARCH_ERR("%s\n",
|
||||
msg_hash_to_str(MSG_FAILED_TO_SEND_SRAM_DATA_TO_CLIENT));
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else if (remote_sram_size != 0)
|
||||
else
|
||||
{
|
||||
/* We can't load this, but we still need to get rid of the data */
|
||||
uint32_t quickbuf;
|
||||
while (remote_sram_size > 0)
|
||||
/* Get SRAM data from User 1. */
|
||||
if (sram_size != 0 && sram_size == remote_sram_size)
|
||||
{
|
||||
if (!socket_receive_all_blocking(netplay->fd, &quickbuf, (remote_sram_size > sizeof(uint32_t)) ? sizeof(uint32_t) : remote_sram_size))
|
||||
sram = mem_info.data;
|
||||
|
||||
if (!socket_receive_all_blocking(netplay->fd, sram, sram_size))
|
||||
{
|
||||
RARCH_ERR("%s\n",
|
||||
msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST));
|
||||
return false;
|
||||
}
|
||||
if (remote_sram_size > sizeof(uint32_t))
|
||||
remote_sram_size -= sizeof(uint32_t);
|
||||
else
|
||||
remote_sram_size = 0;
|
||||
|
||||
}
|
||||
else if (remote_sram_size != 0)
|
||||
{
|
||||
/* We can't load this, but we still need to get rid of the data */
|
||||
uint32_t quickbuf;
|
||||
while (remote_sram_size > 0)
|
||||
{
|
||||
if (!socket_receive_all_blocking(netplay->fd, &quickbuf, (remote_sram_size > sizeof(uint32_t)) ? sizeof(uint32_t) : remote_sram_size))
|
||||
{
|
||||
RARCH_ERR("%s\n",
|
||||
msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST));
|
||||
return false;
|
||||
}
|
||||
if (remote_sram_size > sizeof(uint32_t))
|
||||
remote_sram_size -= sizeof(uint32_t);
|
||||
else
|
||||
remote_sram_size = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!netplay_get_nickname(netplay, netplay->fd))
|
||||
{
|
||||
RARCH_ERR("%s\n",
|
||||
msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME_FROM_HOST));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Reset our frame count so it's consistent with the server */
|
||||
netplay->self_frame_count = netplay->read_frame_count = netplay->other_frame_count = 0;
|
||||
netplay->self_frame_count = netplay->other_frame_count = 0;
|
||||
netplay->read_frame_count = 1;
|
||||
for (i = 0; i < netplay->buffer_size; i++)
|
||||
{
|
||||
netplay->buffer[i].used = false;
|
||||
if (i == netplay->self_ptr)
|
||||
{
|
||||
netplay_delta_frame_ready(netplay, &netplay->buffer[i], 0);
|
||||
netplay->read_ptr = netplay->other_ptr = i;
|
||||
netplay->buffer[i].have_remote = true;
|
||||
netplay->other_ptr = i;
|
||||
netplay->read_ptr = NEXT_PTR(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -239,108 +330,19 @@ bool netplay_send_info(netplay_t *netplay)
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(msg, sizeof(msg), "%s: \"%s\"",
|
||||
msg_hash_to_str(MSG_CONNECTED_TO),
|
||||
netplay->other_nick);
|
||||
RARCH_LOG("%s\n", msg);
|
||||
runloop_msg_queue_push(msg, 1, 180, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool netplay_get_info(netplay_t *netplay)
|
||||
{
|
||||
unsigned sram_size, remote_sram_size;
|
||||
uint32_t header[3];
|
||||
retro_ctx_memory_info_t mem_info;
|
||||
uint32_t *content_crc_ptr = NULL;
|
||||
const void *sram = NULL;
|
||||
size_t i;
|
||||
|
||||
/* FIXME: There's a huge amount of duplication between send_info and
|
||||
* get_info */
|
||||
|
||||
mem_info.id = RETRO_MEMORY_SAVE_RAM;
|
||||
|
||||
core_get_memory(&mem_info);
|
||||
content_get_crc(&content_crc_ptr);
|
||||
|
||||
header[0] = htonl(*content_crc_ptr);
|
||||
header[1] = htonl(netplay_impl_magic());
|
||||
header[2] = htonl(mem_info.size);
|
||||
|
||||
if (!socket_send_all_blocking(netplay->fd, header, sizeof(header), false))
|
||||
return false;
|
||||
|
||||
if (!socket_receive_all_blocking(netplay->fd, header, sizeof(header)))
|
||||
if (is_server)
|
||||
{
|
||||
RARCH_ERR("%s\n",
|
||||
msg_hash_to_str(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT));
|
||||
return false;
|
||||
netplay_log_connection(&netplay->other_addr, 0, netplay->other_nick);
|
||||
}
|
||||
|
||||
if (*content_crc_ptr != ntohl(header[0]))
|
||||
else
|
||||
{
|
||||
RARCH_ERR("%s\n", msg_hash_to_str(MSG_CONTENT_CRC32S_DIFFER));
|
||||
return false;
|
||||
snprintf(msg, sizeof(msg), "%s: \"%s\"",
|
||||
msg_hash_to_str(MSG_CONNECTED_TO),
|
||||
netplay->other_nick);
|
||||
RARCH_LOG("%s\n", msg);
|
||||
runloop_msg_queue_push(msg, 1, 180, false);
|
||||
}
|
||||
|
||||
if (netplay_impl_magic() != ntohl(header[1]))
|
||||
{
|
||||
RARCH_ERR("Implementations differ, make sure you're using exact same "
|
||||
"libretro implementations and RetroArch version.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
sram_size = mem_info.size;
|
||||
remote_sram_size = ntohl(header[2]);
|
||||
if (sram_size != 0 && remote_sram_size != 0 && sram_size != remote_sram_size)
|
||||
{
|
||||
RARCH_WARN("Content SRAM sizes do not correspond.\n");
|
||||
}
|
||||
|
||||
if (!netplay_get_nickname(netplay, netplay->fd))
|
||||
{
|
||||
RARCH_ERR("%s\n",
|
||||
msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Send SRAM data to our User 2. */
|
||||
sram = mem_info.data;
|
||||
|
||||
if (!socket_send_all_blocking(netplay->fd, sram, sram_size, false))
|
||||
{
|
||||
RARCH_ERR("%s\n",
|
||||
msg_hash_to_str(MSG_FAILED_TO_SEND_SRAM_DATA_TO_CLIENT));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!netplay_send_nickname(netplay, netplay->fd))
|
||||
{
|
||||
RARCH_ERR("%s\n",
|
||||
msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_CLIENT));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Reset our frame count so it's consistent with the client */
|
||||
netplay->self_frame_count = netplay->read_frame_count = netplay->other_frame_count = 0;
|
||||
for (i = 0; i < netplay->buffer_size; i++)
|
||||
{
|
||||
netplay->buffer[i].used = false;
|
||||
if (i == netplay->self_ptr)
|
||||
{
|
||||
netplay_delta_frame_ready(netplay, &netplay->buffer[i], 0);
|
||||
netplay->read_ptr = netplay->other_ptr = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
netplay->buffer[i].used = false;
|
||||
}
|
||||
}
|
||||
|
||||
netplay_log_connection(&netplay->other_addr, 0, netplay->other_nick);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ static bool netplay_net_pre_frame(netplay_t *netplay)
|
||||
serial_info.size = netplay->state_size;
|
||||
|
||||
memset(serial_info.data, 0, serial_info.size);
|
||||
if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION)
|
||||
if ((netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) || netplay->self_frame_count == 0)
|
||||
{
|
||||
/* Don't serialize until it's safe */
|
||||
}
|
||||
@ -105,6 +105,7 @@ static bool netplay_net_pre_frame(netplay_t *netplay)
|
||||
|
||||
/* If we can't transmit savestates, we must stall until the client is ready */
|
||||
if (!netplay->has_connection &&
|
||||
netplay->self_frame_count > 0 &&
|
||||
(netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION)))
|
||||
netplay->stall = RARCH_NETPLAY_STALL_NO_CONNECTION;
|
||||
}
|
||||
@ -148,14 +149,23 @@ static bool netplay_net_pre_frame(netplay_t *netplay)
|
||||
RARCH_WARN("Cannot set Netplay port to close-on-exec. It may fail to reopen if the client disconnects.\n");
|
||||
#endif
|
||||
|
||||
/* Connection header */
|
||||
if (netplay_get_info(netplay))
|
||||
/* Establish the connection */
|
||||
if (netplay_handshake(netplay))
|
||||
{
|
||||
netplay->has_connection = true;
|
||||
|
||||
/* Send them the savestate */
|
||||
if (!(netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION)))
|
||||
netplay_load_savestate(netplay, NULL, true);
|
||||
{
|
||||
netplay->force_send_savestate = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Because the first frame isn't serialized, we're actually at
|
||||
* frame 1 */
|
||||
netplay->self_ptr = NEXT_PTR(netplay->self_ptr);
|
||||
netplay->self_frame_count = 1;
|
||||
}
|
||||
|
||||
/* And expect the current frame from the other side */
|
||||
netplay->read_frame_count = netplay->other_frame_count = netplay->self_frame_count;
|
||||
@ -322,7 +332,7 @@ static bool netplay_net_info_cb(netplay_t* netplay, unsigned frames)
|
||||
{
|
||||
if (!netplay_is_server(netplay))
|
||||
{
|
||||
if (!netplay_send_info(netplay))
|
||||
if (!netplay_handshake(netplay))
|
||||
return false;
|
||||
netplay->has_connection = true;
|
||||
}
|
||||
|
@ -35,7 +35,7 @@
|
||||
#define MAX_SPECTATORS 16
|
||||
#define RARCH_DEFAULT_PORT 55435
|
||||
|
||||
#define NETPLAY_PROTOCOL_VERSION 1
|
||||
#define NETPLAY_PROTOCOL_VERSION 2
|
||||
|
||||
#define PREV_PTR(x) ((x) == 0 ? netplay->buffer_size - 1 : (x) - 1)
|
||||
#define NEXT_PTR(x) ((x + 1) % netplay->buffer_size)
|
||||
@ -45,6 +45,8 @@
|
||||
#define NETPLAY_QUIRK_NO_SAVESTATES (1<<0)
|
||||
#define NETPLAY_QUIRK_NO_TRANSMISSION (1<<1)
|
||||
#define NETPLAY_QUIRK_INITIALIZATION (1<<2)
|
||||
#define NETPLAY_QUIRK_ENDIAN_DEPENDENT (1<<3)
|
||||
#define NETPLAY_QUIRK_PLATFORM_DEPENDENT (1<<4)
|
||||
|
||||
/* Mapping of serialization quirks to netplay quirks. */
|
||||
#define NETPLAY_QUIRK_MAP_UNDERSTOOD \
|
||||
@ -56,11 +58,13 @@
|
||||
#define NETPLAY_QUIRK_MAP_NO_SAVESTATES \
|
||||
(RETRO_SERIALIZATION_QUIRK_INCOMPLETE)
|
||||
#define NETPLAY_QUIRK_MAP_NO_TRANSMISSION \
|
||||
(RETRO_SERIALIZATION_QUIRK_SINGLE_SESSION \
|
||||
|RETRO_SERIALIZATION_QUIRK_ENDIAN_DEPENDENT \
|
||||
|RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT)
|
||||
(RETRO_SERIALIZATION_QUIRK_SINGLE_SESSION)
|
||||
#define NETPLAY_QUIRK_MAP_INITIALIZATION \
|
||||
(RETRO_SERIALIZATION_QUIRK_MUST_INITIALIZE)
|
||||
#define NETPLAY_QUIRK_MAP_ENDIAN_DEPENDENT \
|
||||
(RETRO_SERIALIZATION_QUIRK_ENDIAN_DEPENDENT)
|
||||
#define NETPLAY_QUIRK_MAP_PLATFORM_DEPENDENT \
|
||||
(RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT)
|
||||
|
||||
struct delta_frame
|
||||
{
|
||||
@ -214,14 +218,10 @@ bool netplay_get_nickname(netplay_t *netplay, int fd);
|
||||
|
||||
bool netplay_send_nickname(netplay_t *netplay, int fd);
|
||||
|
||||
bool netplay_send_info(netplay_t *netplay);
|
||||
bool netplay_handshake(netplay_t *netplay);
|
||||
|
||||
uint32_t netplay_impl_magic(void);
|
||||
|
||||
bool netplay_send_info(netplay_t *netplay);
|
||||
|
||||
bool netplay_get_info(netplay_t *netplay);
|
||||
|
||||
bool netplay_is_server(netplay_t* netplay);
|
||||
|
||||
bool netplay_is_spectate(netplay_t* netplay);
|
||||
|
Loading…
Reference in New Issue
Block a user