revised the proxy a bit. one cluster, one udp socket, multiple streams. Hurrah! I wonder how many bugs I just added.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@1435 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2005-10-07 02:02:15 +00:00
parent cf2a589dbf
commit dad5de099b
7 changed files with 1438 additions and 496 deletions

View File

@ -176,7 +176,6 @@ unsigned char *ReadFile_WINDOWSSUCKS(char *gamedir, char *filename, int *size)
f = FindInPaks("id1", filename, size); f = FindInPaks("id1", filename, size);
if (!f) if (!f)
{ {
printf("Couldn't open bsp file\n");
return NULL; return NULL;
} }
} }
@ -193,7 +192,7 @@ unsigned char *ReadFile_WINDOWSSUCKS(char *gamedir, char *filename, int *size)
return data; return data;
} }
bsp_t *BSP_LoadModel(char *gamedir, char *bspname) bsp_t *BSP_LoadModel(cluster_t *cluster, char *gamedir, char *bspname)
{ {
unsigned char *data; unsigned char *data;
unsigned int size; unsigned int size;
@ -217,7 +216,7 @@ bsp_t *BSP_LoadModel(char *gamedir, char *bspname)
data = ReadFile_WINDOWSSUCKS("id1", bspname, &size); data = ReadFile_WINDOWSSUCKS("id1", bspname, &size);
if (!data) if (!data)
{ {
printf("Couldn't open bsp file \"%s\" (gamedir \"%s\")\n", bspname, gamedir); Sys_Printf(cluster, "Couldn't open bsp file \"%s\" (gamedir \"%s\")\n", bspname, gamedir);
return NULL; return NULL;
} }
} }
@ -226,7 +225,7 @@ bsp_t *BSP_LoadModel(char *gamedir, char *bspname)
header = (dheader_t*)data; header = (dheader_t*)data;
if (data[0] != 29) if (data[0] != 29)
{ {
printf("BSP not version 29\n", bspname, gamedir); Sys_Printf(cluster, "BSP not version 29\n", bspname, gamedir);
return NULL; return NULL;
} }

View File

@ -25,13 +25,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
void NET_SendPacket(SOCKET sock, int length, char *data, netadr_t adr) void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, char *data, netadr_t adr)
{ {
int ret; int ret;
ret = sendto(sock, data, length, 0, (struct sockaddr *)adr, sizeof(struct sockaddr_in)); ret = sendto(sock, data, length, 0, (struct sockaddr *)adr, sizeof(struct sockaddr_in));
if (ret < 0) if (ret < 0)
{ {
printf("udp send error\n"); Sys_Printf(cluster, "udp send error\n");
} }
} }
@ -104,7 +104,7 @@ Netchan_OutOfBand
Sends an out-of-band datagram Sends an out-of-band datagram
================ ================
*/ */
void Netchan_OutOfBand (SOCKET sock, netadr_t adr, int length, unsigned char *data) void Netchan_OutOfBand (cluster_t *cluster, SOCKET sock, netadr_t adr, int length, unsigned char *data)
{ {
netmsg_t send; netmsg_t send;
unsigned char send_buf[MAX_MSGLEN + PACKET_HEADER]; unsigned char send_buf[MAX_MSGLEN + PACKET_HEADER];
@ -116,7 +116,7 @@ void Netchan_OutOfBand (SOCKET sock, netadr_t adr, int length, unsigned char *da
WriteData (&send, data, length); WriteData (&send, data, length);
// send the datagram // send the datagram
NET_SendPacket (sock, send.cursize, send.data, adr); NET_SendPacket (cluster, sock, send.cursize, send.data, adr);
} }
@ -127,7 +127,7 @@ Netchan_OutOfBandPrint
Sends a text message in an out-of-band datagram Sends a text message in an out-of-band datagram
================ ================
*/ */
void Netchan_OutOfBandPrint (SOCKET sock, netadr_t adr, char *format, ...) void Netchan_OutOfBandPrint (cluster_t *cluster, SOCKET sock, netadr_t adr, char *format, ...)
{ {
va_list argptr; va_list argptr;
char string[8192]; char string[8192];
@ -141,7 +141,7 @@ void Netchan_OutOfBandPrint (SOCKET sock, netadr_t adr, char *format, ...)
#endif // _WIN32 #endif // _WIN32
va_end (argptr); va_end (argptr);
Netchan_OutOfBand (sock, adr, strlen(string), (unsigned char *)string); Netchan_OutOfBand (cluster, sock, adr, strlen(string), (unsigned char *)string);
} }
/* /*
@ -225,7 +225,7 @@ transmition / retransmition of the reliable messages.
A 0 length will still generate a packet and deal with the reliable messages. A 0 length will still generate a packet and deal with the reliable messages.
================ ================
*/ */
void Netchan_Transmit (netchan_t *chan, int length, const unsigned char *data) void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const unsigned char *data)
{ {
netmsg_t send; netmsg_t send;
unsigned char send_buf[MAX_MSGLEN + PACKET_HEADER]; unsigned char send_buf[MAX_MSGLEN + PACKET_HEADER];
@ -291,7 +291,7 @@ void Netchan_Transmit (netchan_t *chan, int length, const unsigned char *data)
// chan->outgoing_size[i] = send.cursize; // chan->outgoing_size[i] = send.cursize;
// chan->outgoing_time[i] = curtime; // chan->outgoing_time[i] = curtime;
NET_SendPacket (chan->sock, send.cursize, send.data, chan->remote_address); NET_SendPacket (cluster, chan->sock, send.cursize, send.data, chan->remote_address);
if (chan->cleartime < curtime) if (chan->cleartime < curtime)
chan->cleartime = curtime + send.cursize*chan->rate; chan->cleartime = curtime + send.cursize*chan->rate;

View File

@ -186,8 +186,9 @@ void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playerma
case dem_single: case dem_single:
case dem_stats: case dem_stats:
//check and send to them only if they're tracking this player(s). //check and send to them only if they're tracking this player(s).
for (v = tv->viewers; v; v = v->next) for (v = tv->cluster->viewers; v; v = v->next)
{ {
if (v->server == tv)
if (v->trackplayer>=0) if (v->trackplayer>=0)
if ((1<<v->trackplayer)&playermask) if ((1<<v->trackplayer)&playermask)
SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type
@ -195,8 +196,9 @@ void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playerma
break; break;
default: default:
//send to all //send to all
for (v = tv->viewers; v; v = v->next) for (v = tv->cluster->viewers; v; v = v->next)
{ {
if (v->server == tv)
SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type
} }
break; break;
@ -224,10 +226,10 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma
/*tv->servertime =*/ ReadFloat(m); /*tv->servertime =*/ ReadFloat(m);
ReadString(m, tv->mapname, sizeof(tv->mapname)); ReadString(m, tv->mapname, sizeof(tv->mapname));
printf("Gamedir: %s\n", tv->gamedir); Sys_Printf(tv->cluster, "Gamedir: %s\n", tv->gamedir);
printf("---------------------\n"); Sys_Printf(tv->cluster, "---------------------\n");
printf("%s\n", tv->mapname); Sys_Printf(tv->cluster, "%s\n", tv->mapname);
printf("---------------------\n"); Sys_Printf(tv->cluster, "---------------------\n");
// get the movevars // get the movevars
tv->movevars.gravity = ReadFloat(m); tv->movevars.gravity = ReadFloat(m);
@ -241,8 +243,11 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma
tv->movevars.waterfriction = ReadFloat(m); tv->movevars.waterfriction = ReadFloat(m);
tv->movevars.entgrav = ReadFloat(m); tv->movevars.entgrav = ReadFloat(m);
for (v = tv->viewers; v; v = v->next) for (v = tv->cluster->viewers; v; v = v->next)
{
if (v->server == tv)
v->thinksitsconnected = false; v->thinksitsconnected = false;
}
tv->maxents = 0; //clear these tv->maxents = 0; //clear these
memset(tv->modellist, 0, sizeof(tv->modellist)); memset(tv->modellist, 0, sizeof(tv->modellist));
@ -273,13 +278,16 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
if (!strcmp(text, "skins\n")) if (!strcmp(text, "skins\n"))
{ {
const char newcmd[10] = {svc_stufftext, 'c', 'm', 'd', ' ', 'n','e','w','\n','\0'}; const char newcmd[10] = {svc_stufftext, 'c', 'm', 'd', ' ', 'n','e','w','\n','\0'};
tv->servercount++;
tv->parsingconnectiondata = false; tv->parsingconnectiondata = false;
for (v = tv->viewers; v; v = v->next) for (v = tv->cluster->viewers; v; v = v->next)
{ {
if (v->server == tv)
{
v->servercount++;
SendBufferToViewer(v, newcmd, sizeof(newcmd), true); SendBufferToViewer(v, newcmd, sizeof(newcmd), true);
} }
}
return; return;
} }
else if (!strncmp(text, "fullserverinfo ", 15)) else if (!strncmp(text, "fullserverinfo ", 15))
@ -306,14 +314,14 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
{ //the fromproxy check is because it's fairly common to find a qw server with brackets after it's name. { //the fromproxy check is because it's fairly common to find a qw server with brackets after it's name.
char *s; char *s;
s = strchr(value, '('); //so strip the parent proxy's hostname, and put our hostname first, leaving the origional server's hostname within the brackets s = strchr(value, '('); //so strip the parent proxy's hostname, and put our hostname first, leaving the origional server's hostname within the brackets
snprintf(text, sizeof(text), "%s %s", tv->hostname, s); snprintf(text, sizeof(text), "%s %s", tv->cluster->hostname, s);
} }
else else
{ {
if (tv->file) if (tv->file)
snprintf(text, sizeof(text), "%s (recorded from: %s)", tv->hostname, value); snprintf(text, sizeof(text), "%s (recorded from: %s)", tv->cluster->hostname, value);
else else
snprintf(text, sizeof(text), "%s (live: %s)", tv->hostname, value); snprintf(text, sizeof(text), "%s (live: %s)", tv->cluster->hostname, value);
} }
Info_SetValueForStarKey(tv->serverinfo, "hostname", text, sizeof(tv->serverinfo)); Info_SetValueForStarKey(tv->serverinfo, "hostname", text, sizeof(tv->serverinfo));
@ -325,6 +333,22 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
} }
static void ParseSetInfo(sv_t *tv, netmsg_t *m)
{
int pnum;
char key[64];
char value[256];
pnum = ReadByte(m);
ReadString(m, key, sizeof(key));
ReadString(m, value, sizeof(value));
if (pnum < MAX_CLIENTS)
Info_SetValueForStarKey(tv->players[pnum].userinfo, key, value, sizeof(tv->players[pnum].userinfo));
if (!tv->parsingconnectiondata)
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, dem_all, (unsigned)-1);
}
static void ParseServerinfo(sv_t *tv, netmsg_t *m) static void ParseServerinfo(sv_t *tv, netmsg_t *m)
{ {
char key[64]; char key[64];
@ -366,10 +390,16 @@ static void ParsePrint(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
*t = '['; *t = '[';
if (*t == 17) if (*t == 17)
*t = ']'; *t = ']';
if (*t == 29)
*t = '-';
if (*t == 30)
*t = '-';
if (*t == 31)
*t = '-';
if (*t == '\a') //doh. :D if (*t == '\a') //doh. :D
*t = ' '; *t = ' ';
} }
printf("%s", text); Sys_Printf(tv->cluster, "%s", text);
} }
} }
@ -430,7 +460,7 @@ static void ParseStaticSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
if (tv->staticsound_count == MAX_STATICSOUNDS) if (tv->staticsound_count == MAX_STATICSOUNDS)
{ {
tv->staticsound_count--; // don't be fatal. tv->staticsound_count--; // don't be fatal.
printf("Too many static sounds\n"); Sys_Printf(tv->cluster, "Too many static sounds\n");
} }
tv->staticsound[tv->staticsound_count].origin[0] = ReadShort(m); tv->staticsound[tv->staticsound_count].origin[0] = ReadShort(m);
@ -462,7 +492,7 @@ void ParseSpawnStatic(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
if (tv->spawnstatic_count == MAX_STATICENTITIES) if (tv->spawnstatic_count == MAX_STATICENTITIES)
{ {
tv->spawnstatic_count--; // don't be fatal. tv->spawnstatic_count--; // don't be fatal.
printf("Too many static entities\n"); Sys_Printf(tv->cluster, "Too many static entities\n");
} }
ParseEntityState(&tv->spawnstatic[tv->spawnstatic_count], m); ParseEntityState(&tv->spawnstatic[tv->spawnstatic_count], m);
@ -473,16 +503,28 @@ void ParseSpawnStatic(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
} }
static void ParsePlayerInfo(sv_t *tv, netmsg_t *m) static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers)
{ {
int flags; int flags;
int num; int num;
int i; int i;
if (clearoldplayers)
{
for (i = 0; i < MAX_CLIENTS; i++)
{ //hide players
//they'll be sent after this packet.
tv->players[i].active = false;
}
}
num = ReadByte(m); num = ReadByte(m);
if (num >= MAX_CLIENTS) if (num >= MAX_CLIENTS)
{ {
num = 0; // don't be fatal. num = 0; // don't be fatal.
printf("Too many svc_playerinfos, wrapping\n"); Sys_Printf(tv->cluster, "Too many svc_playerinfos, wrapping\n");
} }
tv->players[num].old = tv->players[num].current; tv->players[num].old = tv->players[num].current;
@ -544,18 +586,13 @@ static void ParsePacketEntities(sv_t *tv, netmsg_t *m)
tv->physicstime = tv->parsetime; tv->physicstime = tv->parsetime;
if (tv->chokeonnotupdated) if (tv->cluster->chokeonnotupdated)
for (v = tv->viewers; v; v = v->next) for (v = tv->cluster->viewers; v; v = v->next)
{ {
if (v->server == tv)
v->chokeme = false; v->chokeme = false;
} }
for (entnum = 0; entnum < MAX_CLIENTS; entnum++)
{ //hide players
//they'll be sent after this packet.
tv->players[entnum].active = false;
}
//luckilly, only updated entities are here, so that keeps cpu time down a bit. //luckilly, only updated entities are here, so that keeps cpu time down a bit.
for (;;) for (;;)
{ {
@ -630,7 +667,7 @@ static void ParseUpdatePing(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
if (pnum < MAX_CLIENTS) if (pnum < MAX_CLIENTS)
tv->players[pnum].ping = ping; tv->players[pnum].ping = ping;
else else
printf("svc_updateping: invalid player number\n"); Sys_Printf(tv->cluster, "svc_updateping: invalid player number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
} }
@ -645,7 +682,7 @@ static void ParseUpdateFrags(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
if (pnum < MAX_CLIENTS) if (pnum < MAX_CLIENTS)
tv->players[pnum].frags = frags; tv->players[pnum].frags = frags;
else else
printf("svc_updatefrags: invalid player number\n"); Sys_Printf(tv->cluster, "svc_updatefrags: invalid player number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
} }
@ -668,7 +705,7 @@ static void ParseUpdateStat(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
} }
} }
else else
printf("svc_updatestat: invalid stat number\n"); Sys_Printf(tv->cluster, "svc_updatestat: invalid stat number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
} }
@ -690,7 +727,7 @@ static void ParseUpdateStatLong(sv_t *tv, netmsg_t *m, int to, unsigned int mask
} }
} }
else else
printf("svc_updatestatlong: invalid stat number\n"); Sys_Printf(tv->cluster, "svc_updatestatlong: invalid stat number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
} }
@ -704,7 +741,7 @@ static void ParseUpdateUserinfo(sv_t *tv, netmsg_t *m)
ReadString(m, tv->players[pnum].userinfo, sizeof(tv->players[pnum].userinfo)); ReadString(m, tv->players[pnum].userinfo, sizeof(tv->players[pnum].userinfo));
else else
{ {
printf("svc_updateuserinfo: invalid player number\n"); Sys_Printf(tv->cluster, "svc_updateuserinfo: invalid player number\n");
while (ReadByte(m)) //suck out the message. while (ReadByte(m)) //suck out the message.
{ {
} }
@ -722,7 +759,7 @@ static void ParsePacketloss(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
if (pnum < MAX_CLIENTS) if (pnum < MAX_CLIENTS)
tv->players[pnum].packetloss = value; tv->players[pnum].packetloss = value;
else else
printf("svc_updatepl: invalid player number\n"); Sys_Printf(tv->cluster, "svc_updatepl: invalid player number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
} }
@ -738,7 +775,7 @@ static void ParseUpdateEnterTime(sv_t *tv, netmsg_t *m, int to, unsigned int mas
if (pnum < MAX_CLIENTS) if (pnum < MAX_CLIENTS)
tv->players[pnum].entertime = value; tv->players[pnum].entertime = value;
else else
printf("svc_updateentertime: invalid player number\n"); Sys_Printf(tv->cluster, "svc_updateentertime: invalid player number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
} }
@ -881,7 +918,7 @@ static void ParseTempEntity(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
ReadShort (m); ReadShort (m);
break; break;
default: default:
printf("temp entity %i not recognised\n", i); Sys_Printf(tv->cluster, "temp entity %i not recognised\n", i);
return; return;
} }
@ -896,7 +933,7 @@ void ParseLightstyle(sv_t *tv, netmsg_t *m)
ReadString(m, tv->lightstyle[style].name, sizeof(tv->lightstyle[style].name)); ReadString(m, tv->lightstyle[style].name, sizeof(tv->lightstyle[style].name));
else else
{ {
printf("svc_lightstyle: invalid lightstyle index (%i)\n", style); Sys_Printf(tv->cluster, "svc_lightstyle: invalid lightstyle index (%i)\n", style);
while (ReadByte(m)) //suck out the message. while (ReadByte(m)) //suck out the message.
{ {
} }
@ -908,6 +945,7 @@ void ParseLightstyle(sv_t *tv, netmsg_t *m)
void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
{ {
netmsg_t buf; netmsg_t buf;
qboolean clearoldplayers = true;
buf.cursize = length; buf.cursize = length;
buf.maxsize = length; buf.maxsize = length;
buf.readpos = 0; buf.readpos = 0;
@ -917,7 +955,7 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
{ {
if (buf.readpos > buf.cursize) if (buf.readpos > buf.cursize)
{ {
printf("Read past end of parse buffer\n"); Sys_Printf(tv->cluster, "Read past end of parse buffer\n");
return; return;
} }
// printf("%i\n", buf.buffer[0]); // printf("%i\n", buf.buffer[0]);
@ -926,10 +964,10 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
{ {
case svc_bad: case svc_bad:
ParseError(&buf); ParseError(&buf);
printf("ParseMessage: svc_bad\n"); Sys_Printf(tv->cluster, "ParseMessage: svc_bad\n");
return; return;
case svc_nop: //quakeworld isn't meant to send these. case svc_nop: //quakeworld isn't meant to send these.
printf("nop\n"); Sys_Printf(tv->cluster, "nop\n");
break; break;
case svc_disconnect: case svc_disconnect:
@ -1054,7 +1092,8 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
//#define svc_download 41 // [short] size [size bytes] //#define svc_download 41 // [short] size [size bytes]
case svc_playerinfo: case svc_playerinfo:
ParsePlayerInfo(tv, &buf); ParsePlayerInfo(tv, &buf, clearoldplayers);
clearoldplayers = false;
break; break;
//#define svc_nails 43 // [qbyte] num [48 bits] xyzpy 12 12 12 4 8 //#define svc_nails 43 // [qbyte] num [48 bits] xyzpy 12 12 12 4 8
case svc_chokecount: case svc_chokecount:
@ -1067,10 +1106,10 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
if (tv->bsp) if (tv->bsp)
BSP_Free(tv->bsp); BSP_Free(tv->bsp);
if (tv->nobsp) if (tv->cluster->nobsp)
tv->bsp = NULL; tv->bsp = NULL;
else else
tv->bsp = BSP_LoadModel(tv->gamedir, tv->modellist[1].name); tv->bsp = BSP_LoadModel(tv->cluster, tv->gamedir, tv->modellist[1].name);
tv->numinlines = 0; tv->numinlines = 0;
for (i = 2; i < 256; i++) for (i = 2; i < 256; i++)
@ -1094,7 +1133,9 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
break; break;
//#define svc_maxspeed 49 // maxspeed change, for prediction //#define svc_maxspeed 49 // maxspeed change, for prediction
//#define svc_entgravity 50 // gravity change, for prediction //#define svc_entgravity 50 // gravity change, for prediction
//#define svc_setinfo 51 // setinfo on a client case svc_setinfo:
ParseSetInfo(tv, &buf);
break;
case svc_serverinfo: case svc_serverinfo:
ParseServerinfo(tv, &buf); ParseServerinfo(tv, &buf);
break; break;
@ -1106,7 +1147,7 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
default: default:
buf.readpos = buf.startpos; buf.readpos = buf.startpos;
printf("Can't handle svc %i\n", (unsigned int)ReadByte(&buf)); Sys_Printf(tv->cluster, "Can't handle svc %i\n", (unsigned int)ReadByte(&buf));
return; return;
} }
} }

View File

@ -27,6 +27,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//this means that when a new proxy connects, we have to send initial state as well as a chunk of pending state, expect to need to send new data before the proxy even has all the init stuff. We may need to raise MAX_PROXY_BUFFER to be larger than on the server //this means that when a new proxy connects, we have to send initial state as well as a chunk of pending state, expect to need to send new data before the proxy even has all the init stuff. We may need to raise MAX_PROXY_BUFFER to be larger than on the server
//how does multiple servers work
//each proxy acts as a cluster of connections to servers
//when a viewer connects, they are given a list of active server connections
//if there's only one server connection, they are given that one automatically.
#ifdef _WIN32 #ifdef _WIN32
#include <conio.h> #include <conio.h>
#include <winsock.h> #include <winsock.h>
@ -42,6 +49,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#endif #endif
#define snprintf _snprintf #define snprintf _snprintf
#define vsnprintf _vsnprintf
#elif defined(__CYGWIN__) #elif defined(__CYGWIN__)
@ -221,6 +229,8 @@ typedef struct {
} packet_entities_t; } packet_entities_t;
#define MAX_BACK_BUFFERS 16 #define MAX_BACK_BUFFERS 16
typedef struct sv_s sv_t;
typedef struct cluster_s cluster_t;
typedef struct viewer_s { typedef struct viewer_s {
qboolean drop; qboolean drop;
netchan_t netchan; netchan_t netchan;
@ -229,6 +239,8 @@ typedef struct viewer_s {
qboolean thinksitsconnected; qboolean thinksitsconnected;
int delta_frame; int delta_frame;
int servercount;
netmsg_t backbuf[MAX_BACK_BUFFERS]; //note data is malloced! netmsg_t backbuf[MAX_BACK_BUFFERS]; //note data is malloced!
int backbuffered; int backbuffered;
@ -245,6 +257,12 @@ typedef struct viewer_s {
int settime; //the time that we last told the client. int settime; //the time that we last told the client.
float origin[3]; float origin[3];
sv_t *server;
int menunum;
int menuop;
int fwdval; //for scrolling up/down the menu using +forward/+back :)
} viewer_t; } viewer_t;
typedef struct oproxy_s { typedef struct oproxy_s {
@ -287,7 +305,6 @@ typedef struct sv_s {
unsigned int parsetime; unsigned int parsetime;
int servercount;
char gamedir[MAX_QPATH]; char gamedir[MAX_QPATH];
char mapname[256]; char mapname[256];
struct { struct {
@ -312,6 +329,7 @@ typedef struct sv_s {
filename_t lightstyle[MAX_LIGHTSTYLES]; filename_t lightstyle[MAX_LIGHTSTYLES];
char serverinfo[MAX_SERVERINFO_STRING]; char serverinfo[MAX_SERVERINFO_STRING];
char hostname[MAX_QPATH];
playerinfo_t players[MAX_CLIENTS]; playerinfo_t players[MAX_CLIENTS];
filename_t modellist[MAX_MODELS]; filename_t modellist[MAX_MODELS];
@ -323,10 +341,7 @@ typedef struct sv_s {
SOCKET sourcesock; SOCKET sourcesock;
SOCKET listenmvd; //tcp + mvd protocol SOCKET listenmvd; //tcp + mvd protocol
SOCKET qwdsocket; //udp + quakeworld protocols
viewer_t *viewers;
int numviewers;
oproxy_t *proxies; oproxy_t *proxies;
int numproxies; int numproxies;
@ -336,35 +351,51 @@ typedef struct sv_s {
unsigned int curtime; unsigned int curtime;
unsigned int oldpackettime; unsigned int oldpackettime;
unsigned int nextpackettime; unsigned int nextpackettime;
unsigned int nextconnectattemp;
int tcplistenportnum; int tcplistenportnum;
int qwlistenportnum;
unsigned int mastersendtime;
unsigned int mastersequence;
char commandinput[512];
int inputlength;
cluster_t *cluster;
sv_t *next; //next proxy->server connection
bsp_t *bsp; bsp_t *bsp;
int numinlines; int numinlines;
//options: //options:
char server[MAX_QPATH];
} sv_t;
typedef struct cluster_s {
SOCKET qwdsocket; //udp + quakeworld protocols
char commandinput[512];
int inputlength;
unsigned int mastersendtime;
unsigned int mastersequence;
unsigned int curtime;
viewer_t *viewers;
int numviewers;
sv_t *servers;
int numservers;
//options
int qwlistenportnum;
char password[256];
char hostname[256];
char master[MAX_QPATH];
qboolean chokeonnotupdated; qboolean chokeonnotupdated;
qboolean lateforward; qboolean lateforward;
qboolean notalking; qboolean notalking;
char password[256];
char hostname[256];
char server[MAX_QPATH];
char master[MAX_QPATH];
qboolean nobsp; qboolean nobsp;
int maxviewers; int maxviewers;
int maxproxies; int maxproxies;
} sv_t;
typedef struct { boolean wanttoexit;
sv_t server;
} qtv_t; } cluster_t;
@ -465,7 +496,7 @@ void ReadString(netmsg_t *b, char *string, int maxlen);
#define svc_deltapacketentities 48 // [...] #define svc_deltapacketentities 48 // [...]
//#define svc_maxspeed 49 // maxspeed change, for prediction //#define svc_maxspeed 49 // maxspeed change, for prediction
//#define svc_entgravity 50 // gravity change, for prediction //#define svc_entgravity 50 // gravity change, for prediction
//#define svc_setinfo 51 // setinfo on a client #define svc_setinfo 51 // setinfo on a client
#define svc_serverinfo 52 // serverinfo #define svc_serverinfo 52 // serverinfo
#define svc_updatepl 53 // [qbyte] [qbyte] #define svc_updatepl 53 // [qbyte] [qbyte]
@ -548,9 +579,9 @@ void WriteData(netmsg_t *b, const char *data, int length);
void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playermask); void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playermask);
void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask); void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask);
void BuildServerData(sv_t *tv, netmsg_t *msg, qboolean mvd); void BuildServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount);
SOCKET QW_InitUDPSocket(int port); SOCKET QW_InitUDPSocket(int port);
void QW_UpdateUDPStuff(sv_t *qtv); void QW_UpdateUDPStuff(cluster_t *qtv);
unsigned int Sys_Milliseconds(void); unsigned int Sys_Milliseconds(void);
void Prox_SendInitialEnts(sv_t *qtv, oproxy_t *prox, netmsg_t *msg); void Prox_SendInitialEnts(sv_t *qtv, oproxy_t *prox, netmsg_t *msg);
qboolean QTV_Connect(sv_t *qtv, char *serverurl); qboolean QTV_Connect(sv_t *qtv, char *serverurl);
@ -559,22 +590,29 @@ qboolean NET_StringToAddr (char *s, netadr_t *sadr);
void SendBufferToViewer(viewer_t *v, const char *buffer, int length, qboolean reliable); void SendBufferToViewer(viewer_t *v, const char *buffer, int length, qboolean reliable);
void Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport); void Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport);
void Netchan_OutOfBandPrint (SOCKET sock, netadr_t adr, char *format, ...); void Netchan_OutOfBandPrint (cluster_t *cluster, SOCKET sock, netadr_t adr, char *format, ...);
void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, char *data, netadr_t adr);
qboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2); qboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2);
qboolean Netchan_Process (netchan_t *chan, netmsg_t *msg); qboolean Netchan_Process (netchan_t *chan, netmsg_t *msg);
void Netchan_Transmit (netchan_t *chan, int length, const unsigned char *data); void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const unsigned char *data);
int SendList(sv_t *qtv, int first, filename_t *list, int svc, netmsg_t *msg); int SendList(sv_t *qtv, int first, const filename_t *list, int svc, netmsg_t *msg);
int Prespawn(sv_t *qtv, int curmsgsize, netmsg_t *msg, int bufnum); int Prespawn(sv_t *qtv, int curmsgsize, netmsg_t *msg, int bufnum);
bsp_t *BSP_LoadModel(char *gamedir, char *bspname); bsp_t *BSP_LoadModel(cluster_t *cluster, char *gamedir, char *bspname);
void BSP_Free(bsp_t *bsp); void BSP_Free(bsp_t *bsp);
int BSP_LeafNum(bsp_t *bsp, float x, float y, float z); int BSP_LeafNum(bsp_t *bsp, float x, float y, float z);
int BSP_SphereLeafNums(bsp_t *bsp, int maxleafs, unsigned short *list, float x, float y, float z, float radius); int BSP_SphereLeafNums(bsp_t *bsp, int maxleafs, unsigned short *list, float x, float y, float z, float radius);
qboolean BSP_Visible(bsp_t *bsp, int leafcount, unsigned short *list); qboolean BSP_Visible(bsp_t *bsp, int leafcount, unsigned short *list);
void BSP_SetupForPosition(bsp_t *bsp, float x, float y, float z); void BSP_SetupForPosition(bsp_t *bsp, float x, float y, float z);
char *Rcon_Command(sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qboolean localcommand); char *Rcon_Command(cluster_t *cluster, sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qboolean localcommand);
char *COM_ParseToken (char *data, char *out, int outsize, const char *punctuation); char *COM_ParseToken (char *data, char *out, int outsize, const char *punctuation);
char *Info_ValueForKey (char *s, const char *key, char *buffer, int buffersize); char *Info_ValueForKey (char *s, const char *key, char *buffer, int buffersize);
void Info_SetValueForStarKey (char *s, const char *key, const char *value, int maxsize); void Info_SetValueForStarKey (char *s, const char *key, const char *value, int maxsize);
void Sys_Printf(cluster_t *cluster, char *fmt, ...);
qboolean Net_FileProxy(sv_t *qtv, char *filename);
sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, qboolean force);
SOCKET Net_MVDListen(int port);
qboolean Net_StopFileProxy(sv_t *qtv);

View File

@ -169,11 +169,46 @@ SOCKET QW_InitUDPSocket(int port)
return sock; return sock;
} }
void BuildServerData(sv_t *tv, netmsg_t *msg, qboolean mvd) void BuildServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount)
{ {
WriteByte(msg, svc_serverdata); WriteByte(msg, svc_serverdata);
WriteLong(msg, PROTOCOL_VERSION); WriteLong(msg, PROTOCOL_VERSION);
WriteLong(msg, tv->servercount); WriteLong(msg, servercount);
if (!tv)
{
//dummy connection, for choosing a game to watch.
WriteString(msg, "qw");
if (mvd)
WriteFloat(msg, 0);
else
WriteByte(msg, MAX_CLIENTS-1);
WriteString(msg, "FTEQTV Proxy");
// get the movevars
WriteFloat(msg, 0);
WriteFloat(msg, 0);
WriteFloat(msg, 0);
WriteFloat(msg, 0);
WriteFloat(msg, 0);
WriteFloat(msg, 0);
WriteFloat(msg, 0);
WriteFloat(msg, 0);
WriteFloat(msg, 0);
WriteFloat(msg, 0);
WriteByte(msg, svc_stufftext);
WriteString2(msg, "fullserverinfo \"");
WriteString2(msg, "\\*QTV\\"VERSION);
WriteString(msg, "\"\n");
}
else
{
WriteString(msg, tv->gamedir); WriteString(msg, tv->gamedir);
if (mvd) if (mvd)
@ -202,6 +237,7 @@ void BuildServerData(sv_t *tv, netmsg_t *msg, qboolean mvd)
WriteString2(msg, tv->serverinfo); WriteString2(msg, tv->serverinfo);
WriteString(msg, "\"\n"); WriteString(msg, "\"\n");
} }
}
void SendServerData(sv_t *tv, viewer_t *viewer) void SendServerData(sv_t *tv, viewer_t *viewer)
{ {
@ -209,7 +245,7 @@ void SendServerData(sv_t *tv, viewer_t *viewer)
char buffer[1024]; char buffer[1024];
InitNetMsg(&msg, buffer, sizeof(buffer)); InitNetMsg(&msg, buffer, sizeof(buffer));
BuildServerData(tv, &msg, false); BuildServerData(tv, &msg, false, viewer->servercount);
SendBufferToViewer(viewer, msg.data, msg.cursize, true); SendBufferToViewer(viewer, msg.data, msg.cursize, true);
@ -342,7 +378,7 @@ int SendStaticEntities(sv_t *tv, int cursize, netmsg_t *msg, int i)
return i; return i;
} }
int SendList(sv_t *qtv, int first, filename_t *list, int svc, netmsg_t *msg) int SendList(sv_t *qtv, int first, const filename_t *list, int svc, netmsg_t *msg)
{ {
int i; int i;
@ -382,7 +418,7 @@ qboolean ChallengePasses(netadr_t *addr, int challenge)
return false; return false;
} }
void NewQWClient(sv_t *qtv, netadr_t *addr, char *connectmessage) void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage)
{ {
viewer_t *viewer; viewer_t *viewer;
@ -398,7 +434,7 @@ void NewQWClient(sv_t *qtv, netadr_t *addr, char *connectmessage)
if (!ChallengePasses(addr, atoi(challenge))) if (!ChallengePasses(addr, atoi(challenge)))
{ {
Netchan_OutOfBandPrint(qtv->qwdsocket, *addr, "n" "Bad challenge"); Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *addr, "n" "Bad challenge");
return; return;
} }
@ -407,29 +443,39 @@ void NewQWClient(sv_t *qtv, netadr_t *addr, char *connectmessage)
memset(viewer, 0, sizeof(viewer_t)); memset(viewer, 0, sizeof(viewer_t));
viewer->trackplayer = -1; viewer->trackplayer = -1;
Netchan_Setup (qtv->qwdsocket, &viewer->netchan, *addr, atoi(qport)); Netchan_Setup (cluster->qwdsocket, &viewer->netchan, *addr, atoi(qport));
viewer->next = qtv->viewers; viewer->next = cluster->viewers;
qtv->viewers = viewer; cluster->viewers = viewer;
viewer->delta_frame = -1; viewer->delta_frame = -1;
qtv->numviewers++; if (cluster->numservers == 1)
{
viewer->server = cluster->servers;
if (!viewer->server->modellist[1].name[0])
viewer->server = NULL; //damn, that server isn't ready
}
if (!viewer->server)
viewer->menunum = 1;
cluster->numviewers++;
Info_ValueForKey(infostring, "name", viewer->name, sizeof(viewer->name)); Info_ValueForKey(infostring, "name", viewer->name, sizeof(viewer->name));
Netchan_OutOfBandPrint(qtv->qwdsocket, *addr, "j"); Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *addr, "j");
} }
void QTV_Rcon(sv_t *qtv, char *message, netadr_t *from) void QTV_Rcon(cluster_t *cluster, char *message, netadr_t *from)
{ {
char buffer[8192]; char buffer[8192];
char *command; char *command;
int passlen; int passlen;
if (!*qtv->password) if (!*cluster->password)
{ {
Netchan_OutOfBandPrint(qtv->qwdsocket, *from, "n" "Bad rcon_password.\n"); Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *from, "n" "Bad rcon_password.\n");
return; return;
} }
@ -438,31 +484,37 @@ void QTV_Rcon(sv_t *qtv, char *message, netadr_t *from)
command = strchr(message, ' '); command = strchr(message, ' ');
passlen = command-message; passlen = command-message;
if (passlen != strlen(qtv->password) || strncmp(message, qtv->password, passlen)) if (passlen != strlen(cluster->password) || strncmp(message, cluster->password, passlen))
{ {
Netchan_OutOfBandPrint(qtv->qwdsocket, *from, "n" "Bad rcon_password.\n"); Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *from, "n" "Bad rcon_password.\n");
return; return;
} }
Netchan_OutOfBandPrint(qtv->qwdsocket, *from, "n%s", Rcon_Command(qtv, command, buffer, sizeof(buffer), false)); Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *from, "n%s", Rcon_Command(cluster, NULL, command, buffer, sizeof(buffer), false));
} }
void QTV_Status(sv_t *qtv, netadr_t *from)
void QTV_Status(cluster_t *cluster, netadr_t *from)
{ {
int i; int i;
char buffer[8192]; char buffer[8192];
sv_t *sv;
netmsg_t msg; netmsg_t msg;
char elem[256]; char elem[256];
InitNetMsg(&msg, buffer, sizeof(buffer)); InitNetMsg(&msg, buffer, sizeof(buffer));
WriteLong(&msg, -1); WriteLong(&msg, -1);
WriteByte(&msg, 'n'); WriteByte(&msg, 'n');
WriteString2(&msg, qtv->serverinfo);
WriteString2(&msg, "\n");
if (cluster->numservers==1)
{ //show this server's info
sv = cluster->servers;
WriteString2(&msg, sv->serverinfo);
WriteString2(&msg, "\n");
for (i = 0;i < MAX_CLIENTS-1; i++) for (i = 0;i < MAX_CLIENTS-1; i++)
{ {
if (!qtv->players[i].active) if (!sv->players[i].active)
continue; continue;
//userid //userid
sprintf(elem, "%i", i); sprintf(elem, "%i", i);
@ -470,7 +522,7 @@ void QTV_Status(sv_t *qtv, netadr_t *from)
WriteString2(&msg, " "); WriteString2(&msg, " ");
//frags //frags
sprintf(elem, "%i", qtv->players[i].frags); sprintf(elem, "%i", sv->players[i].frags);
WriteString2(&msg, elem); WriteString2(&msg, elem);
WriteString2(&msg, " "); WriteString2(&msg, " ");
@ -480,46 +532,57 @@ void QTV_Status(sv_t *qtv, netadr_t *from)
WriteString2(&msg, " "); WriteString2(&msg, " ");
//ping //ping
sprintf(elem, "%i", qtv->players[i].ping); sprintf(elem, "%i", sv->players[i].ping);
WriteString2(&msg, elem); WriteString2(&msg, elem);
WriteString2(&msg, " "); WriteString2(&msg, " ");
//name //name
Info_ValueForKey(qtv->players[i].userinfo, "name", elem, sizeof(elem)); Info_ValueForKey(sv->players[i].userinfo, "name", elem, sizeof(elem));
WriteString2(&msg, "\""); WriteString2(&msg, "\"");
WriteString2(&msg, elem); WriteString2(&msg, elem);
WriteString2(&msg, "\" "); WriteString2(&msg, "\" ");
//skin //skin
Info_ValueForKey(qtv->players[i].userinfo, "skin", elem, sizeof(elem)); Info_ValueForKey(sv->players[i].userinfo, "skin", elem, sizeof(elem));
WriteString2(&msg, "\""); WriteString2(&msg, "\"");
WriteString2(&msg, elem); WriteString2(&msg, elem);
WriteString2(&msg, "\" "); WriteString2(&msg, "\" ");
WriteString2(&msg, " "); WriteString2(&msg, " ");
//tc //tc
Info_ValueForKey(qtv->players[i].userinfo, "topcolor", elem, sizeof(elem)); Info_ValueForKey(sv->players[i].userinfo, "topcolor", elem, sizeof(elem));
WriteString2(&msg, elem); WriteString2(&msg, elem);
WriteString2(&msg, " "); WriteString2(&msg, " ");
//bc //bc
Info_ValueForKey(qtv->players[i].userinfo, "bottomcolor", elem, sizeof(elem)); Info_ValueForKey(sv->players[i].userinfo, "bottomcolor", elem, sizeof(elem));
WriteString2(&msg, elem); WriteString2(&msg, elem);
WriteString2(&msg, " "); WriteString2(&msg, " ");
WriteString2(&msg, "\n"); WriteString2(&msg, "\n");
} }
}
else
{
WriteString2(&msg, "\\hostname\\");
WriteString2(&msg, cluster->hostname);
for (sv = cluster->servers, i = 0; sv; sv = sv->next, i++)
{
sprintf(elem, "\\%i\\", i);
WriteString2(&msg, elem);
WriteString2(&msg, sv->serveraddress);
sprintf(elem, " (%s)", sv->serveraddress);
WriteString2(&msg, sv->serveraddress);
}
}
WriteByte(&msg, 0); WriteByte(&msg, 0);
NET_SendPacket(qtv->qwdsocket, msg.cursize, msg.data, *from); NET_SendPacket(cluster, cluster->qwdsocket, msg.cursize, msg.data, *from);
//Netchan_OutOfBandPrint(qtv->qwdsocket, *from, "n%s\n", qtv->serverinfo);
} }
void ConnectionlessPacket(sv_t *qtv, netadr_t *from, netmsg_t *m) void ConnectionlessPacket(cluster_t *cluster, netadr_t *from, netmsg_t *m)
{ {
char buffer[MAX_MSGLEN]; char buffer[MAX_MSGLEN];
int i; int i;
@ -529,40 +592,36 @@ void ConnectionlessPacket(sv_t *qtv, netadr_t *from, netmsg_t *m)
if (!strncmp(buffer, "rcon ", 5)) if (!strncmp(buffer, "rcon ", 5))
{ {
QTV_Rcon(qtv, buffer+5, from); QTV_Rcon(cluster, buffer+5, from);
return; return;
} }
if (!strncmp(buffer, "ping", 4)) if (!strncmp(buffer, "ping", 4))
{ //ack { //ack
NET_SendPacket (qtv->qwdsocket, 1, "l", *from); NET_SendPacket (cluster, cluster->qwdsocket, 1, "l", *from);
return; return;
} }
if (!strncmp(buffer, "status", 6)) if (!strncmp(buffer, "status", 6))
{ {
QTV_Status(qtv, from); QTV_Status(cluster, from);
return; return;
} }
if (!strncmp(buffer, "getchallenge", 12)) if (!strncmp(buffer, "getchallenge", 12))
{ {
i = NewChallenge(from); i = NewChallenge(from);
Netchan_OutOfBandPrint(qtv->qwdsocket, *from, "c%i", i); Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *from, "c%i", i);
return; return;
} }
if (!strncmp(buffer, "connect 28 ", 11)) if (!strncmp(buffer, "connect 28 ", 11))
{ {
if (qtv->sourcesock == INVALID_SOCKET && !qtv->file) //tell them what's going on instead of expecting them to see a lack of anything happening. if (cluster->numviewers >= cluster->maxviewers && cluster->maxviewers)
Netchan_OutOfBandPrint(qtv->qwdsocket, *from, "n" "Proxy is not connected to a server\n"); Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *from, "n" "Sorry, proxy is full.\n");
else if (qtv->parsingconnectiondata) //connecting at this time is a bit silly.
Netchan_OutOfBandPrint(qtv->qwdsocket, *from, "n" "Buffering demo, please try again\n");
else if (qtv->numviewers >= qtv->maxviewers && qtv->maxviewers)
Netchan_OutOfBandPrint(qtv->qwdsocket, *from, "n" "Sorry, proxy is full.\n");
else else
NewQWClient(qtv, from, buffer); NewQWClient(cluster, from, buffer);
return; return;
} }
if (!strncmp(buffer, "l\n", 2)) if (!strncmp(buffer, "l\n", 2))
{ {
printf("Ack\n"); Sys_Printf(cluster, "Ack\n");
} }
} }
@ -744,7 +803,12 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg)
short interp; short interp;
float lerp; float lerp;
if (tv->physicstime != v->settime && tv->chokeonnotupdated)
memset(&to, 0, sizeof(to));
if (v->server)
{
if (tv->physicstime != v->settime && tv->cluster->chokeonnotupdated)
{ {
WriteByte(msg, svc_updatestatlong); WriteByte(msg, svc_updatestatlong);
WriteByte(msg, STAT_TIME); WriteByte(msg, STAT_TIME);
@ -753,19 +817,7 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg)
v->settime = tv->physicstime; v->settime = tv->physicstime;
} }
memset(&to, 0, sizeof(to));
/*if (v->trackplayer>=0)
{
BSP_SetupForPosition(tv->bsp, tv->players[v->trackplayer].current.origin[0]/8.0f,
tv->players[v->trackplayer].current.origin[1]/8.0f,
tv->players[v->trackplayer].current.origin[2]/8.0f);
}
else*/
{
BSP_SetupForPosition(tv->bsp, v->origin[0], v->origin[1], v->origin[2]); BSP_SetupForPosition(tv->bsp, v->origin[0], v->origin[1], v->origin[2]);
}
lerp = ((tv->curtime - tv->oldpackettime)/1000.0f) / ((tv->nextpackettime - tv->oldpackettime)/1000.0f); lerp = ((tv->curtime - tv->oldpackettime)/1000.0f) / ((tv->nextpackettime - tv->oldpackettime)/1000.0f);
if (lerp < 0) if (lerp < 0)
@ -773,7 +825,6 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg)
if (lerp > 1) if (lerp > 1)
lerp = 1; lerp = 1;
for (i = 0; i < MAX_CLIENTS-1; i++) for (i = 0; i < MAX_CLIENTS-1; i++)
{ {
if (!tv->players[i].active) if (!tv->players[i].active)
@ -822,6 +873,11 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg)
if (flags & PF_WEAPONFRAME) if (flags & PF_WEAPONFRAME)
WriteByte(msg, tv->players[i].current.weaponframe); WriteByte(msg, tv->players[i].current.weaponframe);
} }
}
else
{
lerp = 1;
}
WriteByte(msg, svc_playerinfo); WriteByte(msg, svc_playerinfo);
WriteByte(msg, MAX_CLIENTS-1); WriteByte(msg, MAX_CLIENTS-1);
@ -839,6 +895,7 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg)
e = &v->frame[v->netchan.outgoing_sequence&(ENTITY_FRAMES-1)]; e = &v->frame[v->netchan.outgoing_sequence&(ENTITY_FRAMES-1)];
e->numents = 0; e->numents = 0;
if (v->server)
for (i = 0; i < tv->maxents; i++) for (i = 0; i < tv->maxents; i++)
{ {
if (!tv->entity[i].current.modelindex) if (!tv->entity[i].current.modelindex)
@ -871,13 +928,13 @@ void UpdateStats(sv_t *qtv, viewer_t *v)
netmsg_t msg; netmsg_t msg;
char buf[6]; char buf[6];
int i; int i;
const static unsigned int nullstats[MAX_STATS]; const static unsigned int nullstats[MAX_STATS] = {1000};
const unsigned int *stats; const unsigned int *stats;
InitNetMsg(&msg, buf, sizeof(buf)); InitNetMsg(&msg, buf, sizeof(buf));
if (v->trackplayer < 0) if (v->trackplayer < 0 || !qtv)
stats = nullstats; stats = nullstats;
else else
stats = qtv->players[v->trackplayer].stats; stats = qtv->players[v->trackplayer].stats;
@ -1000,14 +1057,14 @@ void QTV_Say(sv_t *qtv, viewer_t *v, char *message)
Multicast(qtv, msg.data, msg.cursize, dem_all, (unsigned int)-1); Multicast(qtv, msg.data, msg.cursize, dem_all, (unsigned int)-1);
} }
qboolean QW_IsOn(sv_t *qtv, char *name) viewer_t *QW_IsOn(cluster_t *cluster, char *name)
{ {
viewer_t *v; viewer_t *v;
for (v = qtv->viewers; v; v = v->next) for (v = cluster->viewers; v; v = v->next)
if (!strcmp(v->name, name)) //this needs to allow dequakified names. if (!strcmp(v->name, name)) //this needs to allow dequakified names.
return true; return v;
return false; return NULL;
} }
void QW_PrintfToViewer(viewer_t *v, char *format, ...) void QW_PrintfToViewer(viewer_t *v, char *format, ...)
@ -1032,7 +1089,13 @@ void QW_PrintfToViewer(viewer_t *v, char *format, ...)
SendBufferToViewer(v, buf, strlen(buf)+1, true); SendBufferToViewer(v, buf, strlen(buf)+1, true);
} }
void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m) static const filename_t ConnectionlessModelList[] = {"", "maps/start.bsp", "progs/player.mdl", ""};
static const filename_t ConnectionlessSoundList[] = {"", ""};
void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum);
void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m)
{ {
usercmd_t oldest, oldcmd, newcmd; usercmd_t oldest, oldcmd, newcmd;
char buf[1024]; char buf[1024];
@ -1055,10 +1118,6 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m)
if (!strcmp(buf, "new")) if (!strcmp(buf, "new"))
SendServerData(qtv, v); SendServerData(qtv, v);
else if (!strncmp(buf, "say \"", 5) && !qtv->notalking)
QTV_Say(qtv, v, buf+5);
else if (!strncmp(buf, "say ", 4) && !qtv->notalking)
QTV_Say(qtv, v, buf+4);
else if (!strncmp(buf, "modellist ", 10)) else if (!strncmp(buf, "modellist ", 10))
{ {
char *cmd = buf+10; char *cmd = buf+10;
@ -1071,12 +1130,15 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m)
InitNetMsg(&msg, buf, sizeof(buf)); InitNetMsg(&msg, buf, sizeof(buf));
if (svcount != qtv->servercount) if (svcount != v->servercount)
{ //looks like we changed map without them. { //looks like we changed map without them.
SendServerData(qtv, v); SendServerData(qtv, v);
return; return;
} }
if (!qtv)
SendList(qtv, first, ConnectionlessModelList, svc_modellist, &msg);
else
SendList(qtv, first, qtv->modellist, svc_modellist, &msg); SendList(qtv, first, qtv->modellist, svc_modellist, &msg);
SendBufferToViewer(v, msg.data, msg.cursize, true); SendBufferToViewer(v, msg.data, msg.cursize, true);
} }
@ -1092,12 +1154,15 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m)
InitNetMsg(&msg, buf, sizeof(buf)); InitNetMsg(&msg, buf, sizeof(buf));
if (svcount != qtv->servercount) if (svcount != v->servercount)
{ //looks like we changed map without them. { //looks like we changed map without them.
SendServerData(qtv, v); SendServerData(qtv, v);
return; return;
} }
if (!qtv)
SendList(qtv, first, ConnectionlessSoundList, svc_soundlist, &msg);
else
SendList(qtv, first, qtv->soundlist, svc_soundlist, &msg); SendList(qtv, first, qtv->soundlist, svc_soundlist, &msg);
SendBufferToViewer(v, msg.data, msg.cursize, true); SendBufferToViewer(v, msg.data, msg.cursize, true);
} }
@ -1105,7 +1170,7 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m)
{ {
char skin[128]; char skin[128];
if (atoi(buf + 9) != qtv->servercount) if (atoi(buf + 9) != v->servercount)
SendServerData(qtv, v); //we're old. SendServerData(qtv, v); //we're old.
else else
{ {
@ -1120,13 +1185,17 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m)
InitNetMsg(&msg, buf, sizeof(buf)); InitNetMsg(&msg, buf, sizeof(buf));
if (v->server)
{
r = Prespawn(qtv, v->netchan.message.cursize, &msg, r); r = Prespawn(qtv, v->netchan.message.cursize, &msg, r);
SendBufferToViewer(v, msg.data, msg.cursize, true); SendBufferToViewer(v, msg.data, msg.cursize, true);
}
else r = -1;
if (r < 0) if (r < 0)
sprintf(skin, "%ccmd spawn\n", svc_stufftext); sprintf(skin, "%ccmd spawn\n", svc_stufftext);
else else
sprintf(skin, "%ccmd prespawn %i %i\n", svc_stufftext, qtv->servercount, r); sprintf(skin, "%ccmd prespawn %i %i\n", svc_stufftext, v->servercount, r);
SendBufferToViewer(v, skin, strlen(skin)+1, true); SendBufferToViewer(v, skin, strlen(skin)+1, true);
} }
@ -1139,7 +1208,7 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m)
} }
else if (!strncmp(buf, "begin", 5)) else if (!strncmp(buf, "begin", 5))
{ {
if (atoi(buf+6) != qtv->servercount) if (atoi(buf+6) != v->servercount)
SendServerData(qtv, v); //this is unfortunate! SendServerData(qtv, v); //this is unfortunate!
else else
v->thinksitsconnected = true; v->thinksitsconnected = true;
@ -1157,15 +1226,44 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m)
v->drop = true; v->drop = true;
else if (!strncmp(buf, "ison", 4)) else if (!strncmp(buf, "ison", 4))
{ {
if (QW_IsOn(qtv, buf+6)) viewer_t *other;
QW_PrintfToViewer(v, "%s is on the proxy\n", buf+6); if ((other = QW_IsOn(qtv->cluster, buf+5)))
{
if (!other->server)
QW_PrintfToViewer(v, "%s is on the proxy, but not yet watching a game\n");
else else
QW_PrintfToViewer(v, "%s is not on the proxy, sorry\n", buf+6); //the apology is to make the alternatives distinct. QW_PrintfToViewer(v, "%s is watching %s\n", buf+5, other->server->server);
}
else
QW_PrintfToViewer(v, "%s is not on the proxy, sorry\n", buf+5); //the apology is to make the alternatives distinct.
} }
else if (!strncmp(buf, "ptrack ", 7)) else if (!strncmp(buf, "ptrack ", 7))
v->trackplayer = atoi(buf+7); v->trackplayer = atoi(buf+7);
else if (!strncmp(buf, "ptrack", 6)) else if (!strncmp(buf, "ptrack", 6))
v->trackplayer = -1; v->trackplayer = -1;
else if (!strncmp(buf, "pings", 5))
{
}
else if (!strncmp(buf, "say \"", 5) && !cluster->notalking)
QTV_Say(qtv, v, buf+5);
else if (!strncmp(buf, "say ", 4) && !cluster->notalking)
QTV_Say(qtv, v, buf+4);
else if (!strncmp(buf, "servers", 7))
{
v->menunum = 1;
}
else if (!strncmp(buf, "reset", 7))
{
v->server = NULL;
v->menunum = 1;
}
else if (!qtv)
{
//all the other things need an active server.
QW_PrintfToViewer(v, "Choose a server first\n");
}
else if (!strncmp(buf, "serverinfo", 5)) else if (!strncmp(buf, "serverinfo", 5))
{ {
char *key, *value, *end; char *key, *value, *end;
@ -1208,7 +1306,7 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m)
} }
else else
{ {
printf("Client sent unknown string command: %s\n", buf); Sys_Printf(cluster, "Client sent unknown string command: %s\n", buf);
} }
break; break;
@ -1219,6 +1317,29 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m)
ReadDeltaUsercmd(m, &nullcmd, &oldest); ReadDeltaUsercmd(m, &nullcmd, &oldest);
ReadDeltaUsercmd(m, &oldest, &oldcmd); ReadDeltaUsercmd(m, &oldest, &oldcmd);
ReadDeltaUsercmd(m, &oldcmd, &newcmd); ReadDeltaUsercmd(m, &oldcmd, &newcmd);
if (v->menunum)
{
if (newcmd.buttons&1 && !(oldcmd.buttons&1))
Menu_Enter(cluster, v, 0);
if (newcmd.buttons&2 && !(oldcmd.buttons&2))
Menu_Enter(cluster, v, 1);
if (newcmd.sidemove && !oldcmd.sidemove)
Menu_Enter(cluster, v, newcmd.sidemove<0);
if (newcmd.forwardmove && !oldcmd.forwardmove)
{ //they pressed the button...
if (newcmd.forwardmove < 0)
{
v->menuop+=1;
}
else
{
v->menuop-=1;
}
}
}
else
PMove(v, &newcmd); PMove(v, &newcmd);
break; break;
case clc_tmove: case clc_tmove:
@ -1238,18 +1359,159 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m)
} }
} }
void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum)
{
//build a possible message, even though it'll probably not be sent
sv_t *sv;
int i, min;
char buffer[2048];
netmsg_t m;
InitNetMsg(&m, buffer, sizeof(buffer));
WriteByte(&m, svc_stufftext);
switch(viewer->menunum)
{
default:
break;
case 1:
if (!cluster->servers)
{
WriteString(&m, "messagemode\n");
SendBufferToViewer(viewer, m.data, m.cursize, true);
}
else
{
if (viewer->menuop < 0)
viewer->menuop = 0;
i = 0;
min = viewer->menuop - 10;
if (min < 0)
min = 0;
for (sv = cluster->servers; sv && i<min; sv = sv->next, i++)
{//skip over the early connections.
}
min+=20;
for (; sv && i < min; sv = sv->next, i++)
{
if (i == viewer->menuop)
{
if (sv->parsingconnectiondata || !sv->modellist[1].name[0])
{
WriteString(&m, "echo But that stream isn't connected\n");
SendBufferToViewer(viewer, m.data, m.cursize, true);
}
else
{
WriteString(&m, "cmd new\n");
viewer->servercount++;
viewer->server = sv;
SendBufferToViewer(viewer, m.data, m.cursize, true);
viewer->menunum = 0;
viewer->thinksitsconnected = false;
}
break;
}
}
}
break;
}
}
void Menu_Draw(cluster_t *cluster, viewer_t *viewer)
{
char buffer[2048];
char str[64];
sv_t *sv;
int i, min;
unsigned char *s;
netmsg_t m;
InitNetMsg(&m, buffer, sizeof(buffer));
WriteByte(&m, svc_centerprint);
WriteString2(&m, "FTEQTV\n");
if (strcmp(cluster->hostname, DEFAULT_HOSTNAME))
WriteString2(&m, cluster->hostname);
WriteString2(&m, "\n\n");
switch(viewer->menunum)
{
default:
WriteString2(&m, "bad menu");
break;
case 1: //connections list
if (!cluster->servers)
{
WriteString2(&m, "No active connections");
}
else
{
if (viewer->menuop < 0)
viewer->menuop = 0;
i = 0;
min = viewer->menuop - 10;
if (min < 0)
min = 0;
for (sv = cluster->servers; sv && i<min; sv = sv->next, i++)
{//skip over the early connections.
}
min+=20;
for (; sv && i < min; sv = sv->next, i++)
{
Info_ValueForKey(sv->serverinfo, "hostname", str, sizeof(str));
if (sv->parsingconnectiondata || !sv->modellist[1].name[0])
snprintf(str, sizeof(str), "%s", sv->server);
if (i == viewer->menuop)
for (s = (unsigned char *)str; *s; s++)
{
if ((unsigned)*s >= ' ')
*s = 128 | (*s&~128);
}
WriteString2(&m, str);
WriteString2(&m, "\n");
}
}
break;
case 2: //admin menu
WriteString2(&m, " port");
WriteString2(&m, (viewer->menuop==0)?" \r ":" : ");
sprintf(str, "%-20i", cluster->qwlistenportnum);
WriteString2(&m, str);
WriteString2(&m, "\n");
WriteString2(&m, "hostname");
WriteString2(&m, (viewer->menuop==1)?" \r ":" : ");
sprintf(str, "%-20s", cluster->hostname);
WriteString2(&m, str);
WriteString2(&m, "\n");
break;
}
WriteByte(&m, 0);
SendBufferToViewer(viewer, m.data, m.cursize, true);
}
static const char dropcmd[] = {svc_stufftext, 'd', 'i', 's', 'c', 'o', 'n', 'n', 'e', 'c', 't', '\n', '\0'}; static const char dropcmd[] = {svc_stufftext, 'd', 'i', 's', 'c', 'o', 'n', 'n', 'e', 'c', 't', '\n', '\0'};
void QW_FreeViewer(sv_t *qtv, viewer_t *viewer)
void QW_FreeViewer(cluster_t *cluster, viewer_t *viewer)
{ {
int i; int i;
//note: unlink them yourself. //note: unlink them yourself.
printf("Dropping viewer %s\n", viewer->name); Sys_Printf(cluster, "Dropping viewer %s\n", viewer->name);
//spam them thrice, then forget about them //spam them thrice, then forget about them
Netchan_Transmit(&viewer->netchan, strlen(dropcmd)+1, dropcmd); Netchan_Transmit(cluster, &viewer->netchan, strlen(dropcmd)+1, dropcmd);
Netchan_Transmit(&viewer->netchan, strlen(dropcmd)+1, dropcmd); Netchan_Transmit(cluster, &viewer->netchan, strlen(dropcmd)+1, dropcmd);
Netchan_Transmit(&viewer->netchan, strlen(dropcmd)+1, dropcmd); Netchan_Transmit(cluster, &viewer->netchan, strlen(dropcmd)+1, dropcmd);
for (i = 0; i < MAX_BACK_BUFFERS; i++) for (i = 0; i < MAX_BACK_BUFFERS; i++)
{ {
@ -1259,10 +1521,10 @@ void QW_FreeViewer(sv_t *qtv, viewer_t *viewer)
free(viewer); free(viewer);
qtv->numviewers--; cluster->numviewers--;
} }
void QW_UpdateUDPStuff(sv_t *qtv) void QW_UpdateUDPStuff(cluster_t *cluster)
{ {
char buffer[MAX_MSGLEN*2]; char buffer[MAX_MSGLEN*2];
netadr_t from; netadr_t from;
@ -1273,18 +1535,18 @@ void QW_UpdateUDPStuff(sv_t *qtv)
viewer_t *v, *f; viewer_t *v, *f;
if (*qtv->master && (qtv->curtime > qtv->mastersendtime || qtv->mastersendtime > qtv->curtime + 4*1000*60)) //urm... time wrapped? if (*cluster->master && (cluster->curtime > cluster->mastersendtime || cluster->mastersendtime > cluster->curtime + 4*1000*60)) //urm... time wrapped?
{ {
if (NET_StringToAddr(qtv->master, &from)) if (NET_StringToAddr(cluster->master, &from))
{ {
sprintf(buffer, "a\n%i\n0\n", qtv->mastersequence++); //fill buffer with a heartbeat sprintf(buffer, "a\n%i\n0\n", cluster->mastersequence++); //fill buffer with a heartbeat
//why is there no \xff\xff\xff\xff ?.. //why is there no \xff\xff\xff\xff ?..
NET_SendPacket(qtv->qwdsocket, strlen(buffer), buffer, from); NET_SendPacket(cluster, cluster->qwdsocket, strlen(buffer), buffer, from);
} }
else else
printf("Cannot resolve master %s\n", qtv->master); Sys_Printf(cluster, "Cannot resolve master %s\n", cluster->master);
qtv->mastersendtime = qtv->curtime + 3*1000*60; //3 minuites. cluster->mastersendtime = cluster->curtime + 3*1000*60; //3 minuites.
} }
m.data = buffer; m.data = buffer;
@ -1294,7 +1556,7 @@ void QW_UpdateUDPStuff(sv_t *qtv)
for (;;) for (;;)
{ {
read = recvfrom(qtv->qwdsocket, buffer, sizeof(buffer), 0, (struct sockaddr*)from, &fromsize); read = recvfrom(cluster->qwdsocket, buffer, sizeof(buffer), 0, (struct sockaddr*)from, &fromsize);
if (read <= 5) //otherwise it's a runt or bad. if (read <= 5) //otherwise it's a runt or bad.
{ {
@ -1310,7 +1572,7 @@ void QW_UpdateUDPStuff(sv_t *qtv)
if (*(int*)buffer == -1) if (*(int*)buffer == -1)
{ //connectionless message { //connectionless message
ConnectionlessPacket(qtv, &from, &m); ConnectionlessPacket(cluster, &from, &m);
continue; continue;
} }
@ -1319,20 +1581,22 @@ void QW_UpdateUDPStuff(sv_t *qtv)
ReadLong(&m); ReadLong(&m);
qport = ReadShort(&m); qport = ReadShort(&m);
for (v = qtv->viewers; v; v = v->next) for (v = cluster->viewers; v; v = v->next)
{ {
if (Net_CompareAddress(&v->netchan.remote_address, &from, v->netchan.qport, qport)) if (Net_CompareAddress(&v->netchan.remote_address, &from, v->netchan.qport, qport))
{ {
if (Netchan_Process(&v->netchan, &m)) if (Netchan_Process(&v->netchan, &m))
{ {
v->netchan.outgoing_sequence = v->netchan.incoming_sequence; //compensate for client->server packetloss. v->netchan.outgoing_sequence = v->netchan.incoming_sequence; //compensate for client->server packetloss.
if (!v->chokeme || !qtv->chokeonnotupdated) if (!v->server)
v->maysend = true;
else if (!v->chokeme || !cluster->chokeonnotupdated)
{ {
v->maysend = true; v->maysend = true;
v->chokeme = qtv->chokeonnotupdated; v->chokeme = cluster->chokeonnotupdated;
} }
ParseQWC(qtv, v, &m); ParseQWC(cluster, v->server, v, &m);
} }
break; break;
} }
@ -1340,36 +1604,39 @@ void QW_UpdateUDPStuff(sv_t *qtv)
} }
if (qtv->viewers && qtv->viewers->drop) if (cluster->viewers && cluster->viewers->drop)
{ {
printf("Dropping client\n"); Sys_Printf(cluster, "Dropping client\n");
f = qtv->viewers; f = cluster->viewers;
qtv->viewers = f->next; cluster->viewers = f->next;
QW_FreeViewer(qtv, f); QW_FreeViewer(cluster, f);
} }
for (v = qtv->viewers; v; v = v->next) for (v = cluster->viewers; v; v = v->next)
{ {
if (v->next && v->next->drop) if (v->next && v->next->drop)
{ //free the next/ { //free the next/
printf("Dropping client\n"); Sys_Printf(cluster, "Dropping client\n");
f = v->next; f = v->next;
v->next = f->next; v->next = f->next;
QW_FreeViewer(qtv, f); QW_FreeViewer(cluster, f);
} }
if (v->maysend && !qtv->parsingconnectiondata) //don't send incompleate connection data. if (v->maysend && (!v->server || !v->server->parsingconnectiondata)) //don't send incompleate connection data.
{ {
v->maysend = false; v->maysend = false;
m.cursize = 0; m.cursize = 0;
if (v->thinksitsconnected) if (v->thinksitsconnected)
{ {
SendPlayerStates(qtv, v, &m); SendPlayerStates(v->server, v, &m);
UpdateStats(qtv, v); UpdateStats(v->server, v);
if (v->menunum)
Menu_Draw(cluster, v);
} }
Netchan_Transmit(&v->netchan, m.cursize, m.data); Netchan_Transmit(cluster, &v->netchan, m.cursize, m.data);
if (!v->netchan.message.cursize && v->backbuffered) if (!v->netchan.message.cursize && v->backbuffered)
{//shift the backbuffers around {//shift the backbuffers around

View File

@ -89,7 +89,7 @@ void Info_RemoveKey (char *s, const char *key)
if (strstr (key, "\\")) if (strstr (key, "\\"))
{ {
printf ("Key has a slash\n"); // printf ("Key has a slash\n");
return; return;
} }
@ -139,19 +139,19 @@ void Info_SetValueForStarKey (char *s, const char *key, const char *value, int m
if (strstr (key, "\\") || strstr (value, "\\") ) if (strstr (key, "\\") || strstr (value, "\\") )
{ {
printf ("Key has a slash\n"); // printf ("Key has a slash\n");
return; return;
} }
if (strstr (key, "\"") || strstr (value, "\"") ) if (strstr (key, "\"") || strstr (value, "\"") )
{ {
printf ("Key has a quote\n"); // printf ("Key has a quote\n");
return; return;
} }
if (strlen(key) >= MAX_INFO_KEY || strlen(value) >= MAX_INFO_KEY) if (strlen(key) >= MAX_INFO_KEY || strlen(value) >= MAX_INFO_KEY)
{ {
printf ("Key or value is too long\n"); // printf ("Key or value is too long\n");
return; return;
} }
@ -177,7 +177,7 @@ void Info_SetValueForStarKey (char *s, const char *key, const char *value, int m
if ((int)(strlen(newv) + strlen(s) + 1) > maxsize) if ((int)(strlen(newv) + strlen(s) + 1) > maxsize)
{ {
printf ("info buffer is too small\n"); // printf ("info buffer is too small\n");
return; return;
} }
@ -293,23 +293,219 @@ skipwhite:
return data; return data;
} }
char *Rcon_Command(sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qboolean localcommand) #define MAX_ARGS 8
#define ARG_LEN 512
char *Cluster_Rcon_Dispatch(cluster_t *cluster, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
if (!strcmp(arg[0], "hostname"))
{
strncpy(cluster->hostname, arg[1], sizeof(cluster->hostname)-1);
return "hostname will change at start of next map\n"; //I'm too lazy to alter the serverinfo here.
}
else if (!strcmp(arg[0], "master"))
{
netadr_t addr;
strncpy(cluster->master, arg[1], sizeof(cluster->master)-1);
cluster->mastersendtime = cluster->curtime;
if (NET_StringToAddr(arg[1], &addr)) //send a ping like a qw server does. this is kinda pointless of course.
NET_SendPacket (cluster, cluster->qwdsocket, 1, "k", addr);
return "Master server set.\n";
}
else if (!strcmp(arg[0], "port"))
{
int news;
int newp = atoi(arg[1]);
news = QW_InitUDPSocket(newp);
if (news != INVALID_SOCKET)
{
cluster->mastersendtime = cluster->curtime;
closesocket(cluster->qwdsocket);
cluster->qwdsocket = news;
cluster->qwlistenportnum = newp;
return "Opened udp port (all connected qw clients will time out)\n";
}
else
return "Failed to open udp port\n";
}
else if (!strcmp(arg[0], "password"))
{
if (!localcommand)
return "Rejecting rcon password change.\n";
strncpy(cluster->password, arg[1], sizeof(cluster->password)-1);
return "Password changed.\n";
}
else if (!strcmp(arg[0], "connect") || !strcmp(arg[0], "addserver"))
{
if (!*arg[1])
return "connect requires an ip:port parameter\n";
memmove(arg[1]+4, arg[1], ARG_LEN-5);
strncpy(arg[1], "tcp:", 4);
if (!QTV_NewServerConnection(cluster, arg[1], false))
return "Failed to connect to server, connection aborted\n";
return "Connection registered\n";
}
else if (!strcmp(arg[0], "demo") || !strcmp(arg[0], "adddemo") || !strcmp(arg[0], "addfile"))
{
if (!*arg[1])
return "adddemo requires an filename parameter\n";
if (!localcommand)
if (*arg[1] == '\\' || *arg[1] == '/' || strstr(arg[1], "..") || arg[1][1] == ':')
return "Absolute paths are prohibited.\n";
memmove(arg[1]+5, arg[1], ARG_LEN-6);
strncpy(arg[1], "file:", 5);
if (!QTV_NewServerConnection(cluster, arg[1], false))
return "Failed to connect to server, connection aborted\n";
return "Connection registered\n";
}
else if (!strcmp(arg[0], "exec"))
{
FILE *f;
char line[512], *res;
if (!localcommand)
if (*arg[1] == '\\' || *arg[1] == '/' || strstr(arg[1], "..") || arg[1][1] == ':')
return "Absolute paths are prohibited.\n";
f = fopen(arg[1], "rt");
if (!f)
{
snprintf(buffer, sizeofbuffer, "Couldn't exec \"%s\"\n", arg[1]);
return buffer;
}
else
{
while(fgets(line, sizeof(line)-1, f))
{
res = Rcon_Command(cluster, NULL, line, buffer, sizeofbuffer, localcommand);
Sys_Printf(cluster, "%s", res);
}
fclose(f);
return "Execed\n";
}
}
else if (!strcmp(arg[0], "status"))
{
buffer[0] = '\0';
sprintf(buffer, "%i connections\n", cluster->numservers);
strcat(buffer, "Options:\n");
if (cluster->chokeonnotupdated)
strcat(buffer, " Choke\n");
if (cluster->lateforward)
strcat(buffer, " Late forwarding\n");
if (!cluster->notalking)
strcat(buffer, " Talking allowed\n");
if (cluster->nobsp)
strcat(buffer, " No BSP loading\n");
strcat(buffer, "\n");
return buffer;
}
else if (!strcmp(arg[0], "choke"))
{
cluster->chokeonnotupdated = !!atoi(arg[1]);
return "choke-until-update set\n";
}
else if (!strcmp(arg[0], "late"))
{
cluster->lateforward = !!atoi(arg[1]);
return "late forwarding set\n";
}
else if (!strcmp(arg[0], "talking"))
{
cluster->notalking = !atoi(arg[1]);
return "talking permissions set\n";
}
else if (!strcmp(arg[0], "nobsp"))
{
cluster->nobsp = !!atoi(arg[1]);
return "nobsp will change at start of next map\n";
}
else if (!strcmp(arg[1], "maxviewers"))
{
cluster->maxviewers = atoi(arg[2]);
return "maxviewers set\n";
}
else if (!strcmp(arg[1], "maxproxies"))
{
cluster->maxproxies = atoi(arg[2]);
return "maxproxies set\n";
}
else if (!strcmp(arg[0], "ping"))
{
netadr_t addr;
if (NET_StringToAddr(arg[1], &addr))
{
NET_SendPacket (cluster, cluster->qwdsocket, 1, "k", addr);
return "pinged\n";
}
return "couldn't resolve\n";
}
else if (!strcmp(arg[0], "help"))
{
return "FTEQTV proxy\nValid commands: connect, addserver, adddemo, status, choke, late, talking, nobsp, exec, password, master, hostname, port, maxviewers, maxproxies\n";
}
else if (!strcmp(arg[0], "mvdport"))
{
return "mvdport requires a targeted server. Connect first.\n";
}
else if (!strcmp(arg[0], "record"))
{
return "record requires a targeted server\n";
}
else if (!strcmp(arg[0], "reconnect"))
{
return "reconnect requires a targeted server\n";
}
else if (!strcmp(arg[0], "stop"))
{ //fixme
return "stop requires a targeted server\n";
}
else if (!strcmp(arg[0], "echo"))
{
return "Poly wants a cracker.\n";
}
else if (!strcmp(arg[0], "quit"))
{
cluster->wanttoexit = true;
return "Shutting down.\n";
}
else
return NULL;
}
char *Server_Rcon_Dispatch(sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{ {
#define TOKENIZE_PUNCTUATION "" #define TOKENIZE_PUNCTUATION ""
int i;
#define MAX_ARGS 8
#define ARG_LEN 512
char arg[MAX_ARGS][ARG_LEN];
for (i = 0; i < MAX_ARGS; i++)
command = COM_ParseToken(command, arg[i], ARG_LEN, TOKENIZE_PUNCTUATION);
buffer[0] = '\0'; buffer[0] = '\0';
if (!strcmp(arg[0], "status")) if (!strcmp(arg[0], "status"))
{ {
sprintf(buffer, "%i connections\n", qtv->cluster->numservers);
strcat(buffer, "\n"); strcat(buffer, "\n");
strcat(buffer, "Server: "); strcat(buffer, "Selected server: ");
strcat(buffer, qtv->server); strcat(buffer, qtv->server);
strcat(buffer, "\n"); strcat(buffer, "\n");
if (qtv->file) if (qtv->file)
@ -326,13 +522,6 @@ char *Rcon_Command(sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qbo
strcat(buffer, arg[0]); strcat(buffer, arg[0]);
strcat(buffer, ")\n"); strcat(buffer, ")\n");
} }
if (qtv->qwdsocket != INVALID_SOCKET)
{
strcat(buffer, "Listening for qwcl (");
sprintf(arg[0], "%i", qtv->qwlistenportnum);
strcat(buffer, arg[0]);
strcat(buffer, ")\n");
}
if (qtv->bsp) if (qtv->bsp)
{ {
@ -342,55 +531,19 @@ char *Rcon_Command(sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qbo
} }
strcat(buffer, "Options:\n"); strcat(buffer, "Options:\n");
if (qtv->chokeonnotupdated) if (qtv->cluster->chokeonnotupdated)
strcat(buffer, " Choke\n"); strcat(buffer, " Choke\n");
if (qtv->lateforward) if (qtv->cluster->lateforward)
strcat(buffer, " Late forwarding\n"); strcat(buffer, " Late forwarding\n");
if (!qtv->notalking) if (!qtv->cluster->notalking)
strcat(buffer, " Talking allowed\n"); strcat(buffer, " Talking allowed\n");
if (qtv->cluster->nobsp)
strcat(buffer, " No BSP loading\n");
strcat(buffer, "\n"); strcat(buffer, "\n");
return buffer; return buffer;
} }
else if (!strcmp(arg[0], "option"))
{
if (!*arg[1])
return "option X Y\nWhere X is choke/late/talking/hostname/nobsp/master/maxviewers/maxproxies and Y is (mostly) 0/1\n";
if (!strcmp(arg[1], "choke"))
qtv->chokeonnotupdated = !!atoi(arg[2]);
else if (!strcmp(arg[1], "late"))
qtv->lateforward = !!atoi(arg[2]);
else if (!strcmp(arg[1], "talking"))
qtv->notalking = !atoi(arg[2]);
else if (!strcmp(arg[1], "nobsp"))
{
qtv->nobsp = !!atoi(arg[2]);
return "nobsp will change at start of next map\n";
}
else if (!strcmp(arg[1], "hostname"))
{
strncpy(qtv->hostname, arg[2], sizeof(qtv->hostname)-1);
return "hostname will change at start of next map\n"; //I'm too lazy to alter the serverinfo here.
}
else if (!strcmp(arg[1], "master"))
{
netadr_t addr;
strncpy(qtv->master, arg[2], sizeof(qtv->master)-1);
qtv->mastersendtime = qtv->curtime;
if (NET_StringToAddr(arg[1], &addr))
NET_SendPacket (qtv->qwdsocket, 1, "k", addr);
}
else if (!strcmp(arg[1], "maxviewers"))
qtv->maxviewers = atoi(arg[2]);
else if (!strcmp(arg[1], "maxproxies"))
qtv->maxproxies = atoi(arg[2]);
else
return "Option not recognised\n";
return "Set\n";
}
else if (!strcmp(arg[0], "connect")) else if (!strcmp(arg[0], "connect"))
{ {
if (!*arg[1]) if (!*arg[1])
@ -410,7 +563,7 @@ char *Rcon_Command(sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qbo
if (*arg[1] == '\\' || *arg[1] == '/' || strstr(arg[1], "..") || arg[1][1] == ':') if (*arg[1] == '\\' || *arg[1] == '/' || strstr(arg[1], "..") || arg[1][1] == ':')
return "Absolute paths are prohibited.\n"; return "Absolute paths are prohibited.\n";
memmove(arg[1]+5, arg[1], sizeof(arg[1])-6); memmove(arg[1]+5, arg[1], ARG_LEN-6);
strncpy(arg[1], "file:", 5); strncpy(arg[1], "file:", 5);
if (QTV_Connect(qtv, arg[1])) if (QTV_Connect(qtv, arg[1]))
return "File opened successfully\n"; return "File opened successfully\n";
@ -438,10 +591,7 @@ char *Rcon_Command(sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qbo
else else
return "not recording to disk\n"; return "not recording to disk\n";
} }
else if (!strcmp(arg[0], "help"))
{
return "FTEQTV proxy version "VERSION"\nValid commands: connect, file, status, option, mvdport, port, reconnect\n";
}
else if (!strcmp(arg[0], "reconnect")) else if (!strcmp(arg[0], "reconnect"))
{ {
if (QTV_Connect(qtv, qtv->server)) if (QTV_Connect(qtv, qtv->server))
@ -483,64 +633,72 @@ char *Rcon_Command(sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qbo
} }
} }
else if (!strcmp(arg[0], "ping")) else if (!strcmp(arg[0], "exec"))
{ {
netadr_t addr; FILE *f;
if (NET_StringToAddr(arg[1], &addr)) char line[512], *res;
{
NET_SendPacket (qtv->qwdsocket, 1, "k", addr);
return "pinged\n";
}
return "couldn't resolve\n";
}
else if (!strcmp(arg[0], "port") || !strcmp(arg[0], "udpport"))
{
int news;
int newp = atoi(arg[1]);
if (!localcommand) if (!localcommand)
return "Changing udp port is not allowed via rcon\n"; if (*arg[1] == '\\' || *arg[1] == '/' || strstr(arg[1], "..") || arg[1][1] == ':')
return "Absolute paths are prohibited.\n";
if (!newp) f = fopen(arg[1], "rt");
if (!f)
{ {
if (qtv->listenmvd != INVALID_SOCKET) snprintf(buffer, sizeofbuffer, "Couldn't exec \"%s\"\n", arg[1]);
{ return buffer;
closesocket(qtv->listenmvd);
qtv->listenmvd = INVALID_SOCKET;
qtv->tcplistenportnum = 0;
return "Closed udp port\n";
}
return "udp port was already closed\n";
} }
else else
{ {
news = QW_InitUDPSocket(newp); while(fgets(line, sizeof(line)-1, f))
if (news != INVALID_SOCKET)
{ {
qtv->mastersendtime = qtv->curtime; res = Rcon_Command(qtv->cluster, qtv, line, buffer, sizeofbuffer, localcommand);
closesocket(qtv->qwdsocket); Sys_Printf(qtv->cluster, "%s", res);
qtv->qwdsocket = news;
qtv->qwlistenportnum = newp;
return "Opened udp port\n";
} }
else fclose(f);
return "Failed to open udp port\n"; return "Execed\n";
} }
} }
else if (!strcmp(arg[0], "password")) else
{ {
if (!localcommand) return NULL;
return "Rejecting rcon password change.\n";
strncpy(qtv->password, arg[1], sizeof(qtv->password)-1);
return "Password changed.\n";
} }
else }
return "Unrecognised command.\n";
char *Rcon_Command(cluster_t *cluster, sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qboolean localcommand)
{
#define TOKENIZE_PUNCTUATION ""
int i;
char arg[MAX_ARGS][ARG_LEN];
char *argptrs[MAX_ARGS];
char *result;
for (i = 0; i < MAX_ARGS; i++)
{
command = COM_ParseToken(command, arg[i], ARG_LEN, TOKENIZE_PUNCTUATION);
argptrs[i] = arg[i];
}
if (qtv)
{ //if there is a specific connection targetted
result = Server_Rcon_Dispatch(qtv, argptrs, buffer, sizeofbuffer, localcommand);
if (result)
return result;
}
else if (cluster->numservers == 1)
{ //if it's a single-connection proxy
result = Server_Rcon_Dispatch(cluster->servers, argptrs, buffer, sizeofbuffer, localcommand);
if (result)
return result;
}
result = Cluster_Rcon_Dispatch(cluster, argptrs, buffer, sizeofbuffer, localcommand);
if (result)
return result;
snprintf(buffer, sizeofbuffer, "Command \"%s\" not recognised.\n", arg[0]);
return buffer;
} }

View File

@ -20,6 +20,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "qtv.h" #include "qtv.h"
#define RECONNECT_TIME (1000*30)
qboolean NET_StringToAddr (char *s, netadr_t *sadr) qboolean NET_StringToAddr (char *s, netadr_t *sadr)
{ {
@ -216,13 +218,21 @@ qboolean Net_ConnectToServer(sv_t *qtv, char *ip)
fseek(qtv->file, 0, SEEK_SET); fseek(qtv->file, 0, SEEK_SET);
return true; return true;
} }
printf("Unable to open file %s\n", ip+5); Sys_Printf(qtv->cluster, "Unable to open file %s\n", ip+5);
return false; return false;
} }
if (!NET_StringToAddr(ip, &qtv->serveraddress)) qtv->nextconnectattemp = qtv->curtime + RECONNECT_TIME; //wait half a minuite before trying to reconnect
if (strncmp(ip, "tcp:", 4))
{ {
printf("Unable to resolve %s\n", ip); Sys_Printf(qtv->cluster, "Unknown source type %s\n", ip);
return false;
}
if (!NET_StringToAddr(ip+4, &qtv->serveraddress))
{
Sys_Printf(qtv->cluster, "Unable to resolve %s\n", ip);
return false; return false;
} }
qtv->sourcesock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); qtv->sourcesock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
@ -238,19 +248,22 @@ qboolean Net_ConnectToServer(sv_t *qtv, char *ip)
return false; return false;
} }
if (connect(qtv->sourcesock, (struct sockaddr *)&qtv->serveraddress, sizeof(qtv->serveraddress)) == INVALID_SOCKET) if (ioctlsocket (qtv->sourcesock, FIONBIO, &nonblocking) == -1)
{ {
closesocket(qtv->sourcesock); closesocket(qtv->sourcesock);
qtv->sourcesock = INVALID_SOCKET; qtv->sourcesock = INVALID_SOCKET;
return false; return false;
} }
if (ioctlsocket (qtv->sourcesock, FIONBIO, &nonblocking) == -1) if (connect(qtv->sourcesock, (struct sockaddr *)&qtv->serveraddress, sizeof(qtv->serveraddress)) == INVALID_SOCKET)
{
if (qerrno != EWOULDBLOCK)
{ {
closesocket(qtv->sourcesock); closesocket(qtv->sourcesock);
qtv->sourcesock = INVALID_SOCKET; qtv->sourcesock = INVALID_SOCKET;
return false; return false;
} }
}
return true; return true;
} }
@ -264,7 +277,7 @@ void Net_FindProxies(sv_t *qtv)
if (sock == INVALID_SOCKET) if (sock == INVALID_SOCKET)
return; return;
if (qtv->numproxies >= qtv->maxproxies && qtv->maxproxies) if (qtv->numproxies >= qtv->cluster->maxproxies && qtv->cluster->maxproxies)
{ {
const char buffer[] = {dem_all, 1, 'P','r','o','x','y',' ','i','s',' ','f','u','l','l','.'}; const char buffer[] = {dem_all, 1, 'P','r','o','x','y',' ','i','s',' ','f','u','l','l','.'};
send(sock, buffer, strlen(buffer), 0); send(sock, buffer, strlen(buffer), 0);
@ -353,7 +366,7 @@ void CheckMVDConsistancy(unsigned char *buffer, int pos, int size)
*/ */
} }
void Net_TryFlushProxyBuffer(oproxy_t *prox) void Net_TryFlushProxyBuffer(cluster_t *cluster, oproxy_t *prox)
{ {
char *buffer; char *buffer;
int length; int length;
@ -376,7 +389,7 @@ void Net_TryFlushProxyBuffer(oproxy_t *prox)
// CheckMVDConsistancy(prox->buffer, prox->bufferpos, prox->buffersize); // CheckMVDConsistancy(prox->buffer, prox->bufferpos, prox->buffersize);
if (bufpos+length > MAX_PROXY_BUFFER) if (bufpos+length > MAX_PROXY_BUFFER)
printf("oversize flush\n"); Sys_Printf(cluster, "oversize flush\n");
if (prox->file) if (prox->file)
length = fwrite(buffer, 1, length, prox->file); length = fwrite(buffer, 1, length, prox->file);
@ -398,13 +411,13 @@ void Net_TryFlushProxyBuffer(oproxy_t *prox)
} }
} }
void Net_ProxySend(oproxy_t *prox, char *buffer, int length) void Net_ProxySend(cluster_t *cluster, oproxy_t *prox, char *buffer, int length)
{ {
int wrap; int wrap;
if (prox->buffersize-prox->bufferpos + length > MAX_PROXY_BUFFER) if (prox->buffersize-prox->bufferpos + length > MAX_PROXY_BUFFER)
{ {
Net_TryFlushProxyBuffer(prox); //try flushing Net_TryFlushProxyBuffer(cluster, prox); //try flushing
if (prox->buffersize-prox->bufferpos + length > MAX_PROXY_BUFFER) //damn, still too big. if (prox->buffersize-prox->bufferpos + length > MAX_PROXY_BUFFER) //damn, still too big.
{ //they're too slow. hopefully it was just momentary lag { //they're too slow. hopefully it was just momentary lag
prox->flushing = true; prox->flushing = true;
@ -438,7 +451,7 @@ void Net_ProxySend(oproxy_t *prox, char *buffer, int length)
#endif #endif
} }
void Prox_SendMessage(oproxy_t *prox, char *buf, int length, int dem_type, unsigned int playermask) void Prox_SendMessage(cluster_t *cluster, oproxy_t *prox, char *buf, int length, int dem_type, unsigned int playermask)
{ {
netmsg_t msg; netmsg_t msg;
char tbuf[16]; char tbuf[16];
@ -450,9 +463,9 @@ void Prox_SendMessage(oproxy_t *prox, char *buf, int length, int dem_type, unsig
WriteLong(&msg, playermask); WriteLong(&msg, playermask);
Net_ProxySend(prox, msg.data, msg.cursize); Net_ProxySend(cluster, prox, msg.data, msg.cursize);
Net_ProxySend(prox, buf, length); Net_ProxySend(cluster, prox, buf, length);
} }
void Prox_SendPlayerStats(sv_t *qtv, oproxy_t *prox) void Prox_SendPlayerStats(sv_t *qtv, oproxy_t *prox)
@ -502,31 +515,31 @@ void Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox)
prox->flushing = false; prox->flushing = false;
BuildServerData(qtv, &msg, true); BuildServerData(qtv, &msg, true, 0);
Prox_SendMessage(prox, msg.data, msg.cursize, dem_read, (unsigned)-1); Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0; msg.cursize = 0;
for (prespawn = 0;prespawn >= 0;) for (prespawn = 0;prespawn >= 0;)
{ {
prespawn = SendList(qtv, prespawn, qtv->soundlist, svc_soundlist, &msg); prespawn = SendList(qtv, prespawn, qtv->soundlist, svc_soundlist, &msg);
Prox_SendMessage(prox, msg.data, msg.cursize, dem_read, (unsigned)-1); Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0; msg.cursize = 0;
} }
for (prespawn = 0;prespawn >= 0;) for (prespawn = 0;prespawn >= 0;)
{ {
prespawn = SendList(qtv, prespawn, qtv->modellist, svc_modellist, &msg); prespawn = SendList(qtv, prespawn, qtv->modellist, svc_modellist, &msg);
Prox_SendMessage(prox, msg.data, msg.cursize, dem_read, (unsigned)-1); Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0; msg.cursize = 0;
} }
Net_TryFlushProxyBuffer(prox); //that should be enough data to fill a packet. Net_TryFlushProxyBuffer(qtv->cluster, prox); //that should be enough data to fill a packet.
for(prespawn = 0;prespawn>=0;) for(prespawn = 0;prespawn>=0;)
{ {
prespawn = Prespawn(qtv, 0, &msg, prespawn); prespawn = Prespawn(qtv, 0, &msg, prespawn);
Prox_SendMessage(prox, msg.data, msg.cursize, dem_read, (unsigned)-1); Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0; msg.cursize = 0;
} }
@ -534,30 +547,30 @@ void Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox)
//we do need to send entity states. //we do need to send entity states.
Prox_SendInitialEnts(qtv, prox, &msg); Prox_SendInitialEnts(qtv, prox, &msg);
Prox_SendMessage(prox, msg.data, msg.cursize, dem_read, (unsigned)-1); Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0; msg.cursize = 0;
WriteByte(&msg, svc_stufftext); WriteByte(&msg, svc_stufftext);
WriteString(&msg, "skins\n"); WriteString(&msg, "skins\n");
Prox_SendMessage(prox, msg.data, msg.cursize, dem_read, (unsigned)-1); Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0; msg.cursize = 0;
Net_TryFlushProxyBuffer(prox); Net_TryFlushProxyBuffer(qtv->cluster, prox);
Prox_SendPlayerStats(qtv, prox); Prox_SendPlayerStats(qtv, prox);
Net_TryFlushProxyBuffer(prox); Net_TryFlushProxyBuffer(qtv->cluster, prox);
if (!qtv->lateforward) if (!qtv->cluster->lateforward)
Net_ProxySend(prox, qtv->buffer, qtv->buffersize); //send all the info we've not yet processed. Net_ProxySend(qtv->cluster, prox, qtv->buffer, qtv->buffersize); //send all the info we've not yet processed.
if (prox->flushing) if (prox->flushing)
{ {
printf("Connection data is too big, dropping proxy client\n"); Sys_Printf(qtv->cluster, "Connection data is too big, dropping proxy client\n");
prox->drop = true; //this is unfortunate... prox->drop = true; //this is unfortunate...
} }
else else
Net_TryFlushProxyBuffer(prox); Net_TryFlushProxyBuffer(qtv->cluster, prox);
} }
void Net_ForwardStream(sv_t *qtv, char *buffer, int length) void Net_ForwardStream(sv_t *qtv, char *buffer, int length)
@ -604,7 +617,7 @@ void Net_ForwardStream(sv_t *qtv, char *buffer, int length)
} }
else else
{ {
Net_TryFlushProxyBuffer(prox); //try and flush it. Net_TryFlushProxyBuffer(qtv->cluster, prox); //try and flush it.
continue; continue;
} }
} }
@ -613,12 +626,11 @@ void Net_ForwardStream(sv_t *qtv, char *buffer, int length)
continue; continue;
//add the new data //add the new data
Net_ProxySend(prox, buffer, length); Net_ProxySend(qtv->cluster, prox, buffer, length);
//and try to send it. Net_TryFlushProxyBuffer(qtv->cluster, prox);
Net_TryFlushProxyBuffer(prox); // Net_TryFlushProxyBuffer(qtv->cluster, prox);
Net_TryFlushProxyBuffer(prox); // Net_TryFlushProxyBuffer(qtv->cluster, prox);
Net_TryFlushProxyBuffer(prox);
} }
} }
@ -648,7 +660,7 @@ qboolean Net_ReadStream(sv_t *qtv)
if (read > 0) if (read > 0)
{ {
qtv->buffersize += read; qtv->buffersize += read;
if (!qtv->lateforward) if (!qtv->cluster->lateforward)
Net_ForwardStream(qtv, buffer, read); Net_ForwardStream(qtv, buffer, read);
} }
else else
@ -657,7 +669,12 @@ qboolean Net_ReadStream(sv_t *qtv)
{ {
if (qtv->sourcesock != INVALID_SOCKET) if (qtv->sourcesock != INVALID_SOCKET)
{ {
printf("Error: source socket error %i\n", qerrno); int err;
err = qerrno;
if (qerrno)
Sys_Printf(qtv->cluster, "Error: source socket error %i\n", qerrno);
else
Sys_Printf(qtv->cluster, "Error: server disconnected\n");
closesocket(qtv->sourcesock); closesocket(qtv->sourcesock);
qtv->sourcesock = INVALID_SOCKET; qtv->sourcesock = INVALID_SOCKET;
} }
@ -666,7 +683,7 @@ qboolean Net_ReadStream(sv_t *qtv)
fclose(qtv->file); fclose(qtv->file);
qtv->file = NULL; qtv->file = NULL;
} }
printf("Read error or eof\n"); Sys_Printf(qtv->cluster, "Read error or eof to %s\n", qtv->server);
return false; return false;
} }
} }
@ -688,7 +705,7 @@ unsigned int Sys_Milliseconds(void)
return ((unsigned)tv.tv_sec)*1000 + (((unsigned)tv.tv_usec)/1000); return ((unsigned)tv.tv_sec)*1000 + (((unsigned)tv.tv_usec)/1000);
#endif #endif
} }
/*
void NetSleep(sv_t *tv) void NetSleep(sv_t *tv)
{ {
int m; int m;
@ -741,7 +758,7 @@ void NetSleep(sv_t *tv)
if (tv->inputlength) if (tv->inputlength)
{ {
tv->commandinput[tv->inputlength] = '\0'; tv->commandinput[tv->inputlength] = '\0';
result = Rcon_Command(tv, tv->commandinput, buffer, sizeof(buffer), true); result = Rcon_Command(tv->cluster, tv, tv->commandinput, buffer, sizeof(buffer), true);
printf("%s", result); printf("%s", result);
tv->inputlength = 0; tv->inputlength = 0;
tv->commandinput[0] = '\0'; tv->commandinput[0] = '\0';
@ -787,6 +804,7 @@ void NetSleep(sv_t *tv)
} }
#endif #endif
} }
*/
void Trim(char *s) void Trim(char *s)
{ {
@ -816,7 +834,7 @@ qboolean QTV_Connect(sv_t *qtv, char *serverurl)
*qtv->serverinfo = '\0'; *qtv->serverinfo = '\0';
Info_SetValueForStarKey(qtv->serverinfo, "*version", "FTEQTV", sizeof(qtv->serverinfo)); Info_SetValueForStarKey(qtv->serverinfo, "*version", "FTEQTV", sizeof(qtv->serverinfo));
Info_SetValueForStarKey(qtv->serverinfo, "*qtv", VERSION, sizeof(qtv->serverinfo)); Info_SetValueForStarKey(qtv->serverinfo, "*qtv", VERSION, sizeof(qtv->serverinfo));
Info_SetValueForStarKey(qtv->serverinfo, "hostname", qtv->hostname, sizeof(qtv->serverinfo)); Info_SetValueForStarKey(qtv->serverinfo, "hostname", qtv->cluster->hostname, sizeof(qtv->serverinfo));
Info_SetValueForStarKey(qtv->serverinfo, "maxclients", "99", sizeof(qtv->serverinfo)); Info_SetValueForStarKey(qtv->serverinfo, "maxclients", "99", sizeof(qtv->serverinfo));
if (!strncmp(qtv->server, "file:", 5)) if (!strncmp(qtv->server, "file:", 5))
Info_SetValueForStarKey(qtv->serverinfo, "server", "file", sizeof(qtv->serverinfo)); Info_SetValueForStarKey(qtv->serverinfo, "server", "file", sizeof(qtv->serverinfo));
@ -827,24 +845,24 @@ qboolean QTV_Connect(sv_t *qtv, char *serverurl)
if (!Net_ConnectToServer(qtv, qtv->server)) if (!Net_ConnectToServer(qtv, qtv->server))
{ {
printf("Couldn't connect (%s)\n", qtv->server); Sys_Printf(qtv->cluster, "Couldn't connect (%s)\n", qtv->server);
return false; return false;
} }
printf("Connected\n"); Sys_Printf(qtv->cluster, "Connected\n");
if (qtv->sourcesock == INVALID_SOCKET) if (qtv->sourcesock == INVALID_SOCKET)
{ {
qtv->parsetime = Sys_Milliseconds(); qtv->parsetime = Sys_Milliseconds();
printf("Playing from file\n"); Sys_Printf(qtv->cluster, "Playing from file\n");
} }
else else
{ {
qtv->parsetime = Sys_Milliseconds() + BUFFERTIME*1000; qtv->parsetime = Sys_Milliseconds() + BUFFERTIME*1000;
printf("Buffering for %i seconds\n", BUFFERTIME); Sys_Printf(qtv->cluster, "Buffering for %i seconds\n", BUFFERTIME);
} }
return true; return true;
} }
/*
void QTV_Run(sv_t *qtv) void QTV_Run(sv_t *qtv)
{ {
int lengthofs; int lengthofs;
@ -1112,3 +1130,424 @@ int main(int argc, char **argv)
return 0; return 0;
} }
*/
void QTV_Run(sv_t *qtv)
{
int lengthofs;
unsigned int length;
unsigned char *buffer;
int oldcurtime;
int packettime;
//we will read out as many packets as we can until we're up to date
//note: this can cause real issues when we're overloaded for any length of time
//each new packet comes with a leading msec byte (msecs from last packet)
//then a type, an optional destination mask, and a 4byte size.
//the 4 byte size is probably excessive, a short would do.
//some of the types have thier destination mask encoded inside the type byte, yielding 8 types, and 32 max players.
//if we've no got enough data to read a new packet, we print a message and wait an extra two seconds. this will add a pause, connected clients will get the same pause, and we'll just try to buffer more of the game before playing.
//we'll stay 2 secs behind when the tcp stream catches up, however. This could be bad especially with long up-time.
//All timings are in msecs, which is in keeping with the mvd times, but means we might have issues after 72 or so days.
//the following if statement will reset the parse timer. It might cause the game to play too soon, the buffersize checks in the rest of the function will hopefully put it back to something sensible.
oldcurtime = qtv->curtime;
qtv->curtime = Sys_Milliseconds();
if (oldcurtime > qtv->curtime)
{
Sys_Printf(qtv->cluster, "Time wrapped\n");
qtv->parsetime = qtv->curtime;
}
if (qtv->sourcesock == INVALID_SOCKET && !qtv->file)
{
if (qtv->curtime >= qtv->nextconnectattemp || qtv->curtime < qtv->nextconnectattemp - RECONNECT_TIME*2)
if (!QTV_Connect(qtv, qtv->server))
{
return;
}
}
Net_FindProxies(qtv); //look for any other proxies wanting to muscle in on the action.
if (qtv->file || qtv->sourcesock != INVALID_SOCKET)
{
if (!Net_ReadStream(qtv))
{ //if we have an error reading it
//if it's valid, give up
//what should we do here?
//obviously, we need to keep reading the stream to keep things smooth
}
}
while (qtv->curtime >= qtv->parsetime)
{
if (qtv->buffersize < 2)
{ //not enough stuff to play.
if (qtv->parsetime < qtv->curtime)
{
qtv->parsetime = qtv->curtime + 2*1000; //add two seconds
if (qtv->file || qtv->sourcesock != INVALID_SOCKET)
Sys_Printf(qtv->cluster, "Not enough buffered\n");
}
break;
}
buffer = qtv->buffer;
switch (qtv->buffer[1]&dem_mask)
{
case dem_set:
if (qtv->buffersize < 10)
{ //not enough stuff to play.
qtv->parsetime = qtv->curtime + 2*1000; //add two seconds
if (qtv->file || qtv->sourcesock != INVALID_SOCKET)
Sys_Printf(qtv->cluster, "Not enough buffered\n");
continue;
}
qtv->parsetime += buffer[0]; //well this was pointless
memmove(qtv->buffer, qtv->buffer+10, qtv->buffersize-(10));
qtv->buffersize -= 10;
continue;
case dem_multiple:
lengthofs = 6;
break;
default:
lengthofs = 2;
break;
}
if (qtv->buffersize < lengthofs+4)
{ //the size parameter doesn't fit.
if (qtv->file || qtv->sourcesock != INVALID_SOCKET)
Sys_Printf(qtv->cluster, "Not enough buffered\n");
qtv->parsetime = qtv->curtime + 2*1000; //add two seconds
break;
}
length = (buffer[lengthofs]<<0) + (buffer[lengthofs+1]<<8) + (buffer[lengthofs+2]<<16) + (buffer[lengthofs+3]<<24);
if (length > 1450)
{ //FIXME: THIS SHOULDN'T HAPPEN!
//Blame the upstream proxy!
Sys_Printf(qtv->cluster, "Warning: corrupt input packet (%i) too big! Flushing and reconnecting!\n", length);
if (qtv->file)
{
fclose(qtv->file);
qtv->file = NULL;
}
else
{
closesocket(qtv->sourcesock);
qtv->sourcesock = INVALID_SOCKET;
}
qtv->buffersize = 0;
break;
}
if (length+lengthofs+4 > qtv->buffersize)
{
if (qtv->file || qtv->sourcesock != INVALID_SOCKET)
Sys_Printf(qtv->cluster, "Not enough buffered\n");
qtv->parsetime = qtv->curtime + 2*1000; //add two seconds
break; //can't parse it yet.
}
qtv->nextpackettime = qtv->parsetime+buffer[0];
if (qtv->nextpackettime < qtv->curtime)
{
if (qtv->cluster->lateforward)
Net_ForwardStream(qtv, qtv->buffer, lengthofs+4+length);
switch(qtv->buffer[1]&dem_mask)
{
case dem_multiple:
ParseMessage(qtv, buffer+lengthofs+4, length, qtv->buffer[1]&dem_mask, (buffer[lengthofs-4]<<0) + (buffer[lengthofs+3]<<8) + (buffer[lengthofs-2]<<16) + (buffer[lengthofs-1]<<24));
break;
case dem_single:
case dem_stats:
ParseMessage(qtv, buffer+lengthofs+4, length, qtv->buffer[1]&dem_mask, 1<<(qtv->buffer[1]>>3));
break;
case dem_read:
case dem_all:
ParseMessage(qtv, buffer+lengthofs+4, length, qtv->buffer[1]&dem_mask, 0xffffffff);
break;
default:
Sys_Printf(qtv->cluster, "Message type %i\n", qtv->buffer[1]&dem_mask);
break;
}
qtv->oldpackettime = qtv->curtime;
packettime = buffer[0];
if (qtv->buffersize)
{ //svc_disconnect can flush our input buffer (to prevent the EndOfDemo part from interfering)
memmove(qtv->buffer, qtv->buffer+lengthofs+4+length, qtv->buffersize-(lengthofs+length+4));
qtv->buffersize -= lengthofs+4+length;
}
if (qtv->file)
Net_ReadStream(qtv);
qtv->parsetime += packettime;
}
else
break;
}
}
void Cluster_Run(cluster_t *cluster)
{
sv_t *sv;
int m;
struct timeval timeout;
fd_set socketset;
FD_ZERO(&socketset);
m = 0;
if (cluster->qwdsocket != INVALID_SOCKET)
{
FD_SET(cluster->qwdsocket, &socketset);
if (cluster->qwdsocket >= m)
m = cluster->qwdsocket+1;
}
/*
for (sv = cluster->servers; sv; sv = sv->next)
{
if (sv->sourcesock != INVALID_SOCKET)
{
FD_SET(sv->sourcesock, &socketset);
if (sv->sourcesock >= m)
m = sv->sourcesock+1;
}
}
*/
#ifndef _WIN32
#ifndef STDIN
#define STDIN 0
#endif
FD_SET(STDIN, &socketset);
if (STDIN >= m)
m = STDIN+1;
#endif
timeout.tv_sec = 100/1000;
timeout.tv_usec = (100%1000)*1000;
m = select(m, &socketset, NULL, NULL, &timeout);
#ifdef _WIN32
for (;;)
{
char buffer[8192];
char *result;
char c;
if (!kbhit())
break;
c = getch();
if (c == '\n' || c == '\r')
{
Sys_Printf(cluster, "\n");
if (cluster->inputlength)
{
cluster->commandinput[cluster->inputlength] = '\0';
result = Rcon_Command(cluster, NULL, cluster->commandinput, buffer, sizeof(buffer), true);
Sys_Printf(cluster, "%s", result);
cluster->inputlength = 0;
cluster->commandinput[0] = '\0';
}
}
else if (c == '\b')
{
if (cluster->inputlength > 0)
{
Sys_Printf(cluster, "%c", c);
Sys_Printf(cluster, " ", c);
Sys_Printf(cluster, "%c", c);
cluster->inputlength--;
cluster->commandinput[cluster->inputlength] = '\0';
}
}
else
{
Sys_Printf(cluster, "%c", c);
if (cluster->inputlength < sizeof(cluster->commandinput)-1)
{
cluster->commandinput[cluster->inputlength++] = c;
cluster->commandinput[cluster->inputlength] = '\0';
}
}
}
#else
if (FD_ISSET(STDIN, &socketset))
{
char buffer[8192];
char *result;
cluster->inputlength = read (0, cluster->commandinput, sizeof(cluster->commandinput));
if (cluster->inputlength >= 1)
{
cluster->commandinput[cluster->inputlength-1] = 0; // rip off the /n and terminate
if (cluster->inputlength)
{
cluster->commandinput[cluster->inputlength] = '\0';
result = Rcon_Command(cluster, NULL, cluster->commandinput, buffer, sizeof(buffer), true);
printf("%s", result);
cluster->inputlength = 0;
cluster->commandinput[0] = '\0';
}
}
}
#endif
for (sv = cluster->servers; sv; sv = sv->next)
{
QTV_Run(sv);
}
QW_UpdateUDPStuff(cluster);
}
sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, qboolean force)
{
sv_t *qtv = malloc(sizeof(sv_t));
memset(qtv, 0, sizeof(*qtv));
//set up a default config
qtv->tcplistenportnum = PROX_DEFAULTLISTENPORT;
strcpy(qtv->server, PROX_DEFAULTSERVER);
qtv->listenmvd = INVALID_SOCKET;
qtv->sourcesock = INVALID_SOCKET;
qtv->cluster = cluster;
qtv->next = cluster->servers;
if (!QTV_Connect(qtv, server) && !force)
{
free(qtv);
return NULL;
}
cluster->servers = qtv;
cluster->numservers++;
return qtv;
}
void DoCommandLine(cluster_t *cluster, int argc, char **argv)
{
int i;
char commandline[8192];
char *start, *end, *result;
char buffer[8192];
commandline[0] = '\0';
//build a block of strings.
for (i = 1; i < argc; i++)
{
strcat(commandline, argv[i]);
strcat(commandline, " ");
}
strcat(commandline, "+");
start = commandline;
while(start)
{
end = strchr(start+1, '+');
if (end)
*end = '\0';
if (start[1])
{
result = Rcon_Command(cluster, NULL, start+1, buffer, sizeof(buffer), true);
Sys_Printf(cluster, "%s", result);
}
start = end;
}
}
int main(int argc, char **argv)
{
cluster_t cluster;
#ifdef _WIN32
{
WSADATA discard;
WSAStartup(MAKEWORD(2,0), &discard);
}
#endif
memset(&cluster, 0, sizeof(cluster));
cluster.qwdsocket = INVALID_SOCKET;
cluster.qwlistenportnum = 0;
strcpy(cluster.hostname, DEFAULT_HOSTNAME);
DoCommandLine(&cluster, argc, argv);
if (!cluster.numservers)
{ //probably running on a home user's computer
if (cluster.qwdsocket == INVALID_SOCKET && !cluster.qwlistenportnum)
{
cluster.qwdsocket = QW_InitUDPSocket(cluster.qwlistenportnum = 27599);
if (cluster.qwdsocket != INVALID_SOCKET)
Sys_Printf(&cluster, "opened port %i\n", cluster.qwlistenportnum);
}
Sys_Printf(&cluster, "\nWelcome to FTEQTV\nPlease type \nconnect server:ip\nto connect to a server.\n\n");
}
while (!cluster.wanttoexit)
Cluster_Run(&cluster);
return 0;
}
void Sys_Printf(cluster_t *cluster, char *fmt, ...)
{
va_list argptr;
char string[2024];
va_start (argptr, fmt);
vsnprintf (string, sizeof(string), fmt,argptr);
va_end (argptr);
printf("%s", string);
}