709 lines
17 KiB
C
709 lines
17 KiB
C
|
|
#include "network.h"
|
|
#include "announce.h"
|
|
#include "bomber.h"
|
|
#include "draw.h"
|
|
#include "game.h"
|
|
#include "menu.h"
|
|
#include "utils.h"
|
|
|
|
#define MAXMSG 4096
|
|
|
|
int udpsocket;
|
|
const unsigned char gameversion[4] = {0xda, 0x01, 0x00, 0x09};
|
|
|
|
struct netnode netnodes[MAXNETNODES];
|
|
|
|
static int informsize;
|
|
static unsigned char regpacket[64];
|
|
|
|
static struct sockaddr_in myname = {0}, mastername = {0};
|
|
static socklen_t senderlength;
|
|
static struct sockaddr_in sender = {0};
|
|
|
|
static unsigned char mesg[MAXMSG] = "";
|
|
uchar needwhole = 0;
|
|
int mydatacount;
|
|
int myslot;
|
|
network_type network = NETWORK_NONE;
|
|
|
|
int actioncount;
|
|
unsigned char actionblock[ACTIONHIST * MAXNETNODES];
|
|
|
|
int myaction;
|
|
unsigned char actions[MAXNETNODES];
|
|
|
|
unsigned char latestactions[MAXNETNODES];
|
|
long latestcounts[MAXNETNODES];
|
|
|
|
enum network_packet_types {
|
|
PKT_ACK, /* perfect copy of packet received */
|
|
/* join / host game */
|
|
/* slave -> master packets */
|
|
PKT_JOIN, /* 4 bytes version #, 4 bytes joinunique #, 16 bytes name */
|
|
PKT_QUIT, /* 4 bytes unique # */
|
|
/* master -> slave packets */
|
|
PKT_INVITE, /* 4 bytes unique #, 1 byte your slot (0xff for kick, no data following it) #, any # of 1:slot,16:name sets (-1 end) */
|
|
PKT_BEGIN, /* clone of INVITE */
|
|
PKT_CONFIG, /* 4 bytes unique #, config */
|
|
PKT_ACCEPT, /* 4 bytes join unique #, 132 bytes seed + unique #, config, slot info (see invite) */
|
|
PKT_REJECT, /* 4 bytes join unique #, 4 bytes version #, 1: reason */
|
|
/* ingame actions */
|
|
/* slave -> master packets */
|
|
PKT_MYDATA, /* 4 bytes unique #,4 bytes frame #, 1 byte data */
|
|
/* master -> slave packets */
|
|
PKT_STEP, /* 4 bytes unique #, 4 bytes frame #, history x MAXNETNODES bytes ACT_* */
|
|
|
|
PKT_INVALID = 0xff
|
|
};
|
|
|
|
enum reject_reason {
|
|
REJECT_FULL,
|
|
REJECT_VERSION
|
|
/* TODO: password? */
|
|
};
|
|
|
|
#define _REJECT_LAST REJECT_VERSION
|
|
|
|
const char* reject_reason_str[] = {"Server full", "Version mismatch"};
|
|
|
|
/* all bytes stored MSB first */
|
|
|
|
/*
|
|
game startup:
|
|
<master and matcher>
|
|
Master: send REGISTER to matcher with optional password, wait for ack. If
|
|
timout, resend.
|
|
matcher: Wait for REGISTER packet, when received maintain database. respond
|
|
to sender with ACK. REGISTER packet can close a game also. The REGISTER
|
|
packet sent by the master has a unique word to be used to avoid confusion.
|
|
REGISTER packet also contains a game version #
|
|
|
|
After master registers game and receives ACK, just waits for slaves to contact.
|
|
|
|
<slave and matcher>
|
|
slave: send QUERY to matcher with optional password, wait for INFO, if timeout,
|
|
resend.
|
|
matcher: respond to QUERY with INFO packet. matcher need not maintain any
|
|
database for slave requests. INFO packet contains IP addr and port for each
|
|
master machine that matches the QUERY spec (ALL or password). Only a
|
|
certain MAX # of entries are sent if there are too many to choose from.
|
|
|
|
<slave and master>
|
|
slave: send JOIN to master, wait for INVITE. If timeout, resend. JOIN packet
|
|
contains the unique word the master created. JOIN also contains username.
|
|
master: Respond to JOIN with INVITE. INVITE contains unique word from JOIN
|
|
packet. INVITE either contains NO meaning game no longer exists or is closed
|
|
or player is not invited. IF yes, INVITE contains info on other players
|
|
already in the game (username and slot # for each). Master allocates the
|
|
slots and avoids confusion based on IP addr and port #. INVITE also contains
|
|
game options structure. Whenever a new player JOINS and is admitted, master
|
|
sends updated INVITE packets to everyone already in the JOIN list. Whenever
|
|
master changes game options, master sends out another set of INVITES
|
|
|
|
Duplicate JOINS are answered with updated INVITE but nothing changes as far
|
|
as allocation.
|
|
|
|
Master player launches game after he's satisfied everyone has joined.
|
|
|
|
Master sends BEGIN packet to everyone. BEGIN is identical to INVITE except
|
|
that the data is final. Slave must respond with its first MYDATA packet with
|
|
frame # of 0. If master times out waiting, master sends duplicate BEGIN to
|
|
wayward slaves. Once master has received MYDATA from everyone, game starts.
|
|
|
|
Within game slave sends MYDATA to master and waits for STEP packet. If
|
|
timeout, slave sends duplicate MYDATA.
|
|
|
|
If master times out waiting for a slave's MYDATA, slave gets dropped. MYDATAs
|
|
received will be answered with PKT_QUIT.
|
|
|
|
*/
|
|
|
|
|
|
/* Network I/O, building/checking packets */
|
|
|
|
#if defined(TEST_LATENCY)
|
|
#define NUMQ 512
|
|
struct message {
|
|
int time;
|
|
struct sockaddr_in* to;
|
|
int tosize;
|
|
unsigned char msg[512];
|
|
int len;
|
|
} message[NUMQ] = {0};
|
|
|
|
|
|
outmsgs() {
|
|
int i;
|
|
|
|
for (i = 0; i < NUMQ; ++i) {
|
|
if (message[i].time) {
|
|
--message[i].time;
|
|
if (message[i].time) continue;
|
|
sendto(udpsocket, message[i].msg, message[i].len, 0, message[i].to, sizeof(struct sockaddr_in));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static int putmsg(struct sockaddr_in* toname, unsigned char* msg, int len) {
|
|
int status;
|
|
|
|
#if defined(TEST_LATENCY)
|
|
int i;
|
|
|
|
for (i = 0; i < NUMQ; ++i) {
|
|
if (!message[i].time) {
|
|
message[i].time = 10;
|
|
message[i].to = toname;
|
|
memcpy(message[i].msg, msg, len);
|
|
message[i].len = len;
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
#else
|
|
status = sendto(udpsocket, msg, len, 0, (struct sockaddr*) toname, sizeof(struct sockaddr_in));
|
|
return status;
|
|
#endif
|
|
}
|
|
|
|
static int getmsg(int msec) {
|
|
int size;
|
|
|
|
memset(&sender, 0, sizeof(sender));
|
|
senderlength = sizeof(sender);
|
|
if (msec) {
|
|
struct timeval timeout;
|
|
fd_set readfds;
|
|
int res;
|
|
|
|
memset(&timeout, 0, sizeof(timeout));
|
|
timeout.tv_sec = msec / 1000;
|
|
timeout.tv_usec = (msec % 1000) * 1000;
|
|
FD_ZERO(&readfds);
|
|
FD_SET(udpsocket, &readfds);
|
|
res = select(udpsocket + 1, &readfds, 0, 0, &timeout);
|
|
if (res <= 0) return -1;
|
|
}
|
|
size = recvfrom(udpsocket, mesg, MAXMSG, 0, (struct sockaddr*) &sender, &senderlength);
|
|
return size;
|
|
}
|
|
|
|
static int isvalidunique(unsigned char* p) {
|
|
return 0 == memcmp(p, &network_unique, 4);
|
|
}
|
|
|
|
static int isvalidversion(unsigned char* p) {
|
|
return 0 == memcmp(p, gameversion, 4);
|
|
}
|
|
|
|
static unsigned char* writeuint32(unsigned char* p, Uint32 i) {
|
|
p[0] = i >> 24L;
|
|
p[1] = i >> 16L;
|
|
p[2] = i >> 8L;
|
|
p[3] = i;
|
|
return p + 4;
|
|
}
|
|
|
|
static Uint32 readuint32(unsigned char* p) {
|
|
return (p[0] << 24L) | (p[1] << 16L) | (p[2] << 8) | p[3];
|
|
}
|
|
|
|
static unsigned char* write_unique(unsigned char* p) {
|
|
memcpy(p, &network_unique, 4);
|
|
return p + 4;
|
|
}
|
|
|
|
static unsigned char* write_version(unsigned char* p) {
|
|
memcpy(p, &gameversion, 4);
|
|
return p + 4;
|
|
}
|
|
|
|
static int isvalidmsg_from_slave() {
|
|
int i;
|
|
void* host;
|
|
void* port;
|
|
|
|
if (!isvalidunique(mesg + 1)) return -1;
|
|
host = &sender.sin_addr.s_addr;
|
|
port = &sender.sin_port;
|
|
for (i = 1; i < MAXNETNODES; ++i)
|
|
if (netnodes[i].used && !memcmp(&netnodes[i].netname.sin_addr.s_addr, host, 4) && !memcmp(&netnodes[i].netname.sin_port, port, 2)) return i;
|
|
return -1;
|
|
}
|
|
|
|
static int isvalidmsg_from_master() {
|
|
if (sender.sin_family != mastername.sin_family || sender.sin_addr.s_addr != mastername.sin_addr.s_addr || sender.sin_port != mastername.sin_port) return 0;
|
|
return 1;
|
|
}
|
|
|
|
/* Handling game actions */
|
|
|
|
static void addactions(void) {
|
|
memmove(actionblock + MAXNETNODES, actionblock, (ACTIONHIST - 1) * MAXNETNODES);
|
|
memcpy(actionblock, actions, MAXNETNODES);
|
|
++actioncount;
|
|
}
|
|
|
|
static void sendactions(int which) {
|
|
unsigned char msg[512];
|
|
|
|
msg[0] = PKT_STEP;
|
|
write_unique(msg + 1);
|
|
writeuint32(msg + 5, actioncount);
|
|
memcpy(msg + 9, actionblock, MAXNETNODES * ACTIONHIST);
|
|
putmsg(&netnodes[which].netname, msg, MAXNETNODES * ACTIONHIST + 9);
|
|
}
|
|
|
|
static void sendmine(int frame) {
|
|
unsigned char msg[64];
|
|
|
|
msg[0] = PKT_MYDATA;
|
|
write_unique(msg + 1);
|
|
writeuint32(msg + 5, frame);
|
|
msg[9] = myaction;
|
|
putmsg(&mastername, msg, 10);
|
|
}
|
|
|
|
int networktraffic(void) {
|
|
int i;
|
|
int length;
|
|
int whosent;
|
|
long now;
|
|
long count;
|
|
|
|
switch (network) {
|
|
case NETWORK_NONE:
|
|
return -1;
|
|
case NETWORK_MASTER:
|
|
memcpy(actions, latestactions, MAXNETNODES);
|
|
actions[0] = myaction;
|
|
if (myaction == ACT_QUIT) {
|
|
for (i = 1; i < MAXNETNODES; ++i) {
|
|
if (netnodes[i].used) actions[i] = ACT_QUIT;
|
|
}
|
|
} else {
|
|
for (i = 1; i < MAXNETNODES; ++i) {
|
|
if (netnodes[i].used) actions[i] &= ACT_MASK; /* only keep direction */
|
|
}
|
|
now = gtime();
|
|
for (;;) {
|
|
if (gtime() - now > 15) break;
|
|
length = getmsg(5);
|
|
if (length > 0 && *mesg != PKT_MYDATA) fprintf(stderr, "Strange packet %d\n", (int) *mesg);
|
|
/* check for unexpected old packets...
|
|
* for example JOIN on frame 0, respond with BEGIN if player already in game
|
|
* respond with uninvite INVITE on JOIN from others
|
|
*/
|
|
if (length < 10) continue;
|
|
whosent = isvalidmsg_from_slave();
|
|
if (whosent <= 0) continue;
|
|
count = readuint32(mesg + 5);
|
|
if (count > latestcounts[whosent]) {
|
|
latestcounts[whosent] = count;
|
|
actions[whosent] = (actions[whosent] & ~ACT_MASK) | mesg[9]; /* don't drop "action" keys */
|
|
}
|
|
}
|
|
}
|
|
|
|
addactions(); /* update action history block */
|
|
|
|
for (i = 1; i < MAXNETNODES; ++i) {
|
|
if (netnodes[i].used) {
|
|
sendactions(i); /* send actions to every active node */
|
|
if (actions[i] == ACT_QUIT) netnodes[i].used = 0; /* remove disconnected clients */
|
|
}
|
|
}
|
|
return actioncount;
|
|
case NETWORK_SLAVE:
|
|
count = -1; /* set to actioncount if we got at least one packet */
|
|
now = gtime();
|
|
|
|
++mydatacount;
|
|
sendmine(mydatacount);
|
|
|
|
for (;;) {
|
|
/* if we got already one packet we only wait 3msec, otherwise 30msec */
|
|
long cur = gtime();
|
|
if (count >= 0 && cur - now > 3) break;
|
|
if (exitflag || cur - now > 30) break;
|
|
|
|
length = getmsg(count >= 0 ? 3 : 20);
|
|
|
|
if (MAXNETNODES * ACTIONHIST + 9 != length) continue;
|
|
if (!isvalidmsg_from_master()) continue;
|
|
|
|
i = readuint32(mesg + 5);
|
|
if (i < actioncount) continue;
|
|
count = actioncount = i;
|
|
memcpy(actionblock, mesg + 9, MAXNETNODES * ACTIONHIST);
|
|
}
|
|
return actioncount;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* Handling socket init */
|
|
|
|
int winsock = 0;
|
|
|
|
void getsocket(void) {
|
|
int status;
|
|
socklen_t slen = sizeof(myname);
|
|
#if defined(__WIN32__) || defined(WIN32)
|
|
char dummydata[128];
|
|
|
|
if (WSAStartup(0x0101, (void*) dummydata)) {
|
|
printf("Windows dumped\n");
|
|
exit(1);
|
|
}
|
|
winsock = 1;
|
|
#endif
|
|
|
|
udpsocket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
if (udpsocket == -1) {
|
|
perror("socket()");
|
|
exit(1);
|
|
}
|
|
memset(&myname, 0, sizeof(myname));
|
|
myname.sin_family = AF_INET;
|
|
myname.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
myname.sin_port = htons(0);
|
|
status = bind(udpsocket, (struct sockaddr*) &myname, sizeof(myname));
|
|
if (-1 == status) {
|
|
perror("bind()");
|
|
exit(1);
|
|
}
|
|
status = getsockname(udpsocket, (struct sockaddr*) &myname, &slen);
|
|
if (-1 == status) {
|
|
perror("getsockname()");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void freesocket(void) {
|
|
#if defined(__WIN32__) || defined(WIN32)
|
|
if (!winsock) return;
|
|
WSACleanup();
|
|
#endif
|
|
}
|
|
|
|
/* Join / Host Games */
|
|
|
|
/* Master side */
|
|
|
|
static unsigned char* write_inform(unsigned char* put) {
|
|
int i;
|
|
|
|
*put++ = 0xff; /* slot specific for each slave */
|
|
for (i = 0; i < MAXNETNODES; ++i) {
|
|
if (!netnodes[i].used) continue;
|
|
*put++ = i;
|
|
memmove(put, netnodes[i].name, 16);
|
|
put += 16;
|
|
}
|
|
*put++ = 0xff;
|
|
return put;
|
|
}
|
|
|
|
static void send_inform_all(unsigned char type) {
|
|
int i;
|
|
unsigned char* put = mesg;
|
|
|
|
*put++ = type;
|
|
put = write_unique(put);
|
|
put = write_inform(put);
|
|
informsize = put - mesg;
|
|
|
|
for (i = 1; i < MAXNETNODES; ++i) {
|
|
if (netnodes[i].used) {
|
|
mesg[5] = i;
|
|
putmsg(&netnodes[i].netname, mesg, informsize);
|
|
}
|
|
}
|
|
}
|
|
|
|
static unsigned char* write_config(unsigned char* put) {
|
|
*put++ = configopts.density;
|
|
*put++ = configopts.flames;
|
|
*put++ = configopts.bombs;
|
|
*put++ = configopts.generosity;
|
|
return put;
|
|
}
|
|
|
|
static void build_config() {
|
|
unsigned char* put;
|
|
|
|
put = mesg;
|
|
*put++ = PKT_CONFIG;
|
|
put = write_config(put);
|
|
informsize = put - mesg;
|
|
}
|
|
|
|
static void send_config1(int which) {
|
|
putmsg(&netnodes[which].netname, mesg, informsize);
|
|
}
|
|
|
|
void send_config() {
|
|
int i;
|
|
build_config();
|
|
for (i = 1; i < MAXNETNODES; ++i)
|
|
if (netnodes[i].used) send_config1(i);
|
|
}
|
|
|
|
static void send_reject(struct sockaddr_in* toname, Uint32 network_join_unique, unsigned char reason) {
|
|
mesg[0] = PKT_REJECT;
|
|
memcpy(mesg + 1, &network_join_unique, sizeof(network_join_unique));
|
|
write_version(mesg + 5);
|
|
mesg[9] = reason;
|
|
putmsg(toname, mesg, 10);
|
|
}
|
|
|
|
static void send_accept(Uint32 network_join_unique) {
|
|
unsigned char* put = mesg;
|
|
|
|
*put++ = PKT_ACCEPT;
|
|
memcpy(put, &network_join_unique, sizeof(network_join_unique));
|
|
put += sizeof(network_join_unique);
|
|
put = write_seed_unique(put);
|
|
put = write_config(put);
|
|
put = write_inform(put);
|
|
putmsg(&sender, mesg, put - mesg);
|
|
}
|
|
|
|
int start_network_game() {
|
|
if (!registergame(playername, myname.sin_port, gameversion)) return 0;
|
|
memset(netnodes, 0, sizeof(netnodes));
|
|
netnodes[0].used = 1;
|
|
memmove(netnodes[0].name, playername, 16);
|
|
myslot = 0;
|
|
return 1;
|
|
}
|
|
|
|
int begin_network_game() {
|
|
send_inform_all(PKT_BEGIN);
|
|
network = NETWORK_MASTER;
|
|
/* TODO: wait for ack */
|
|
return 1;
|
|
}
|
|
|
|
void send_invites() {
|
|
send_inform_all(PKT_INVITE);
|
|
}
|
|
|
|
void cancel_network_game() {
|
|
int i;
|
|
|
|
mesg[0] = PKT_INVITE;
|
|
write_unique(mesg + 1);
|
|
mesg[5] = 0xff;
|
|
|
|
for (i = 1; i < MAXNETNODES; ++i) {
|
|
if (netnodes[i].used) { putmsg(&netnodes[i].netname, mesg, 6); }
|
|
}
|
|
}
|
|
|
|
int handle_joins() {
|
|
int size;
|
|
int i, j;
|
|
unsigned char temp[64];
|
|
Uint32 network_join_unique = 0;
|
|
|
|
size = getmsg(40);
|
|
switch (*mesg) {
|
|
case PKT_JOIN:
|
|
if (size < 25) return 0;
|
|
memcpy(&network_join_unique, mesg + 5, sizeof(network_join_unique));
|
|
if (!isvalidversion(mesg + 1)) {
|
|
send_reject(&sender, network_join_unique, REJECT_VERSION);
|
|
return 0;
|
|
}
|
|
break;
|
|
case PKT_QUIT:
|
|
if (size < 5 || !isvalidunique(mesg + 1)) return 0;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
/* Find host in list:
|
|
* i == MAXETNODES: host not found, otherwise the found host
|
|
* only if host not found:
|
|
* j == -1: no free slot, otherwise first free slot
|
|
*/
|
|
j = -1;
|
|
for (i = 1; i < MAXNETNODES; ++i) {
|
|
if (!netnodes[i].used) {
|
|
if (-1 == j) j = i;
|
|
continue; /* don't compare with unused host */
|
|
}
|
|
if (memcmp(&netnodes[i].netname.sin_addr.s_addr, &sender.sin_addr.s_addr, 4)) continue;
|
|
if (memcmp(&netnodes[i].netname.sin_port, &sender.sin_port, 2)) continue;
|
|
/* found host */
|
|
break;
|
|
}
|
|
|
|
switch (*mesg) {
|
|
case PKT_QUIT:
|
|
if (i < MAXNETNODES) /* if host found, reset entry */
|
|
memset(netnodes + i, 0, sizeof(struct netnode));
|
|
/* send always ACK for QUITs */
|
|
*temp = PKT_ACK;
|
|
memmove(temp + 1, mesg, 5);
|
|
putmsg(&sender, temp, 6);
|
|
break;
|
|
case PKT_JOIN:
|
|
if (i == MAXNETNODES && j == -1) { /* reject */
|
|
send_reject(&sender, network_join_unique, REJECT_FULL);
|
|
return 0;
|
|
}
|
|
|
|
if (i == MAXNETNODES) i = j;
|
|
memmove(&netnodes[i].netname.sin_addr.s_addr, &sender.sin_addr.s_addr, 4);
|
|
memmove(&netnodes[i].netname.sin_port, &sender.sin_port, 2);
|
|
netnodes[i].netname.sin_family = AF_INET;
|
|
netnodes[i].used = 1;
|
|
memcpy(netnodes[i].name, mesg + 9, 16);
|
|
netnodes[i].name[15] = '\0';
|
|
|
|
send_accept(network_join_unique);
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* Client side */
|
|
|
|
static int read_inform(unsigned char** pbuf, int* psize) {
|
|
unsigned char* buf = *pbuf;
|
|
int size = *psize;
|
|
int i;
|
|
|
|
if (size < 1) return 0;
|
|
myslot = *buf++;
|
|
size--;
|
|
if (0xff == myslot) return 1;
|
|
|
|
if (size < 1) return 0;
|
|
while (0xff != *buf) {
|
|
i = *buf++;
|
|
size--;
|
|
if (size < 17 || i >= MAXNETNODES) return 0;
|
|
netnodes[i].used = 1;
|
|
memcpy(netnodes[i].name, buf, 16);
|
|
buf += 16;
|
|
size -= 16;
|
|
}
|
|
|
|
*psize = size;
|
|
*pbuf = buf;
|
|
return 1;
|
|
}
|
|
|
|
static void read_config(unsigned char* buf) {
|
|
GameOptions opts;
|
|
memset(&opts, 0, sizeof(opts));
|
|
opts.density = *buf++;
|
|
opts.flames = *buf++;
|
|
opts.bombs = *buf++;
|
|
opts.generosity = *buf++;
|
|
set_game_options(&opts);
|
|
}
|
|
|
|
/* returns 0=ignore packet,1=we're rejected,2=INVITE/CONFIG,3=BEGIN */
|
|
int scaninvite(int msec) {
|
|
int size;
|
|
unsigned char* take;
|
|
|
|
size = getmsg(msec);
|
|
|
|
if (size < 6) return 0;
|
|
if (*mesg != PKT_INVITE && *mesg != PKT_BEGIN && *mesg != PKT_CONFIG) return 0;
|
|
if (!isvalidmsg_from_master()) return 0;
|
|
if (!isvalidunique(mesg + 1)) return 0;
|
|
|
|
take = mesg + 5;
|
|
size -= 5;
|
|
|
|
switch (*mesg) {
|
|
case PKT_INVITE:
|
|
case PKT_BEGIN:
|
|
if (!read_inform(&take, &size)) return 0;
|
|
if (0xff == myslot) return 1; /* master closed game */
|
|
break;
|
|
case PKT_CONFIG:
|
|
if (size < 4) return 0;
|
|
read_config(take);
|
|
break;
|
|
}
|
|
|
|
if (*mesg == PKT_BEGIN) {
|
|
network = NETWORK_SLAVE;
|
|
return 3;
|
|
} else {
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
int send_join(struct sockaddr_in* netname, char playername[16]) {
|
|
int size;
|
|
long now;
|
|
Uint32 join_unique = gtime();
|
|
unsigned char* buf;
|
|
|
|
mastername = *netname;
|
|
*regpacket = PKT_JOIN;
|
|
write_version(regpacket + 1);
|
|
writeuint32(regpacket + 5, join_unique);
|
|
memcpy(regpacket + 9, playername, 16);
|
|
now = longtime();
|
|
putmsg(&mastername, regpacket, 1 + 4 + 4 + 16);
|
|
while (longtime() - now < 3) {
|
|
if (0 == (size = getmsg(1000))) {
|
|
/* got no message, send join again */
|
|
putmsg(&mastername, regpacket, 1 + 4 + 4 + 16);
|
|
continue;
|
|
}
|
|
if (size < 5) continue;
|
|
if (readuint32(mesg + 1) != join_unique) continue;
|
|
switch (*mesg) {
|
|
case PKT_ACCEPT:
|
|
if (size < 1 + 4 + 132 + 4 + 2) continue;
|
|
read_seed_unique(mesg + 5);
|
|
read_config(mesg + 137);
|
|
buf = mesg + 141;
|
|
size -= 141;
|
|
if (!read_inform(&buf, &size)) return 0;
|
|
return 2;
|
|
case PKT_REJECT:
|
|
if (size < 10 || mesg[9] > _REJECT_LAST) {
|
|
failure("Couldn't connect");
|
|
} else {
|
|
failure("Couldn't connect: %s", reject_reason_str[mesg[9]]);
|
|
}
|
|
return 0;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
failure("Could not connect - Timeout");
|
|
return 0;
|
|
}
|
|
|
|
void send_quit() {
|
|
long now;
|
|
int size;
|
|
|
|
*regpacket = PKT_QUIT;
|
|
write_unique(regpacket + 1);
|
|
now = longtime();
|
|
while (longtime() - now < 10) {
|
|
putmsg(&mastername, regpacket, 5);
|
|
size = getmsg(1000);
|
|
if (size < 6) continue;
|
|
if (mesg[0] != PKT_ACK || mesg[1] != PKT_QUIT) continue;
|
|
if (isvalidunique(mesg + 2)) break;
|
|
}
|
|
}
|