Nicknames for regular connection.

This commit is contained in:
Themaister 2012-01-21 18:12:42 +01:00
parent 0a3dc05c27
commit 7082561e21
7 changed files with 149 additions and 28 deletions

View File

@ -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.

View File

@ -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
View File

@ -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)
{

View File

@ -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
View File

@ -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)

View File

@ -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.

View File

@ -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)
{