From dad5de099b9168cb4e048f0dd72afe393ee9da9c Mon Sep 17 00:00:00 2001 From: Spoike Date: Fri, 7 Oct 2005 02:02:15 +0000 Subject: [PATCH] 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 --- fteqtv/bsp.c | 7 +- fteqtv/netchan.c | 16 +- fteqtv/parse.c | 139 ++++++--- fteqtv/qtv.h | 88 ++++-- fteqtv/qw.c | 769 +++++++++++++++++++++++++++++++---------------- fteqtv/rcon.c | 382 ++++++++++++++++------- fteqtv/source.c | 533 +++++++++++++++++++++++++++++--- 7 files changed, 1438 insertions(+), 496 deletions(-) diff --git a/fteqtv/bsp.c b/fteqtv/bsp.c index 5cc0bfc65..3b922d4d4 100644 --- a/fteqtv/bsp.c +++ b/fteqtv/bsp.c @@ -176,7 +176,6 @@ unsigned char *ReadFile_WINDOWSSUCKS(char *gamedir, char *filename, int *size) f = FindInPaks("id1", filename, size); if (!f) { - printf("Couldn't open bsp file\n"); return NULL; } } @@ -193,7 +192,7 @@ unsigned char *ReadFile_WINDOWSSUCKS(char *gamedir, char *filename, int *size) 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 int size; @@ -217,7 +216,7 @@ bsp_t *BSP_LoadModel(char *gamedir, char *bspname) data = ReadFile_WINDOWSSUCKS("id1", bspname, &size); 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; } } @@ -226,7 +225,7 @@ bsp_t *BSP_LoadModel(char *gamedir, char *bspname) header = (dheader_t*)data; if (data[0] != 29) { - printf("BSP not version 29\n", bspname, gamedir); + Sys_Printf(cluster, "BSP not version 29\n", bspname, gamedir); return NULL; } diff --git a/fteqtv/netchan.c b/fteqtv/netchan.c index 84a32c241..a7e283dd8 100644 --- a/fteqtv/netchan.c +++ b/fteqtv/netchan.c @@ -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; ret = sendto(sock, data, length, 0, (struct sockaddr *)adr, sizeof(struct sockaddr_in)); 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 ================ */ -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; 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); // 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 ================ */ -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; char string[8192]; @@ -141,7 +141,7 @@ void Netchan_OutOfBandPrint (SOCKET sock, netadr_t adr, char *format, ...) #endif // _WIN32 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. ================ */ -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; 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_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) chan->cleartime = curtime + send.cursize*chan->rate; diff --git a/fteqtv/parse.c b/fteqtv/parse.c index 93837d095..d27f50b8d 100644 --- a/fteqtv/parse.c +++ b/fteqtv/parse.c @@ -186,18 +186,20 @@ void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playerma case dem_single: case dem_stats: //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->trackplayer>=0) - if ((1<trackplayer)&playermask) - SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type + if (v->server == tv) + if (v->trackplayer>=0) + if ((1<trackplayer)&playermask) + SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type } break; default: //send to all - for (v = tv->viewers; v; v = v->next) + for (v = tv->cluster->viewers; v; v = v->next) { - SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type + if (v->server == tv) + SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type } break; } @@ -224,10 +226,10 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma /*tv->servertime =*/ ReadFloat(m); ReadString(m, tv->mapname, sizeof(tv->mapname)); - printf("Gamedir: %s\n", tv->gamedir); - printf("---------------------\n"); - printf("%s\n", tv->mapname); - printf("---------------------\n"); + Sys_Printf(tv->cluster, "Gamedir: %s\n", tv->gamedir); + Sys_Printf(tv->cluster, "---------------------\n"); + Sys_Printf(tv->cluster, "%s\n", tv->mapname); + Sys_Printf(tv->cluster, "---------------------\n"); // get the movevars 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.entgrav = ReadFloat(m); - for (v = tv->viewers; v; v = v->next) - v->thinksitsconnected = false; + for (v = tv->cluster->viewers; v; v = v->next) + { + if (v->server == tv) + v->thinksitsconnected = false; + } tv->maxents = 0; //clear these memset(tv->modellist, 0, sizeof(tv->modellist)); @@ -273,12 +278,15 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) if (!strcmp(text, "skins\n")) { const char newcmd[10] = {svc_stufftext, 'c', 'm', 'd', ' ', 'n','e','w','\n','\0'}; - tv->servercount++; tv->parsingconnectiondata = false; - for (v = tv->viewers; v; v = v->next) + for (v = tv->cluster->viewers; v; v = v->next) { - SendBufferToViewer(v, newcmd, sizeof(newcmd), true); + if (v->server == tv) + { + v->servercount++; + SendBufferToViewer(v, newcmd, sizeof(newcmd), true); + } } return; } @@ -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. 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 - snprintf(text, sizeof(text), "%s %s", tv->hostname, s); + snprintf(text, sizeof(text), "%s %s", tv->cluster->hostname, s); } else { 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 - 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)); @@ -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); } +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) { char key[64]; @@ -366,10 +390,16 @@ static void ParsePrint(sv_t *tv, netmsg_t *m, int to, unsigned int mask) *t = '['; if (*t == 17) *t = ']'; + if (*t == 29) + *t = '-'; + if (*t == 30) + *t = '-'; + if (*t == 31) + *t = '-'; if (*t == '\a') //doh. :D *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) { 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); @@ -462,7 +492,7 @@ void ParseSpawnStatic(sv_t *tv, netmsg_t *m, int to, unsigned int mask) if (tv->spawnstatic_count == MAX_STATICENTITIES) { 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); @@ -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); } -static void ParsePlayerInfo(sv_t *tv, netmsg_t *m) +static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers) { int flags; int num; 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); if (num >= MAX_CLIENTS) { 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; @@ -544,18 +586,13 @@ static void ParsePacketEntities(sv_t *tv, netmsg_t *m) tv->physicstime = tv->parsetime; - if (tv->chokeonnotupdated) - for (v = tv->viewers; v; v = v->next) + if (tv->cluster->chokeonnotupdated) + for (v = tv->cluster->viewers; v; v = v->next) { - v->chokeme = false; + if (v->server == tv) + 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. for (;;) { @@ -630,7 +667,7 @@ static void ParseUpdatePing(sv_t *tv, netmsg_t *m, int to, unsigned int mask) if (pnum < MAX_CLIENTS) tv->players[pnum].ping = ping; 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); } @@ -645,7 +682,7 @@ static void ParseUpdateFrags(sv_t *tv, netmsg_t *m, int to, unsigned int mask) if (pnum < MAX_CLIENTS) tv->players[pnum].frags = frags; 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); } @@ -668,7 +705,7 @@ static void ParseUpdateStat(sv_t *tv, netmsg_t *m, int to, unsigned int mask) } } 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); } @@ -690,7 +727,7 @@ static void ParseUpdateStatLong(sv_t *tv, netmsg_t *m, int to, unsigned int mask } } 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); } @@ -704,7 +741,7 @@ static void ParseUpdateUserinfo(sv_t *tv, netmsg_t *m) ReadString(m, tv->players[pnum].userinfo, sizeof(tv->players[pnum].userinfo)); 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. { } @@ -722,7 +759,7 @@ static void ParsePacketloss(sv_t *tv, netmsg_t *m, int to, unsigned int mask) if (pnum < MAX_CLIENTS) tv->players[pnum].packetloss = value; 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); } @@ -738,7 +775,7 @@ static void ParseUpdateEnterTime(sv_t *tv, netmsg_t *m, int to, unsigned int mas if (pnum < MAX_CLIENTS) tv->players[pnum].entertime = value; 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); } @@ -881,7 +918,7 @@ static void ParseTempEntity(sv_t *tv, netmsg_t *m, int to, unsigned int mask) ReadShort (m); break; default: - printf("temp entity %i not recognised\n", i); + Sys_Printf(tv->cluster, "temp entity %i not recognised\n", i); return; } @@ -896,7 +933,7 @@ void ParseLightstyle(sv_t *tv, netmsg_t *m) ReadString(m, tv->lightstyle[style].name, sizeof(tv->lightstyle[style].name)); 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. { } @@ -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) { netmsg_t buf; + qboolean clearoldplayers = true; buf.cursize = length; buf.maxsize = length; 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) { - printf("Read past end of parse buffer\n"); + Sys_Printf(tv->cluster, "Read past end of parse buffer\n"); return; } // 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: ParseError(&buf); - printf("ParseMessage: svc_bad\n"); + Sys_Printf(tv->cluster, "ParseMessage: svc_bad\n"); return; case svc_nop: //quakeworld isn't meant to send these. - printf("nop\n"); + Sys_Printf(tv->cluster, "nop\n"); break; 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] case svc_playerinfo: - ParsePlayerInfo(tv, &buf); + ParsePlayerInfo(tv, &buf, clearoldplayers); + clearoldplayers = false; break; //#define svc_nails 43 // [qbyte] num [48 bits] xyzpy 12 12 12 4 8 case svc_chokecount: @@ -1067,10 +1106,10 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) if (tv->bsp) BSP_Free(tv->bsp); - if (tv->nobsp) + if (tv->cluster->nobsp) tv->bsp = NULL; 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; for (i = 2; i < 256; i++) @@ -1094,7 +1133,9 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) break; //#define svc_maxspeed 49 // maxspeed 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: ParseServerinfo(tv, &buf); break; @@ -1106,7 +1147,7 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) default: 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; } } diff --git a/fteqtv/qtv.h b/fteqtv/qtv.h index ce1b70017..fc01321ee 100644 --- a/fteqtv/qtv.h +++ b/fteqtv/qtv.h @@ -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 + + +//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 #include #include @@ -42,6 +49,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #define snprintf _snprintf + #define vsnprintf _vsnprintf #elif defined(__CYGWIN__) @@ -221,6 +229,8 @@ typedef struct { } packet_entities_t; #define MAX_BACK_BUFFERS 16 +typedef struct sv_s sv_t; +typedef struct cluster_s cluster_t; typedef struct viewer_s { qboolean drop; netchan_t netchan; @@ -229,6 +239,8 @@ typedef struct viewer_s { qboolean thinksitsconnected; int delta_frame; + int servercount; + netmsg_t backbuf[MAX_BACK_BUFFERS]; //note data is malloced! int backbuffered; @@ -245,6 +257,12 @@ typedef struct viewer_s { int settime; //the time that we last told the client. float origin[3]; + + sv_t *server; + + int menunum; + int menuop; + int fwdval; //for scrolling up/down the menu using +forward/+back :) } viewer_t; typedef struct oproxy_s { @@ -287,7 +305,6 @@ typedef struct sv_s { unsigned int parsetime; - int servercount; char gamedir[MAX_QPATH]; char mapname[256]; struct { @@ -312,6 +329,7 @@ typedef struct sv_s { filename_t lightstyle[MAX_LIGHTSTYLES]; char serverinfo[MAX_SERVERINFO_STRING]; + char hostname[MAX_QPATH]; playerinfo_t players[MAX_CLIENTS]; filename_t modellist[MAX_MODELS]; @@ -323,10 +341,7 @@ typedef struct sv_s { SOCKET sourcesock; SOCKET listenmvd; //tcp + mvd protocol - SOCKET qwdsocket; //udp + quakeworld protocols - viewer_t *viewers; - int numviewers; oproxy_t *proxies; int numproxies; @@ -336,35 +351,51 @@ typedef struct sv_s { unsigned int curtime; unsigned int oldpackettime; unsigned int nextpackettime; + unsigned int nextconnectattemp; 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; int numinlines; //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 lateforward; qboolean notalking; - char password[256]; - char hostname[256]; - char server[MAX_QPATH]; - char master[MAX_QPATH]; qboolean nobsp; int maxviewers; int maxproxies; -} sv_t; -typedef struct { - sv_t server; -} qtv_t; + boolean wanttoexit; + +} cluster_t; @@ -465,7 +496,7 @@ void ReadString(netmsg_t *b, char *string, int maxlen); #define svc_deltapacketentities 48 // [...] //#define svc_maxspeed 49 // maxspeed 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_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 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); -void QW_UpdateUDPStuff(sv_t *qtv); +void QW_UpdateUDPStuff(cluster_t *qtv); unsigned int Sys_Milliseconds(void); void Prox_SendInitialEnts(sv_t *qtv, oproxy_t *prox, netmsg_t *msg); 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 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 Netchan_Process (netchan_t *chan, netmsg_t *msg); -void Netchan_Transmit (netchan_t *chan, int length, const unsigned char *data); -int SendList(sv_t *qtv, int first, filename_t *list, int svc, netmsg_t *msg); +void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const unsigned char *data); +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); -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); 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); qboolean BSP_Visible(bsp_t *bsp, int leafcount, unsigned short *list); 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 *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 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); diff --git a/fteqtv/qw.c b/fteqtv/qw.c index a5260a3a2..a73d418de 100644 --- a/fteqtv/qw.c +++ b/fteqtv/qw.c @@ -169,38 +169,74 @@ SOCKET QW_InitUDPSocket(int port) 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); WriteLong(msg, PROTOCOL_VERSION); - WriteLong(msg, tv->servercount); - WriteString(msg, tv->gamedir); + WriteLong(msg, servercount); - if (mvd) + 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 - WriteByte(msg, MAX_CLIENTS-1); - WriteString(msg, tv->mapname); + { + WriteString(msg, tv->gamedir); + + if (mvd) + WriteFloat(msg, 0); + else + WriteByte(msg, MAX_CLIENTS-1); + WriteString(msg, tv->mapname); - // get the movevars - WriteFloat(msg, tv->movevars.gravity); - WriteFloat(msg, tv->movevars.stopspeed); - WriteFloat(msg, tv->movevars.maxspeed); - WriteFloat(msg, tv->movevars.spectatormaxspeed); - WriteFloat(msg, tv->movevars.accelerate); - WriteFloat(msg, tv->movevars.airaccelerate); - WriteFloat(msg, tv->movevars.wateraccelerate); - WriteFloat(msg, tv->movevars.friction); - WriteFloat(msg, tv->movevars.waterfriction); - WriteFloat(msg, tv->movevars.entgrav); + // get the movevars + WriteFloat(msg, tv->movevars.gravity); + WriteFloat(msg, tv->movevars.stopspeed); + WriteFloat(msg, tv->movevars.maxspeed); + WriteFloat(msg, tv->movevars.spectatormaxspeed); + WriteFloat(msg, tv->movevars.accelerate); + WriteFloat(msg, tv->movevars.airaccelerate); + WriteFloat(msg, tv->movevars.wateraccelerate); + WriteFloat(msg, tv->movevars.friction); + WriteFloat(msg, tv->movevars.waterfriction); + WriteFloat(msg, tv->movevars.entgrav); - WriteByte(msg, svc_stufftext); - WriteString2(msg, "fullserverinfo \""); - WriteString2(msg, tv->serverinfo); - WriteString(msg, "\"\n"); + WriteByte(msg, svc_stufftext); + WriteString2(msg, "fullserverinfo \""); + WriteString2(msg, tv->serverinfo); + WriteString(msg, "\"\n"); + } } void SendServerData(sv_t *tv, viewer_t *viewer) @@ -209,7 +245,7 @@ void SendServerData(sv_t *tv, viewer_t *viewer) char buffer[1024]; InitNetMsg(&msg, buffer, sizeof(buffer)); - BuildServerData(tv, &msg, false); + BuildServerData(tv, &msg, false, viewer->servercount); 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; } -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; @@ -382,7 +418,7 @@ qboolean ChallengePasses(netadr_t *addr, int challenge) 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; @@ -398,7 +434,7 @@ void NewQWClient(sv_t *qtv, netadr_t *addr, char *connectmessage) if (!ChallengePasses(addr, atoi(challenge))) { - Netchan_OutOfBandPrint(qtv->qwdsocket, *addr, "n" "Bad challenge"); + Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *addr, "n" "Bad challenge"); return; } @@ -407,29 +443,39 @@ void NewQWClient(sv_t *qtv, netadr_t *addr, char *connectmessage) memset(viewer, 0, sizeof(viewer_t)); 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; - qtv->viewers = viewer; + viewer->next = cluster->viewers; + cluster->viewers = viewer; 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)); - 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 *command; 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; } @@ -438,88 +484,105 @@ void QTV_Rcon(sv_t *qtv, char *message, netadr_t *from) command = strchr(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; } - 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; char buffer[8192]; + sv_t *sv; netmsg_t msg; char elem[256]; InitNetMsg(&msg, buffer, sizeof(buffer)); WriteLong(&msg, -1); WriteByte(&msg, 'n'); - WriteString2(&msg, qtv->serverinfo); - WriteString2(&msg, "\n"); + if (cluster->numservers==1) + { //show this server's info + sv = cluster->servers; - for (i = 0;i < MAX_CLIENTS-1; i++) - { - if (!qtv->players[i].active) - continue; - //userid - sprintf(elem, "%i", i); - WriteString2(&msg, elem); - WriteString2(&msg, " "); - - //frags - sprintf(elem, "%i", qtv->players[i].frags); - WriteString2(&msg, elem); - WriteString2(&msg, " "); - - //time (minuites) - sprintf(elem, "%i", 0); - WriteString2(&msg, elem); - WriteString2(&msg, " "); - - //ping - sprintf(elem, "%i", qtv->players[i].ping); - WriteString2(&msg, elem); - WriteString2(&msg, " "); - - //name - Info_ValueForKey(qtv->players[i].userinfo, "name", elem, sizeof(elem)); - WriteString2(&msg, "\""); - WriteString2(&msg, elem); - WriteString2(&msg, "\" "); - - //skin - Info_ValueForKey(qtv->players[i].userinfo, "skin", elem, sizeof(elem)); - WriteString2(&msg, "\""); - WriteString2(&msg, elem); - WriteString2(&msg, "\" "); - WriteString2(&msg, " "); - - //tc - Info_ValueForKey(qtv->players[i].userinfo, "topcolor", elem, sizeof(elem)); - WriteString2(&msg, elem); - WriteString2(&msg, " "); - - //bc - Info_ValueForKey(qtv->players[i].userinfo, "bottomcolor", elem, sizeof(elem)); - WriteString2(&msg, elem); - WriteString2(&msg, " "); - + WriteString2(&msg, sv->serverinfo); WriteString2(&msg, "\n"); + + for (i = 0;i < MAX_CLIENTS-1; i++) + { + if (!sv->players[i].active) + continue; + //userid + sprintf(elem, "%i", i); + WriteString2(&msg, elem); + WriteString2(&msg, " "); + + //frags + sprintf(elem, "%i", sv->players[i].frags); + WriteString2(&msg, elem); + WriteString2(&msg, " "); + + //time (minuites) + sprintf(elem, "%i", 0); + WriteString2(&msg, elem); + WriteString2(&msg, " "); + + //ping + sprintf(elem, "%i", sv->players[i].ping); + WriteString2(&msg, elem); + WriteString2(&msg, " "); + + //name + Info_ValueForKey(sv->players[i].userinfo, "name", elem, sizeof(elem)); + WriteString2(&msg, "\""); + WriteString2(&msg, elem); + WriteString2(&msg, "\" "); + + //skin + Info_ValueForKey(sv->players[i].userinfo, "skin", elem, sizeof(elem)); + WriteString2(&msg, "\""); + WriteString2(&msg, elem); + WriteString2(&msg, "\" "); + WriteString2(&msg, " "); + + //tc + Info_ValueForKey(sv->players[i].userinfo, "topcolor", elem, sizeof(elem)); + WriteString2(&msg, elem); + WriteString2(&msg, " "); + + //bc + Info_ValueForKey(sv->players[i].userinfo, "bottomcolor", elem, sizeof(elem)); + WriteString2(&msg, elem); + WriteString2(&msg, " "); + + 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); - NET_SendPacket(qtv->qwdsocket, msg.cursize, msg.data, *from); - - - //Netchan_OutOfBandPrint(qtv->qwdsocket, *from, "n%s\n", qtv->serverinfo); + NET_SendPacket(cluster, cluster->qwdsocket, msg.cursize, msg.data, *from); } -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]; int i; @@ -529,40 +592,36 @@ void ConnectionlessPacket(sv_t *qtv, netadr_t *from, netmsg_t *m) if (!strncmp(buffer, "rcon ", 5)) { - QTV_Rcon(qtv, buffer+5, from); + QTV_Rcon(cluster, buffer+5, from); return; } if (!strncmp(buffer, "ping", 4)) { //ack - NET_SendPacket (qtv->qwdsocket, 1, "l", *from); + NET_SendPacket (cluster, cluster->qwdsocket, 1, "l", *from); return; } if (!strncmp(buffer, "status", 6)) { - QTV_Status(qtv, from); + QTV_Status(cluster, from); return; } if (!strncmp(buffer, "getchallenge", 12)) { i = NewChallenge(from); - Netchan_OutOfBandPrint(qtv->qwdsocket, *from, "c%i", i); + Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *from, "c%i", i); return; } 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. - Netchan_OutOfBandPrint(qtv->qwdsocket, *from, "n" "Proxy is not connected to a server\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"); + if (cluster->numviewers >= cluster->maxviewers && cluster->maxviewers) + Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *from, "n" "Sorry, proxy is full.\n"); else - NewQWClient(qtv, from, buffer); + NewQWClient(cluster, from, buffer); return; } if (!strncmp(buffer, "l\n", 2)) { - printf("Ack\n"); + Sys_Printf(cluster, "Ack\n"); } } @@ -744,83 +803,80 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) short interp; float lerp; - if (tv->physicstime != v->settime && tv->chokeonnotupdated) - { - WriteByte(msg, svc_updatestatlong); - WriteByte(msg, STAT_TIME); - WriteLong(msg, v->settime); - - 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*/ + if (v->server) { + if (tv->physicstime != v->settime && tv->cluster->chokeonnotupdated) + { + WriteByte(msg, svc_updatestatlong); + WriteByte(msg, STAT_TIME); + WriteLong(msg, v->settime); + + v->settime = tv->physicstime; + } + 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); + if (lerp < 0) + lerp = 0; + if (lerp > 1) + lerp = 1; + + for (i = 0; i < MAX_CLIENTS-1; i++) + { + if (!tv->players[i].active) + continue; + + if (v->trackplayer != i && !BSP_Visible(tv->bsp, tv->players[i].leafcount, tv->players[i].leafs)) + continue; + + flags = PF_COMMAND; + if (v->trackplayer == i && tv->players[i].current.weaponframe) + flags |= PF_WEAPONFRAME; + + WriteByte(msg, svc_playerinfo); + WriteByte(msg, i); + WriteShort(msg, flags); + + interp = (lerp)*tv->players[i].current.origin[0] + (1-lerp)*tv->players[i].old.origin[0]; + WriteShort(msg, interp); + interp = (lerp)*tv->players[i].current.origin[1] + (1-lerp)*tv->players[i].old.origin[1]; + WriteShort(msg, interp); + interp = (lerp)*tv->players[i].current.origin[2] + (1-lerp)*tv->players[i].old.origin[2]; + WriteShort(msg, interp); + + WriteByte(msg, tv->players[i].current.frame); + + if (flags & PF_MSEC) + { + WriteByte(msg, 0); + } + if (flags & PF_COMMAND) + { + // to.angles[0] = tv->players[i].current.angles[0]; + // to.angles[1] = tv->players[i].current.angles[1]; + // to.angles[2] = tv->players[i].current.angles[2]; + + to.angles[0] = InterpolateAngle(tv->players[i].old.angles[0], tv->players[i].current.angles[0], lerp); + to.angles[1] = InterpolateAngle(tv->players[i].old.angles[1], tv->players[i].current.angles[1], lerp); + to.angles[2] = InterpolateAngle(tv->players[i].old.angles[2], tv->players[i].current.angles[2], lerp); + WriteDeltaUsercmd(msg, &nullcmd, &to); + } + //vel + //model + //skin + //effects + //weaponframe + if (flags & PF_WEAPONFRAME) + WriteByte(msg, tv->players[i].current.weaponframe); + } } - - lerp = ((tv->curtime - tv->oldpackettime)/1000.0f) / ((tv->nextpackettime - tv->oldpackettime)/1000.0f); - if (lerp < 0) - lerp = 0; - if (lerp > 1) - lerp = 1; - - - for (i = 0; i < MAX_CLIENTS-1; i++) + else { - if (!tv->players[i].active) - continue; - - if (v->trackplayer != i && !BSP_Visible(tv->bsp, tv->players[i].leafcount, tv->players[i].leafs)) - continue; - - flags = PF_COMMAND; - if (v->trackplayer == i && tv->players[i].current.weaponframe) - flags |= PF_WEAPONFRAME; - - WriteByte(msg, svc_playerinfo); - WriteByte(msg, i); - WriteShort(msg, flags); - - interp = (lerp)*tv->players[i].current.origin[0] + (1-lerp)*tv->players[i].old.origin[0]; - WriteShort(msg, interp); - interp = (lerp)*tv->players[i].current.origin[1] + (1-lerp)*tv->players[i].old.origin[1]; - WriteShort(msg, interp); - interp = (lerp)*tv->players[i].current.origin[2] + (1-lerp)*tv->players[i].old.origin[2]; - WriteShort(msg, interp); - - WriteByte(msg, tv->players[i].current.frame); - - if (flags & PF_MSEC) - { - WriteByte(msg, 0); - } - if (flags & PF_COMMAND) - { -// to.angles[0] = tv->players[i].current.angles[0]; -// to.angles[1] = tv->players[i].current.angles[1]; -// to.angles[2] = tv->players[i].current.angles[2]; - - to.angles[0] = InterpolateAngle(tv->players[i].old.angles[0], tv->players[i].current.angles[0], lerp); - to.angles[1] = InterpolateAngle(tv->players[i].old.angles[1], tv->players[i].current.angles[1], lerp); - to.angles[2] = InterpolateAngle(tv->players[i].old.angles[2], tv->players[i].current.angles[2], lerp); - WriteDeltaUsercmd(msg, &nullcmd, &to); - } - //vel - //model - //skin - //effects - //weaponframe - if (flags & PF_WEAPONFRAME) - WriteByte(msg, tv->players[i].current.weaponframe); + lerp = 1; } WriteByte(msg, svc_playerinfo); @@ -839,30 +895,31 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) e = &v->frame[v->netchan.outgoing_sequence&(ENTITY_FRAMES-1)]; e->numents = 0; - for (i = 0; i < tv->maxents; i++) - { - if (!tv->entity[i].current.modelindex) - continue; - - if (tv->entity[i].current.modelindex >= tv->numinlines && !BSP_Visible(tv->bsp, tv->entity[i].leafcount, tv->entity[i].leafs)) - continue; - - e->entnum[e->numents] = i; - memcpy(&e->ents[e->numents], &tv->entity[i].current, sizeof(entity_state_t)); - - if (tv->entity[i].updatetime == tv->oldpackettime) + if (v->server) + for (i = 0; i < tv->maxents; i++) { - e->ents[e->numents].origin[0] = (lerp)*tv->entity[i].current.origin[0] + (1-lerp)*tv->entity[i].old.origin[0]; - e->ents[e->numents].origin[1] = (lerp)*tv->entity[i].current.origin[1] + (1-lerp)*tv->entity[i].old.origin[1]; - e->ents[e->numents].origin[2] = (lerp)*tv->entity[i].current.origin[2] + (1-lerp)*tv->entity[i].old.origin[2]; + if (!tv->entity[i].current.modelindex) + continue; + + if (tv->entity[i].current.modelindex >= tv->numinlines && !BSP_Visible(tv->bsp, tv->entity[i].leafcount, tv->entity[i].leafs)) + continue; + + e->entnum[e->numents] = i; + memcpy(&e->ents[e->numents], &tv->entity[i].current, sizeof(entity_state_t)); + + if (tv->entity[i].updatetime == tv->oldpackettime) + { + e->ents[e->numents].origin[0] = (lerp)*tv->entity[i].current.origin[0] + (1-lerp)*tv->entity[i].old.origin[0]; + e->ents[e->numents].origin[1] = (lerp)*tv->entity[i].current.origin[1] + (1-lerp)*tv->entity[i].old.origin[1]; + e->ents[e->numents].origin[2] = (lerp)*tv->entity[i].current.origin[2] + (1-lerp)*tv->entity[i].old.origin[2]; + } + + e->numents++; + + if (e->numents == ENTS_PER_FRAME) + break; } - e->numents++; - - if (e->numents == ENTS_PER_FRAME) - break; - } - SV_EmitPacketEntities(tv, v, e, msg); } @@ -871,13 +928,13 @@ void UpdateStats(sv_t *qtv, viewer_t *v) netmsg_t msg; char buf[6]; int i; - const static unsigned int nullstats[MAX_STATS]; + const static unsigned int nullstats[MAX_STATS] = {1000}; const unsigned int *stats; InitNetMsg(&msg, buf, sizeof(buf)); - if (v->trackplayer < 0) + if (v->trackplayer < 0 || !qtv) stats = nullstats; else 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); } -qboolean QW_IsOn(sv_t *qtv, char *name) +viewer_t *QW_IsOn(cluster_t *cluster, char *name) { 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. - return true; + return v; - return false; + return NULL; } 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); } -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; char buf[1024]; @@ -1055,10 +1118,6 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m) if (!strcmp(buf, "new")) 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)) { char *cmd = buf+10; @@ -1071,13 +1130,16 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m) InitNetMsg(&msg, buf, sizeof(buf)); - if (svcount != qtv->servercount) + if (svcount != v->servercount) { //looks like we changed map without them. SendServerData(qtv, v); return; } - SendList(qtv, first, qtv->modellist, svc_modellist, &msg); + if (!qtv) + SendList(qtv, first, ConnectionlessModelList, svc_modellist, &msg); + else + SendList(qtv, first, qtv->modellist, svc_modellist, &msg); SendBufferToViewer(v, msg.data, msg.cursize, true); } else if (!strncmp(buf, "soundlist ", 10)) @@ -1092,20 +1154,23 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m) InitNetMsg(&msg, buf, sizeof(buf)); - if (svcount != qtv->servercount) + if (svcount != v->servercount) { //looks like we changed map without them. SendServerData(qtv, v); return; } - SendList(qtv, first, qtv->soundlist, svc_soundlist, &msg); + if (!qtv) + SendList(qtv, first, ConnectionlessSoundList, svc_soundlist, &msg); + else + SendList(qtv, first, qtv->soundlist, svc_soundlist, &msg); SendBufferToViewer(v, msg.data, msg.cursize, true); } else if (!strncmp(buf, "prespawn", 8)) { char skin[128]; - if (atoi(buf + 9) != qtv->servercount) + if (atoi(buf + 9) != v->servercount) SendServerData(qtv, v); //we're old. else { @@ -1120,13 +1185,17 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m) InitNetMsg(&msg, buf, sizeof(buf)); - r = Prespawn(qtv, v->netchan.message.cursize, &msg, r); - SendBufferToViewer(v, msg.data, msg.cursize, true); + if (v->server) + { + r = Prespawn(qtv, v->netchan.message.cursize, &msg, r); + SendBufferToViewer(v, msg.data, msg.cursize, true); + } + else r = -1; if (r < 0) sprintf(skin, "%ccmd spawn\n", svc_stufftext); 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); } @@ -1139,7 +1208,7 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m) } else if (!strncmp(buf, "begin", 5)) { - if (atoi(buf+6) != qtv->servercount) + if (atoi(buf+6) != v->servercount) SendServerData(qtv, v); //this is unfortunate! else v->thinksitsconnected = true; @@ -1157,15 +1226,44 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m) v->drop = true; else if (!strncmp(buf, "ison", 4)) { - if (QW_IsOn(qtv, buf+6)) - QW_PrintfToViewer(v, "%s is on the proxy\n", buf+6); + viewer_t *other; + 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 + 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+6); //the apology is to make the alternatives distinct. + 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)) v->trackplayer = atoi(buf+7); else if (!strncmp(buf, "ptrack", 6)) 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)) { char *key, *value, *end; @@ -1208,7 +1306,7 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m) } else { - printf("Client sent unknown string command: %s\n", buf); + Sys_Printf(cluster, "Client sent unknown string command: %s\n", buf); } break; @@ -1219,7 +1317,30 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m) ReadDeltaUsercmd(m, &nullcmd, &oldest); ReadDeltaUsercmd(m, &oldest, &oldcmd); ReadDeltaUsercmd(m, &oldcmd, &newcmd); - PMove(v, &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); break; case clc_tmove: v->origin[0] = ((signed short)ReadShort(m))/8.0f; @@ -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 && inext, 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 && inext, 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'}; -void QW_FreeViewer(sv_t *qtv, viewer_t *viewer) + +void QW_FreeViewer(cluster_t *cluster, viewer_t *viewer) { int i; //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 - Netchan_Transmit(&viewer->netchan, strlen(dropcmd)+1, dropcmd); - Netchan_Transmit(&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(cluster, &viewer->netchan, strlen(dropcmd)+1, dropcmd); + Netchan_Transmit(cluster, &viewer->netchan, strlen(dropcmd)+1, dropcmd); for (i = 0; i < MAX_BACK_BUFFERS; i++) { @@ -1259,10 +1521,10 @@ void QW_FreeViewer(sv_t *qtv, viewer_t *viewer) free(viewer); - qtv->numviewers--; + cluster->numviewers--; } -void QW_UpdateUDPStuff(sv_t *qtv) +void QW_UpdateUDPStuff(cluster_t *cluster) { char buffer[MAX_MSGLEN*2]; netadr_t from; @@ -1273,18 +1535,18 @@ void QW_UpdateUDPStuff(sv_t *qtv) 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 ?.. - NET_SendPacket(qtv->qwdsocket, strlen(buffer), buffer, from); + NET_SendPacket(cluster, cluster->qwdsocket, strlen(buffer), buffer, from); } 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; @@ -1294,7 +1556,7 @@ void QW_UpdateUDPStuff(sv_t *qtv) 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. { @@ -1310,7 +1572,7 @@ void QW_UpdateUDPStuff(sv_t *qtv) if (*(int*)buffer == -1) { //connectionless message - ConnectionlessPacket(qtv, &from, &m); + ConnectionlessPacket(cluster, &from, &m); continue; } @@ -1319,20 +1581,22 @@ void QW_UpdateUDPStuff(sv_t *qtv) ReadLong(&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 (Netchan_Process(&v->netchan, &m)) { 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->chokeme = qtv->chokeonnotupdated; + v->chokeme = cluster->chokeonnotupdated; } - ParseQWC(qtv, v, &m); + ParseQWC(cluster, v->server, v, &m); } 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"); - f = qtv->viewers; - qtv->viewers = f->next; + Sys_Printf(cluster, "Dropping client\n"); + f = cluster->viewers; + 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) { //free the next/ - printf("Dropping client\n"); + Sys_Printf(cluster, "Dropping client\n"); f = v->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; m.cursize = 0; if (v->thinksitsconnected) { - SendPlayerStates(qtv, v, &m); - UpdateStats(qtv, v); + SendPlayerStates(v->server, v, &m); + 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) {//shift the backbuffers around diff --git a/fteqtv/rcon.c b/fteqtv/rcon.c index 5b5daf5df..1adb1c774 100644 --- a/fteqtv/rcon.c +++ b/fteqtv/rcon.c @@ -89,7 +89,7 @@ void Info_RemoveKey (char *s, const char *key) if (strstr (key, "\\")) { - printf ("Key has a slash\n"); +// printf ("Key has a slash\n"); return; } @@ -139,19 +139,19 @@ void Info_SetValueForStarKey (char *s, const char *key, const char *value, int m if (strstr (key, "\\") || strstr (value, "\\") ) { - printf ("Key has a slash\n"); +// printf ("Key has a slash\n"); return; } if (strstr (key, "\"") || strstr (value, "\"") ) { - printf ("Key has a quote\n"); +// printf ("Key has a quote\n"); return; } 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; } @@ -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) { - printf ("info buffer is too small\n"); +// printf ("info buffer is too small\n"); return; } @@ -293,23 +293,219 @@ skipwhite: 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 "" - 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'; if (!strcmp(arg[0], "status")) { + sprintf(buffer, "%i connections\n", qtv->cluster->numservers); + strcat(buffer, "\n"); - strcat(buffer, "Server: "); + strcat(buffer, "Selected server: "); strcat(buffer, qtv->server); strcat(buffer, "\n"); 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, ")\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) { @@ -342,55 +531,19 @@ char *Rcon_Command(sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qbo } strcat(buffer, "Options:\n"); - if (qtv->chokeonnotupdated) + if (qtv->cluster->chokeonnotupdated) strcat(buffer, " Choke\n"); - if (qtv->lateforward) + if (qtv->cluster->lateforward) strcat(buffer, " Late forwarding\n"); - if (!qtv->notalking) + if (!qtv->cluster->notalking) strcat(buffer, " Talking allowed\n"); + if (qtv->cluster->nobsp) + strcat(buffer, " No BSP loading\n"); strcat(buffer, "\n"); 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")) { 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] == ':') 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); if (QTV_Connect(qtv, arg[1])) return "File opened successfully\n"; @@ -438,10 +591,7 @@ char *Rcon_Command(sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qbo else 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")) { 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; - if (NET_StringToAddr(arg[1], &addr)) - { - 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]); + FILE *f; + char line[512], *res; 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) - { - closesocket(qtv->listenmvd); - qtv->listenmvd = INVALID_SOCKET; - qtv->tcplistenportnum = 0; - - return "Closed udp port\n"; - } - return "udp port was already closed\n"; + snprintf(buffer, sizeofbuffer, "Couldn't exec \"%s\"\n", arg[1]); + return buffer; } else { - news = QW_InitUDPSocket(newp); - - if (news != INVALID_SOCKET) + while(fgets(line, sizeof(line)-1, f)) { - qtv->mastersendtime = qtv->curtime; - closesocket(qtv->qwdsocket); - qtv->qwdsocket = news; - qtv->qwlistenportnum = newp; - return "Opened udp port\n"; + res = Rcon_Command(qtv->cluster, qtv, line, buffer, sizeofbuffer, localcommand); + Sys_Printf(qtv->cluster, "%s", res); } - else - return "Failed to open udp port\n"; + fclose(f); + return "Execed\n"; } } - else if (!strcmp(arg[0], "password")) - { - if (!localcommand) - return "Rejecting rcon password change.\n"; - - strncpy(qtv->password, arg[1], sizeof(qtv->password)-1); - return "Password changed.\n"; - } else - return "Unrecognised command.\n"; - + { + return NULL; + } +} + +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; } diff --git a/fteqtv/source.c b/fteqtv/source.c index 179ad5e38..821e7e6ee 100644 --- a/fteqtv/source.c +++ b/fteqtv/source.c @@ -20,6 +20,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "qtv.h" +#define RECONNECT_TIME (1000*30) + 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); 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; } - 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; } qtv->sourcesock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); @@ -238,18 +248,21 @@ qboolean Net_ConnectToServer(sv_t *qtv, char *ip) 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); qtv->sourcesock = INVALID_SOCKET; return false; } - if (ioctlsocket (qtv->sourcesock, FIONBIO, &nonblocking) == -1) + if (connect(qtv->sourcesock, (struct sockaddr *)&qtv->serveraddress, sizeof(qtv->serveraddress)) == INVALID_SOCKET) { - closesocket(qtv->sourcesock); - qtv->sourcesock = INVALID_SOCKET; - return false; + if (qerrno != EWOULDBLOCK) + { + closesocket(qtv->sourcesock); + qtv->sourcesock = INVALID_SOCKET; + return false; + } } return true; @@ -264,7 +277,7 @@ void Net_FindProxies(sv_t *qtv) if (sock == INVALID_SOCKET) 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','.'}; 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; int length; @@ -376,7 +389,7 @@ void Net_TryFlushProxyBuffer(oproxy_t *prox) // CheckMVDConsistancy(prox->buffer, prox->bufferpos, prox->buffersize); if (bufpos+length > MAX_PROXY_BUFFER) - printf("oversize flush\n"); + Sys_Printf(cluster, "oversize flush\n"); if (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; 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. { //they're too slow. hopefully it was just momentary lag prox->flushing = true; @@ -438,7 +451,7 @@ void Net_ProxySend(oproxy_t *prox, char *buffer, int length) #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; char tbuf[16]; @@ -450,9 +463,9 @@ void Prox_SendMessage(oproxy_t *prox, char *buf, int length, int dem_type, unsig 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) @@ -502,31 +515,31 @@ void Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox) prox->flushing = false; - BuildServerData(qtv, &msg, true); - Prox_SendMessage(prox, msg.data, msg.cursize, dem_read, (unsigned)-1); + BuildServerData(qtv, &msg, true, 0); + Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1); msg.cursize = 0; for (prespawn = 0;prespawn >= 0;) { 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; } for (prespawn = 0;prespawn >= 0;) { 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; } - 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;) { 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; } @@ -534,30 +547,30 @@ void Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox) //we do need to send entity states. 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; WriteByte(&msg, svc_stufftext); 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; - Net_TryFlushProxyBuffer(prox); + Net_TryFlushProxyBuffer(qtv->cluster, prox); Prox_SendPlayerStats(qtv, prox); - Net_TryFlushProxyBuffer(prox); + Net_TryFlushProxyBuffer(qtv->cluster, prox); - if (!qtv->lateforward) - Net_ProxySend(prox, qtv->buffer, qtv->buffersize); //send all the info we've not yet processed. + if (!qtv->cluster->lateforward) + Net_ProxySend(qtv->cluster, prox, qtv->buffer, qtv->buffersize); //send all the info we've not yet processed. 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... } else - Net_TryFlushProxyBuffer(prox); + Net_TryFlushProxyBuffer(qtv->cluster, prox); } 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 { - Net_TryFlushProxyBuffer(prox); //try and flush it. + Net_TryFlushProxyBuffer(qtv->cluster, prox); //try and flush it. continue; } } @@ -613,12 +626,11 @@ void Net_ForwardStream(sv_t *qtv, char *buffer, int length) continue; //add the new data - Net_ProxySend(prox, buffer, length); + Net_ProxySend(qtv->cluster, prox, buffer, length); - //and try to send it. - Net_TryFlushProxyBuffer(prox); - Net_TryFlushProxyBuffer(prox); - Net_TryFlushProxyBuffer(prox); + Net_TryFlushProxyBuffer(qtv->cluster, prox); +// Net_TryFlushProxyBuffer(qtv->cluster, prox); +// Net_TryFlushProxyBuffer(qtv->cluster, prox); } } @@ -648,7 +660,7 @@ qboolean Net_ReadStream(sv_t *qtv) if (read > 0) { qtv->buffersize += read; - if (!qtv->lateforward) + if (!qtv->cluster->lateforward) Net_ForwardStream(qtv, buffer, read); } else @@ -657,7 +669,12 @@ qboolean Net_ReadStream(sv_t *qtv) { 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); qtv->sourcesock = INVALID_SOCKET; } @@ -666,7 +683,7 @@ qboolean Net_ReadStream(sv_t *qtv) fclose(qtv->file); qtv->file = NULL; } - printf("Read error or eof\n"); + Sys_Printf(qtv->cluster, "Read error or eof to %s\n", qtv->server); return false; } } @@ -688,7 +705,7 @@ unsigned int Sys_Milliseconds(void) return ((unsigned)tv.tv_sec)*1000 + (((unsigned)tv.tv_usec)/1000); #endif } - +/* void NetSleep(sv_t *tv) { int m; @@ -741,7 +758,7 @@ void NetSleep(sv_t *tv) if (tv->inputlength) { 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); tv->inputlength = 0; tv->commandinput[0] = '\0'; @@ -787,6 +804,7 @@ void NetSleep(sv_t *tv) } #endif } +*/ void Trim(char *s) { @@ -816,7 +834,7 @@ qboolean QTV_Connect(sv_t *qtv, char *serverurl) *qtv->serverinfo = '\0'; Info_SetValueForStarKey(qtv->serverinfo, "*version", "FTEQTV", 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)); if (!strncmp(qtv->server, "file:", 5)) 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)) { - printf("Couldn't connect (%s)\n", qtv->server); + Sys_Printf(qtv->cluster, "Couldn't connect (%s)\n", qtv->server); return false; } - printf("Connected\n"); + Sys_Printf(qtv->cluster, "Connected\n"); if (qtv->sourcesock == INVALID_SOCKET) { qtv->parsetime = Sys_Milliseconds(); - printf("Playing from file\n"); + Sys_Printf(qtv->cluster, "Playing from file\n"); } else { 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; } - +/* void QTV_Run(sv_t *qtv) { int lengthofs; @@ -1112,3 +1130,424 @@ int main(int argc, char **argv) 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); +} \ No newline at end of file