mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-24 00:20:01 +00:00
Nicknames for regular connection.
This commit is contained in:
parent
0a3dc05c27
commit
7082561e21
@ -177,6 +177,11 @@ Clients can connect and disconnect at any time.
|
||||
Clients thus cannot interact as player 2.
|
||||
For spectating mode to work, both host and clients will need to use this flag.
|
||||
|
||||
.TP
|
||||
\fB--nick NICK\fR
|
||||
Pick a nickname for use with netplay.
|
||||
This is purely cosmetic, and only serves to help players identify each other.
|
||||
|
||||
.TP
|
||||
\fB--ups PATCH, -U PATCH\fR
|
||||
Attempts to apply an UPS patch to the current ROM image. No files are altered.
|
||||
|
@ -333,6 +333,7 @@ struct global
|
||||
bool netplay_is_spectate;
|
||||
unsigned netplay_sync_frames;
|
||||
uint16_t netplay_port;
|
||||
char netplay_nick[32];
|
||||
#endif
|
||||
|
||||
// FFmpeg record.
|
||||
|
18
movie.c
18
movie.c
@ -102,7 +102,7 @@ struct bsv_movie
|
||||
#define BSV_MAGIC 0x42535631
|
||||
|
||||
#define MAGIC_INDEX 0
|
||||
#define SERIALIZER_INDEX 1 // Not current used.
|
||||
#define SERIALIZER_INDEX 1
|
||||
#define CRC_INDEX 2
|
||||
#define STATE_SIZE_INDEX 3
|
||||
|
||||
@ -329,22 +329,23 @@ void bsv_movie_frame_rewind(bsv_movie_t *handle)
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *bsv_header_generate(size_t *size)
|
||||
uint32_t *bsv_header_generate(size_t *size, uint32_t magic)
|
||||
{
|
||||
uint32_t bsv_header[4] = {0};
|
||||
unsigned serialize_size = psnes_serialize_size();
|
||||
size_t header_size = sizeof(bsv_header) + serialize_size;
|
||||
*size = header_size;
|
||||
|
||||
uint8_t *header = (uint8_t*)malloc(header_size);
|
||||
uint32_t *header = (uint32_t*)malloc(header_size);
|
||||
if (!header)
|
||||
return NULL;
|
||||
|
||||
bsv_header[MAGIC_INDEX] = swap_if_little32(BSV_MAGIC);
|
||||
bsv_header[SERIALIZER_INDEX] = swap_if_big32(magic);
|
||||
bsv_header[CRC_INDEX] = swap_if_big32(g_extern.cart_crc);
|
||||
bsv_header[STATE_SIZE_INDEX] = swap_if_big32(serialize_size);
|
||||
|
||||
if (serialize_size && !psnes_serialize(header + sizeof(bsv_header), serialize_size))
|
||||
if (serialize_size && !psnes_serialize((uint8_t*)header + sizeof(bsv_header), serialize_size))
|
||||
{
|
||||
free(header);
|
||||
return NULL;
|
||||
@ -354,7 +355,7 @@ uint8_t *bsv_header_generate(size_t *size)
|
||||
return header;
|
||||
}
|
||||
|
||||
bool bsv_parse_header(const uint32_t *header)
|
||||
bool bsv_parse_header(const uint32_t *header, uint32_t magic)
|
||||
{
|
||||
uint32_t in_bsv = swap_if_little32(header[MAGIC_INDEX]);
|
||||
if (in_bsv != BSV_MAGIC)
|
||||
@ -364,6 +365,13 @@ bool bsv_parse_header(const uint32_t *header)
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t in_magic = swap_if_big32(header[SERIALIZER_INDEX]);
|
||||
if (in_magic != magic)
|
||||
{
|
||||
SSNES_ERR("Magic mismatch, got 0x%x, expected 0x%x!\n", in_magic, magic);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t in_crc = swap_if_big32(header[CRC_INDEX]);
|
||||
if (in_crc != g_extern.cart_crc)
|
||||
{
|
||||
|
4
movie.h
4
movie.h
@ -30,8 +30,8 @@ enum ssnes_movie_type
|
||||
SSNES_MOVIE_RECORD
|
||||
};
|
||||
|
||||
uint8_t *bsv_header_generate(size_t *size);
|
||||
bool bsv_parse_header(const uint32_t *header);
|
||||
uint32_t *bsv_header_generate(size_t *size, uint32_t magic);
|
||||
bool bsv_parse_header(const uint32_t *header, uint32_t magic);
|
||||
|
||||
bsv_movie_t *bsv_movie_init(const char *path, enum ssnes_movie_type type);
|
||||
|
||||
|
137
netplay.c
137
netplay.c
@ -92,6 +92,10 @@ struct delta_frame
|
||||
|
||||
struct netplay
|
||||
{
|
||||
char nick[32];
|
||||
char other_nick[32];
|
||||
struct sockaddr_storage other_addr;
|
||||
|
||||
struct snes_callbacks cbs;
|
||||
int fd; // TCP connection for state sending, etc. Also used for commands.
|
||||
int udp_fd; // UDP connection for game state updates.
|
||||
@ -205,7 +209,8 @@ int16_t input_state_net(bool port, unsigned device, unsigned index, unsigned id)
|
||||
}
|
||||
|
||||
// Custom inet_ntop. Win32 doesn't seem to support this ...
|
||||
static void log_connection(const struct sockaddr_storage *their_addr, unsigned slot)
|
||||
static void log_connection(const struct sockaddr_storage *their_addr,
|
||||
unsigned slot, const char *nick)
|
||||
{
|
||||
union
|
||||
{
|
||||
@ -245,9 +250,9 @@ static void log_connection(const struct sockaddr_storage *their_addr, unsigned s
|
||||
if (str)
|
||||
{
|
||||
char msg[512];
|
||||
snprintf(msg, sizeof(msg), "Got connection from: \"%s\" (#%u)", str, slot);
|
||||
snprintf(msg, sizeof(msg), "Got connection from: \"%s (%s)\" (#%u)", nick, str, slot);
|
||||
msg_queue_push(g_extern.msg_queue, msg, 1, 180);
|
||||
SSNES_LOG("Got connection from: \"%s\" (#%u)\n", str, slot);
|
||||
SSNES_LOG("%s\n", msg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -323,9 +328,9 @@ static bool init_tcp_socket(netplay_t *handle, const char *server, uint16_t port
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sockaddr_storage their_addr;
|
||||
socklen_t addr_size = sizeof(their_addr);
|
||||
int new_fd = accept(handle->fd, (struct sockaddr*)&their_addr, &addr_size);
|
||||
socklen_t addr_size = sizeof(handle->other_addr);
|
||||
int new_fd = accept(handle->fd,
|
||||
(struct sockaddr*)&handle->other_addr, &addr_size);
|
||||
if (new_fd < 0)
|
||||
{
|
||||
SSNES_ERR("Failed to accept socket.\n");
|
||||
@ -336,8 +341,6 @@ static bool init_tcp_socket(netplay_t *handle, const char *server, uint16_t port
|
||||
}
|
||||
close(handle->fd);
|
||||
handle->fd = new_fd;
|
||||
|
||||
log_connection(&their_addr, 0);
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
@ -427,8 +430,11 @@ bool netplay_can_poll(netplay_t *handle)
|
||||
static uint32_t implementation_magic_value(void)
|
||||
{
|
||||
uint32_t res = 0;
|
||||
res |= (psnes_library_revision_major() & 0xf) << 0;
|
||||
res |= (psnes_library_revision_minor() & 0xf) << 4;
|
||||
unsigned major = psnes_library_revision_major();
|
||||
unsigned minor = psnes_library_revision_minor();
|
||||
|
||||
res |= (major & 0xf) << 0;
|
||||
res |= (minor & 0xf) << 4;
|
||||
|
||||
// Shouldn't really use this, but oh well :) It'll do the job.
|
||||
const char *lib = psnes_library_id();
|
||||
@ -436,15 +442,75 @@ static uint32_t implementation_magic_value(void)
|
||||
for (size_t i = 0; i < len; i++)
|
||||
res ^= lib[i] << (i & 0xf);
|
||||
|
||||
const char *ver = PACKAGE_VERSION;
|
||||
len = strlen(ver);
|
||||
for (size_t i = 0; i < len; i++)
|
||||
res ^= ver[i] << ((i & 0xf) + 16);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool send_nickname(netplay_t *handle)
|
||||
{
|
||||
uint8_t nick_size = strlen(handle->nick);
|
||||
|
||||
if (!send_all(handle->fd, &nick_size, sizeof(nick_size)))
|
||||
{
|
||||
SSNES_ERR("Failed to send nick size.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!send_all(handle->fd, handle->nick, nick_size))
|
||||
{
|
||||
SSNES_ERR("Failed to send nick.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool get_nickname(netplay_t *handle)
|
||||
{
|
||||
uint8_t nick_size;
|
||||
|
||||
if (!recv_all(handle->fd, &nick_size, sizeof(nick_size)))
|
||||
{
|
||||
SSNES_ERR("Failed to receive nick size from host.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nick_size >= sizeof(handle->other_nick))
|
||||
{
|
||||
SSNES_ERR("Invalid nick size.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!recv_all(handle->fd, handle->other_nick, nick_size))
|
||||
{
|
||||
SSNES_ERR("Failed to receive nick.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool send_info(netplay_t *handle)
|
||||
{
|
||||
uint32_t header[3] = { htonl(g_extern.cart_crc), htonl(implementation_magic_value()), htonl(psnes_get_memory_size(SNES_MEMORY_CARTRIDGE_RAM)) };
|
||||
uint32_t header[3] = {
|
||||
htonl(g_extern.cart_crc),
|
||||
htonl(implementation_magic_value()),
|
||||
htonl(psnes_get_memory_size(SNES_MEMORY_CARTRIDGE_RAM))
|
||||
};
|
||||
|
||||
if (!send_all(handle->fd, header, sizeof(header)))
|
||||
return false;
|
||||
|
||||
if (!send_nickname(handle))
|
||||
{
|
||||
SSNES_ERR("Failed to send nick to host.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get SRAM data from Player 1 :)
|
||||
uint8_t *sram = psnes_get_memory_data(SNES_MEMORY_CARTRIDGE_RAM);
|
||||
unsigned sram_size = psnes_get_memory_size(SNES_MEMORY_CARTRIDGE_RAM);
|
||||
@ -455,33 +521,54 @@ static bool send_info(netplay_t *handle)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!get_nickname(handle))
|
||||
{
|
||||
SSNES_ERR("Failed to receive nick from host.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
char msg[512];
|
||||
snprintf(msg, sizeof(msg), "Connected to: \"%s\"", handle->other_nick);
|
||||
SSNES_LOG("%s\n", msg);
|
||||
msg_queue_push(g_extern.msg_queue, msg, 1, 180);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool get_info(netplay_t *handle)
|
||||
{
|
||||
uint32_t header[3];
|
||||
|
||||
if (!recv_all(handle->fd, header, sizeof(header)))
|
||||
{
|
||||
SSNES_ERR("Failed to receive header from client.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_extern.cart_crc != ntohl(header[0]))
|
||||
{
|
||||
SSNES_ERR("Cart CRC32s differ! Cannot use different games!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (implementation_magic_value() != ntohl(header[1]))
|
||||
{
|
||||
SSNES_ERR("Implementations differ, make sure you're using exact same libsnes implementations!\n");
|
||||
SSNES_ERR("Implementations differ, make sure you're using exact same libsnes implementations and SSNES version!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (psnes_get_memory_size(SNES_MEMORY_CARTRIDGE_RAM) != ntohl(header[2]))
|
||||
{
|
||||
SSNES_ERR("Cartridge SRAM sizes do not correspond!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!get_nickname(handle))
|
||||
{
|
||||
SSNES_ERR("Failed to get nickname from client.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send SRAM data to our Player 2 :)
|
||||
const uint8_t *sram = psnes_get_memory_data(SNES_MEMORY_CARTRIDGE_RAM);
|
||||
unsigned sram_size = psnes_get_memory_size(SNES_MEMORY_CARTRIDGE_RAM);
|
||||
@ -491,20 +578,29 @@ static bool get_info(netplay_t *handle)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!send_nickname(handle))
|
||||
{
|
||||
SSNES_ERR("Failed to send nickname to client.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
log_connection(&handle->other_addr, 0, handle->other_nick);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool get_info_spectate(netplay_t *handle)
|
||||
{
|
||||
uint32_t header[4];
|
||||
if (recv_all(handle->fd, header, sizeof(header)))
|
||||
|
||||
if (!recv_all(handle->fd, header, sizeof(header)))
|
||||
{
|
||||
SSNES_ERR("Cannot get header from host!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned save_state_size = psnes_serialize_size();
|
||||
if (!bsv_parse_header(header))
|
||||
if (!bsv_parse_header(header, implementation_magic_value()))
|
||||
{
|
||||
SSNES_ERR("Received invalid BSV header from host!\n");
|
||||
return false;
|
||||
@ -543,7 +639,10 @@ static void init_buffers(netplay_t *handle)
|
||||
}
|
||||
}
|
||||
|
||||
netplay_t *netplay_new(const char *server, uint16_t port, unsigned frames, const struct snes_callbacks *cb, bool spectate)
|
||||
netplay_t *netplay_new(const char *server, uint16_t port,
|
||||
unsigned frames, const struct snes_callbacks *cb,
|
||||
bool spectate,
|
||||
const char *nick)
|
||||
{
|
||||
(void)spectate;
|
||||
|
||||
@ -560,6 +659,7 @@ netplay_t *netplay_new(const char *server, uint16_t port, unsigned frames, const
|
||||
handle->port = server ? 0 : 1;
|
||||
handle->spectate = spectate;
|
||||
handle->spectate_client = server != NULL;
|
||||
strlcpy(handle->nick, nick, sizeof(handle->nick));
|
||||
|
||||
if (!init_socket(handle, server, port))
|
||||
{
|
||||
@ -1134,7 +1234,7 @@ static void netplay_pre_frame_spectate(netplay_t *handle)
|
||||
}
|
||||
|
||||
size_t header_size;
|
||||
uint8_t *header = bsv_header_generate(&header_size);
|
||||
uint32_t *header = bsv_header_generate(&header_size, implementation_magic_value());
|
||||
if (!header)
|
||||
{
|
||||
SSNES_ERR("Failed to generate BSV header!\n");
|
||||
@ -1145,8 +1245,7 @@ static void netplay_pre_frame_spectate(netplay_t *handle)
|
||||
int bufsize = header_size;
|
||||
setsockopt(new_fd, SOL_SOCKET, SO_SNDBUF, CONST_CAST &bufsize, sizeof(int));
|
||||
|
||||
const uint8_t *tmp_header = header;
|
||||
if (!send_all(new_fd, tmp_header, header_size))
|
||||
if (!send_all(new_fd, header, header_size))
|
||||
{
|
||||
SSNES_ERR("Failed to send header to client!\n");
|
||||
close(new_fd);
|
||||
@ -1157,7 +1256,7 @@ static void netplay_pre_frame_spectate(netplay_t *handle)
|
||||
free(header);
|
||||
handle->spectate_fds[index] = new_fd;
|
||||
|
||||
log_connection(&their_addr, index);
|
||||
log_connection(&their_addr, index, handle->other_nick);
|
||||
}
|
||||
|
||||
void netplay_pre_frame(netplay_t *handle)
|
||||
|
@ -43,7 +43,8 @@ struct snes_callbacks
|
||||
// Creates a new netplay handle. A NULL host means we're hosting (player 1). :)
|
||||
netplay_t *netplay_new(const char *server,
|
||||
uint16_t port, unsigned frames,
|
||||
const struct snes_callbacks *cb, bool spectate);
|
||||
const struct snes_callbacks *cb, bool spectate,
|
||||
const char *nick);
|
||||
void netplay_free(netplay_t *handle);
|
||||
|
||||
// On regular netplay, flip who controls player 1 and 2.
|
||||
|
9
ssnes.c
9
ssnes.c
@ -495,6 +495,7 @@ static void print_help(void)
|
||||
puts("\t--spectate: Netplay will become spectacting mode.");
|
||||
puts("\t\tHost can live stream the game content to players that connect.");
|
||||
puts("\t\tHowever, the client will not be able to play. Multiple clients can connect to the host.");
|
||||
puts("\t--nick: Picks a nickname for use with netplay. Not mandatory.");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FFMPEG
|
||||
@ -644,6 +645,7 @@ static void parse_input(int argc, char *argv[])
|
||||
{ "frames", 1, NULL, 'F' },
|
||||
{ "port", 1, &val, 'p' },
|
||||
{ "spectate", 0, &val, 'S' },
|
||||
{ "nick", 1, &val, 'N' },
|
||||
#endif
|
||||
{ "ups", 1, NULL, 'U' },
|
||||
{ "bps", 1, &val, 'B' },
|
||||
@ -862,6 +864,10 @@ static void parse_input(int argc, char *argv[])
|
||||
case 'S':
|
||||
g_extern.netplay_is_spectate = true;
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
strlcpy(g_extern.netplay_nick, optarg, sizeof(g_extern.netplay_nick));
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 'B':
|
||||
@ -1229,7 +1235,8 @@ static void init_netplay(void)
|
||||
|
||||
g_extern.netplay = netplay_new(g_extern.netplay_is_client ? g_extern.netplay_server : NULL,
|
||||
g_extern.netplay_port ? g_extern.netplay_port : SSNES_DEFAULT_PORT,
|
||||
g_extern.netplay_sync_frames, &cbs, g_extern.netplay_is_spectate);
|
||||
g_extern.netplay_sync_frames, &cbs, g_extern.netplay_is_spectate,
|
||||
g_extern.netplay_nick);
|
||||
|
||||
if (!g_extern.netplay)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user