diff --git a/fteqtv/Makefile b/fteqtv/Makefile index 279589728..3ba4b4041 100644 --- a/fteqtv/Makefile +++ b/fteqtv/Makefile @@ -4,7 +4,7 @@ STRIP=strip STRIPFLAGS=--strip-unneeded --remove-section=.comment -OBJS = netchan.o parse.o qw.o source.o bsp.o rcon.o mdfour.o crc.o control.o forward.o pmove.o httpsv.o +OBJS = netchan.o parse.o msg.o qw.o source.o bsp.o rcon.o mdfour.o crc.o control.o forward.o pmove.o httpsv.o qtv: $(OBJS) qtv.h $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $@.db -lm diff --git a/fteqtv/bsp.c b/fteqtv/bsp.c index 0d1d3214c..ba59bd09a 100644 --- a/fteqtv/bsp.c +++ b/fteqtv/bsp.c @@ -43,7 +43,7 @@ struct bsp_s { intermission_t intermissionspot[8]; }; -static const intermission_t intermissionspot; +static const intermission_t nullintermissionspot; typedef struct @@ -213,6 +213,8 @@ void BSP_LoadEntities(bsp_t *bsp, char *entitydata) switch (etype) { + case et_random: //a random (unknown) entity + break; case et_primarystart: //a single player start memcpy(startspotorg, org, sizeof(startspotorg)); memcpy(startspotangles, angles, sizeof(startspotangles)); @@ -483,5 +485,6 @@ const intermission_t *BSP_IntermissionSpot(bsp_t *bsp) return &bsp->intermissionspot[spotnum]; } } - return &intermissionspot; -} \ No newline at end of file + return &nullintermissionspot; +} + diff --git a/fteqtv/control.c b/fteqtv/control.c index c8dc8cdd2..05fcf3e3b 100644 --- a/fteqtv/control.c +++ b/fteqtv/control.c @@ -1,538 +1,572 @@ -/* -Contains the control routines that handle both incoming and outgoing stuff -*/ - -#include "qtv.h" -#include - - - -// char *date = "Oct 24 1996"; -static char *date = __DATE__ ; -static char *mon[12] = -{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; -static char mond[12] = -{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - -// returns days since Oct 24 1996 -int build_number( void ) -{ - int m = 0; - int d = 0; - int y = 0; - static int b = 0; - - if (b != 0) - return b; - - for (m = 0; m < 11; m++) - { - if (strncmp( &date[0], mon[m], 3 ) == 0) - break; - d += mond[m]; - } - - d += atoi( &date[4] ) - 1; - - y = atoi( &date[7] ) - 1900; - - b = d + (int)((y - 1) * 365.25); - - if (((y % 4) == 0) && m > 1) - { - b += 1; - } - - b -= 35778; // Dec 16 1998 - - return b; -} - - - -typedef struct { - char name[56]; - int offset; - int length; -} pakfile; -// PACK, offset, lengthofpakfiles -FILE *FindInPaks(char *gamedir, char *filename, int *size) -{ - FILE *f; - char fname[1024]; - int i, j; - int numfiles; - unsigned int header[3]; - - pakfile pf; - - for (i = 0; ; i++) - { - sprintf(fname, "%s/pak%i.pak", gamedir, i); - f = fopen(fname, "rb"); - if (!f) - return NULL; //ran out of possible pak files. - - fread(header, 1, sizeof(header), f); - if (header[0] != *(unsigned int*)"PACK") - { //err... hmm. - fclose(f); - continue; - } - numfiles = LittleLong(header[2])/sizeof(pakfile); - fseek(f, LittleLong(header[1]), SEEK_SET); - for (j = 0; j < numfiles; j++) - { - fread(&pf, 1, sizeof(pf), f); - if (!strcmp(pf.name, filename)) - { - fseek(f, LittleLong(pf.offset), 0); - if (size) - *size = LittleLong(pf.length); - return f; - } - } - fclose(f); - //not found - } - return NULL; -} - -unsigned char *FS_ReadFile2(char *gamedir, char *filename, unsigned int *sizep) -{ - int size; - unsigned char *data; - - FILE *f; - char fname[1024]; - - if (!*filename) - return NULL; - - //try and read it straight out of the file system - sprintf(fname, "%s/%s", gamedir, filename); - f = fopen(fname, "rb"); - if (!f) - f = fopen(filename, "rb"); //see if we're being run from inside the gamedir - if (!f) - { - f = FindInPaks(gamedir, filename, &size); - if (!f) - f = FindInPaks("id1", filename, &size); - if (!f) - { - return NULL; - } - } - else - { - fseek(f, 0, SEEK_END); - size = ftell(f); - fseek(f, 0, SEEK_SET); - } - data = malloc(size); - if (data) - fread(data, 1, size, f); - fclose(f); - - if (sizep) - *sizep = size; - return data; -} - -unsigned char *FS_ReadFile(char *gamedir, char *filename, unsigned int *size) -{ - char *data; - if (!gamedir || !*gamedir || !strcmp(gamedir, "qw")) - data = NULL; - else - data = FS_ReadFile2(gamedir, filename, size); - if (!data) - { - data = FS_ReadFile2("qw", filename, size); - if (!data) - { - data = FS_ReadFile2("id1", filename, size); - if (!data) - { - return NULL; - } - } - } - return data; -} - - -int SortFilesByDate(const void *a, const void *b) -{ - if (((availdemo_t*)a)->time < ((availdemo_t*)b)->time) - return 1; - if (((availdemo_t*)a)->time > ((availdemo_t*)b)->time) - return -1; - - if (((availdemo_t*)a)->smalltime < ((availdemo_t*)b)->smalltime) - return 1; - if (((availdemo_t*)a)->smalltime > ((availdemo_t*)b)->smalltime) - return -1; - return 0; -} - -void Cluster_BuildAvailableDemoList(cluster_t *cluster) -{ - cluster->availdemoscount = 0; - -#ifdef _WIN32 - { - WIN32_FIND_DATA ffd; - HANDLE h; - h = FindFirstFile("*.mvd", &ffd); - if (h != INVALID_HANDLE_VALUE) - { - do - { - if (cluster->availdemoscount == sizeof(cluster->availdemos)/sizeof(cluster->availdemos[0])) - break; - strncpy(cluster->availdemos[cluster->availdemoscount].name, ffd.cFileName, sizeof(cluster->availdemos[0].name)); - cluster->availdemos[cluster->availdemoscount].size = ffd.nFileSizeLow; - cluster->availdemos[cluster->availdemoscount].time = ffd.ftLastWriteTime.dwHighDateTime; - cluster->availdemos[cluster->availdemoscount].smalltime = ffd.ftLastWriteTime.dwLowDateTime; - cluster->availdemoscount++; - } while(FindNextFile(h, &ffd)); - FindClose(h); - } - } -#endif - - qsort(cluster->availdemos, cluster->availdemoscount, sizeof(cluster->availdemos[0]), SortFilesByDate); -} - -void Cluster_Run(cluster_t *cluster, qboolean dowait) -{ - oproxy_t *pend, *pend2, *pend3; - sv_t *sv, *old; - - int m; - struct timeval timeout; - fd_set socketset; - - if (dowait) - { - - 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->usequkeworldprotocols && 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 - - if (cluster->viewserver) - { - timeout.tv_sec = 0; - timeout.tv_usec = 1000; - } - else - { - 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 - cluster->inputlength--; - - 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 - } - - - - cluster->curtime = Sys_Milliseconds(); - - for (sv = cluster->servers; sv; ) - { - old = sv; - sv = sv->next; - QTV_Run(old); - } - - SV_FindProxies(cluster->tcpsocket, cluster, NULL); //look for any other proxies wanting to muscle in on the action. - - QW_UpdateUDPStuff(cluster); - - while(cluster->pendingproxies) - { - pend2 = cluster->pendingproxies->next; - if (SV_ReadPendingProxy(cluster, cluster->pendingproxies)) - cluster->pendingproxies = pend2; - else - break; - } - if (cluster->pendingproxies) - { - for(pend = cluster->pendingproxies; pend && pend->next; ) - { - pend2 = pend->next; - pend3 = pend2->next; - if (SV_ReadPendingProxy(cluster, pend2)) - { - pend->next = pend3; - pend = pend3; - } - else - { - pend = pend2; - } - } - } -} - - - - - -void DoCommandLine(cluster_t *cluster, int argc, char **argv) -{ - int i; - char commandline[8192]; - char *start, *end, *result; - char buffer[8192]; - - result = Rcon_Command(cluster, NULL, "exec qtv.cfg", buffer, sizeof(buffer), true); - Sys_Printf(cluster, "%s", result); - - 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; - } - Sys_Printf(cluster, "\n"); -} - -int main(int argc, char **argv) -{ - cluster_t cluster; - -// soundtest(); - -#ifdef SIGPIPE - signal(SIGPIPE, SIG_IGN); -#endif - -#ifdef _WIN32 - { - WSADATA discard; - WSAStartup(MAKEWORD(2,0), &discard); - } -#endif - - memset(&cluster, 0, sizeof(cluster)); - - cluster.qwdsocket = INVALID_SOCKET; - cluster.tcpsocket = INVALID_SOCKET; - cluster.qwlistenportnum = 0; - cluster.allownqclients = true; - strcpy(cluster.hostname, DEFAULT_HOSTNAME); - cluster.buildnumber = build_number(); - cluster.maxproxies = -1; - - Sys_Printf(&cluster, "QTV Build %i.\n", cluster.buildnumber); - - 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 udp port %i\n", cluster.qwlistenportnum); - } - if (cluster.tcpsocket == INVALID_SOCKET && !cluster.tcplistenportnum) - { - cluster.tcpsocket = Net_MVDListen(cluster.tcplistenportnum = 27599); - if (cluster.tcpsocket != INVALID_SOCKET) - Sys_Printf(&cluster, "opened tcp port %i\n", cluster.tcplistenportnum); - } - - Sys_Printf(&cluster, "\n" - "Welcome to FTEQTV\n" - "Please type\n" - "qtv server:port\n" - " to connect to a tcp server.\n" - "qw server:port\n" - " to connect to a regular qw server.\n" - "demo qw/example.mvd\n" - " to play a demo from an mvd.\n" - "\n"); - } - -// Cluster_BuildAvailableDemoList(&cluster); - - while (!cluster.wanttoexit) - { - Cluster_Run(&cluster, true); -#ifdef VIEWER - DemoViewer_Update(cluster.viewserver); -#endif - } - - return 0; -} - -void QTV_Printf(sv_t *qtv, char *fmt, ...) -{ - va_list argptr; - char string[2048]; - - va_start (argptr, fmt); - vsnprintf (string, sizeof(string)-1, fmt,argptr); - string[sizeof(string)-1] = 0; - va_end (argptr); - - if (qtv->silentstream) - return; - - Sys_Printf(qtv->cluster, "%s", string); -} - -void Sys_Printf(cluster_t *cluster, char *fmt, ...) -{ - va_list argptr; - char string[2048]; - unsigned char *t; - - va_start (argptr, fmt); - vsnprintf (string, sizeof(string)-1, fmt,argptr); - string[sizeof(string)-1] = 0; - va_end (argptr); - - for (t = (unsigned char*)string; *t; t++) - { - if (*t >= 146 && *t < 156) - *t = *t - 146 + '0'; - if (*t == 143) - *t = '.'; - if (*t == 157 || *t == 158 || *t == 159) - *t = '-'; - if (*t >= 128) - *t -= 128; - if (*t == 16) - *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", string); -} - +/* +Contains the control routines that handle both incoming and outgoing stuff +*/ + +#include "qtv.h" +#include +#include +#include + + +// char *date = "Oct 24 1996"; +static char *date = __DATE__ ; +static char *mon[12] = +{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; +static char mond[12] = +{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +// returns days since Oct 24 1996 +int build_number( void ) +{ + int m = 0; + int d = 0; + int y = 0; + static int b = 0; + + if (b != 0) + return b; + + for (m = 0; m < 11; m++) + { + if (strncmp( &date[0], mon[m], 3 ) == 0) + break; + d += mond[m]; + } + + d += atoi( &date[4] ) - 1; + + y = atoi( &date[7] ) - 1900; + + b = d + (int)((y - 1) * 365.25); + + if (((y % 4) == 0) && m > 1) + { + b += 1; + } + + b -= 35778; // Dec 16 1998 + + return b; +} + + + +typedef struct { + char name[56]; + int offset; + int length; +} pakfile; +// PACK, offset, lengthofpakfiles +FILE *FindInPaks(char *gamedir, char *filename, int *size) +{ + FILE *f; + char fname[1024]; + int i, j; + int numfiles; + unsigned int header[3]; + + pakfile pf; + + for (i = 0; ; i++) + { + sprintf(fname, "%s/pak%i.pak", gamedir, i); + f = fopen(fname, "rb"); + if (!f) + return NULL; //ran out of possible pak files. + + fread(header, 1, sizeof(header), f); + if (header[0] != *(unsigned int*)"PACK") + { //err... hmm. + fclose(f); + continue; + } + numfiles = LittleLong(header[2])/sizeof(pakfile); + fseek(f, LittleLong(header[1]), SEEK_SET); + for (j = 0; j < numfiles; j++) + { + fread(&pf, 1, sizeof(pf), f); + if (!strcmp(pf.name, filename)) + { + fseek(f, LittleLong(pf.offset), 0); + if (size) + *size = LittleLong(pf.length); + return f; + } + } + fclose(f); + //not found + } + return NULL; +} + +unsigned char *FS_ReadFile2(char *gamedir, char *filename, unsigned int *sizep) +{ + int size; + unsigned char *data; + + FILE *f; + char fname[1024]; + + if (!*filename) + return NULL; + + //try and read it straight out of the file system + sprintf(fname, "%s/%s", gamedir, filename); + f = fopen(fname, "rb"); + if (!f) + f = fopen(filename, "rb"); //see if we're being run from inside the gamedir + if (!f) + { + f = FindInPaks(gamedir, filename, &size); + if (!f) + f = FindInPaks("id1", filename, &size); + if (!f) + { + return NULL; + } + } + else + { + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + } + data = malloc(size); + if (data) + fread(data, 1, size, f); + fclose(f); + + if (sizep) + *sizep = size; + return data; +} + +unsigned char *FS_ReadFile(char *gamedir, char *filename, unsigned int *size) +{ + unsigned char *data; + if (!gamedir || !*gamedir || !strcmp(gamedir, "qw")) + data = NULL; + else + data = FS_ReadFile2(gamedir, filename, size); + if (!data) + { + data = FS_ReadFile2("qw", filename, size); + if (!data) + { + data = FS_ReadFile2("id1", filename, size); + if (!data) + { + return NULL; + } + } + } + return data; +} + + +int SortFilesByDate(const void *a, const void *b) +{ + if (((availdemo_t*)a)->time < ((availdemo_t*)b)->time) + return 1; + if (((availdemo_t*)a)->time > ((availdemo_t*)b)->time) + return -1; + + if (((availdemo_t*)a)->smalltime < ((availdemo_t*)b)->smalltime) + return 1; + if (((availdemo_t*)a)->smalltime > ((availdemo_t*)b)->smalltime) + return -1; + return 0; +} + +void Cluster_BuildAvailableDemoList(cluster_t *cluster) +{ + cluster->availdemoscount = 0; + +#ifdef _WIN32 + { + WIN32_FIND_DATA ffd; + HANDLE h; + char path[512]; + snprintf(path, sizeof(path), "%s*.mvd", cluster->demodir); + h = FindFirstFile(path, &ffd); + if (h != INVALID_HANDLE_VALUE) + { + do + { + if (cluster->availdemoscount == sizeof(cluster->availdemos)/sizeof(cluster->availdemos[0])) + break; + strncpy(cluster->availdemos[cluster->availdemoscount].name, ffd.cFileName, sizeof(cluster->availdemos[0].name)); + cluster->availdemos[cluster->availdemoscount].size = ffd.nFileSizeLow; + cluster->availdemos[cluster->availdemoscount].time = ffd.ftLastWriteTime.dwHighDateTime; + cluster->availdemos[cluster->availdemoscount].smalltime = ffd.ftLastWriteTime.dwLowDateTime; + cluster->availdemoscount++; + } while(FindNextFile(h, &ffd)); + FindClose(h); + } + } +#else + { + DIR *dir; + struct dirent *ent; + struct stat sb; + char fullname[512]; + dir = opendir(cluster->demodir); //yeek! + if (dir) + { + for(;;) + { + ent = readdir(dir); + if (!ent) + break; + if (*ent->d_name == '.') + continue; //ignore 'hidden' files + snprintf(fullname, sizeof(fullname), "%s%s", cluster->demodir, ent->d_name); + if (stat(fullname, &sb)) + continue; //some kind of error + strncpy(cluster->availdemos[cluster->availdemoscount].name, ent->d_name, sizeof(cluster->availdemos[0].name)); + cluster->availdemos[cluster->availdemoscount].size = sb.st_size; + cluster->availdemos[cluster->availdemoscount].time = sb.st_mtime; + cluster->availdemoscount++; + } + closedir(dir); + } + else + Sys_Printf(cluster, "Couldn't open dir for demo listings\n"); + } +#endif + + qsort(cluster->availdemos, cluster->availdemoscount, sizeof(cluster->availdemos[0]), SortFilesByDate); +} + +void Cluster_Run(cluster_t *cluster, qboolean dowait) +{ + oproxy_t *pend, *pend2, *pend3; + sv_t *sv, *old; + + int m; + struct timeval timeout; + fd_set socketset; + + if (dowait) + { + + 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->usequakeworldprotocols && 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 + + if (cluster->viewserver) + { + timeout.tv_sec = 0; + timeout.tv_usec = 1000; + } + else + { + timeout.tv_sec = 10/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 + cluster->inputlength--; + + 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 + } + + + + cluster->curtime = Sys_Milliseconds(); + + for (sv = cluster->servers; sv; ) + { + old = sv; + sv = sv->next; + QTV_Run(old); + } + + SV_FindProxies(cluster->tcpsocket, cluster, NULL); //look for any other proxies wanting to muscle in on the action. + + QW_UpdateUDPStuff(cluster); + + while(cluster->pendingproxies) + { + pend2 = cluster->pendingproxies->next; + if (SV_ReadPendingProxy(cluster, cluster->pendingproxies)) + cluster->pendingproxies = pend2; + else + break; + } + if (cluster->pendingproxies) + { + for(pend = cluster->pendingproxies; pend && pend->next; ) + { + pend2 = pend->next; + pend3 = pend2->next; + if (SV_ReadPendingProxy(cluster, pend2)) + { + pend->next = pend3; + pend = pend3; + } + else + { + pend = pend2; + } + } + } +} + + + + + +void DoCommandLine(cluster_t *cluster, int argc, char **argv) +{ + int i; + char commandline[8192]; + char *start, *end, *result; + char buffer[8192]; + + result = Rcon_Command(cluster, NULL, "exec qtv.cfg", buffer, sizeof(buffer), true); + Sys_Printf(cluster, "%s", result); + + 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; + } + Sys_Printf(cluster, "\n"); +} + +int main(int argc, char **argv) +{ + cluster_t cluster; + +// soundtest(); + +#ifdef SIGPIPE + signal(SIGPIPE, SIG_IGN); +#endif + +#ifdef _WIN32 + { + WSADATA discard; + WSAStartup(MAKEWORD(2,0), &discard); + } +#endif + + memset(&cluster, 0, sizeof(cluster)); + + cluster.qwdsocket = INVALID_SOCKET; + cluster.tcpsocket = INVALID_SOCKET; + cluster.qwlistenportnum = 0; + cluster.allownqclients = true; + strcpy(cluster.hostname, DEFAULT_HOSTNAME); + cluster.buildnumber = build_number(); + cluster.maxproxies = -1; + + strcpy(cluster.demodir, "qw/demos/"); + + Sys_Printf(&cluster, "QTV Build %i.\n", cluster.buildnumber); + + 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 udp port %i\n", cluster.qwlistenportnum); + } + if (cluster.tcpsocket == INVALID_SOCKET && !cluster.tcplistenportnum) + { + cluster.tcpsocket = Net_MVDListen(cluster.tcplistenportnum = 27599); + if (cluster.tcpsocket != INVALID_SOCKET) + Sys_Printf(&cluster, "opened tcp port %i\n", cluster.tcplistenportnum); + } + + Sys_Printf(&cluster, "\n" + "Welcome to FTEQTV\n" + "Please type\n" + "qtv server:port\n" + " to connect to a tcp server.\n" + "qw server:port\n" + " to connect to a regular qw server.\n" + "demo qw/example.mvd\n" + " to play a demo from an mvd.\n" + "\n"); + } + +// Cluster_BuildAvailableDemoList(&cluster); + + while (!cluster.wanttoexit) + { + Cluster_Run(&cluster, true); +#ifdef VIEWER + DemoViewer_Update(cluster.viewserver); +#endif + } + + return 0; +} + +void QTV_Printf(sv_t *qtv, char *fmt, ...) +{ + va_list argptr; + char string[2048]; + + va_start (argptr, fmt); + vsnprintf (string, sizeof(string)-1, fmt,argptr); + string[sizeof(string)-1] = 0; + va_end (argptr); + + if (qtv->silentstream) + return; + + Sys_Printf(qtv->cluster, "%s", string); +} + +void Sys_Printf(cluster_t *cluster, char *fmt, ...) +{ + va_list argptr; + char string[2048]; + unsigned char *t; + + va_start (argptr, fmt); + vsnprintf (string, sizeof(string)-1, fmt,argptr); + string[sizeof(string)-1] = 0; + va_end (argptr); + + for (t = (unsigned char*)string; *t; t++) + { + if (*t >= 146 && *t < 156) + *t = *t - 146 + '0'; + if (*t == 143) + *t = '.'; + if (*t == 157 || *t == 158 || *t == 159) + *t = '-'; + if (*t >= 128) + *t -= 128; + if (*t == 16) + *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", string); +} + diff --git a/fteqtv/crc.c b/fteqtv/crc.c index 8102831a8..b484ac24e 100644 --- a/fteqtv/crc.c +++ b/fteqtv/crc.c @@ -1,94 +1,95 @@ -/* -Copyright (C) 1996-1997 Id Software, Inc. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ -/* crc.c */ - -#include "qtv.h" - - - -// this is a 16 bit, non-reflected CRC using the polynomial 0x1021 -// and the initial and final xor values shown below... in other words, the -// CCITT standard CRC used by XMODEM - -#define QCRC_INIT_VALUE 0xffff -#define QCRC_XOR_VALUE 0x0000 - -static unsigned short crctable[256] = -{ - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, - 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, - 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, - 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, - 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, - 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, - 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, - 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, - 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, - 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, - 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, - 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, - 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, - 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, - 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, - 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, - 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, - 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, - 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, - 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, - 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, - 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, - 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 -}; - -void QCRC_Init(unsigned short *crcvalue) -{ - *crcvalue = QCRC_INIT_VALUE; -} - -void QCRC_ProcessByte(unsigned short *crcvalue, unsigned char data) -{ - *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; -} - -unsigned short QCRC_Value(unsigned short crcvalue) -{ - return crcvalue ^ QCRC_XOR_VALUE; -} - -unsigned short QCRC_Block (unsigned char *start, int count) -{ - unsigned short crc; - - QCRC_Init (&crc); - while (count--) - crc = (crc << 8) ^ crctable[(crc >> 8) ^ *start++]; - - return crc; -} - +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +/* crc.c */ + +#include "qtv.h" + + + +// this is a 16 bit, non-reflected CRC using the polynomial 0x1021 +// and the initial and final xor values shown below... in other words, the +// CCITT standard CRC used by XMODEM + +#define QCRC_INIT_VALUE 0xffff +#define QCRC_XOR_VALUE 0x0000 + +static unsigned short crctable[256] = +{ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +void QCRC_Init(unsigned short *crcvalue) +{ + *crcvalue = QCRC_INIT_VALUE; +} + +void QCRC_ProcessByte(unsigned short *crcvalue, unsigned char data) +{ + *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; +} + +unsigned short QCRC_Value(unsigned short crcvalue) +{ + return crcvalue ^ QCRC_XOR_VALUE; +} + +unsigned short QCRC_Block (void *start, int count) +{ + unsigned char *data = start; + unsigned short crc; + + QCRC_Init (&crc); + while (count--) + crc = (crc << 8) ^ crctable[(crc >> 8) ^ *data++]; + + return crc; +} + diff --git a/fteqtv/forward.c b/fteqtv/forward.c index c90f8a9c9..b239aaaef 100644 --- a/fteqtv/forward.c +++ b/fteqtv/forward.c @@ -1,889 +1,1001 @@ -/* -Copyright (C) 1996-1997 Id Software, Inc. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the included (GNU.txt) GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -/* -This is the file responsible for handling incoming tcp connections. -This includes mvd recording. -Password checks and stuff are implemented here. This is server side stuff. - -*/ - -#include "qtv.h" -#include "time.h" - - -#undef IN -#define IN(x) buffer[(x)&(MAX_PROXY_BUFFER-1)] - -void CheckMVDConsistancy(unsigned char *buffer, int pos, int size) -{ -/* - int length; - int msec, type; - while(pos < size) - { - msec = IN(pos++); - type = IN(pos++); - if (type == dem_set) - { - pos+=8; - continue; - } - if (type == dem_multiple) - pos+=4; - length = (IN(pos+0)<<0) + (IN(pos+1)<<8) + (IN(pos+2)<<16) + (IN(pos+3)<<24); - pos+=4; - if (length > 1450) - printf("too big (%i)\n", length); - pos += length; - } - if (pos != size) - printf("pos != size\n"); - */ -} - - - - - -void SV_FindProxies(SOCKET sock, cluster_t *cluster, sv_t *defaultqtv) -{ - oproxy_t *prox; - - sock = accept(sock, NULL, NULL); - if (sock == INVALID_SOCKET) - return; - - if (cluster->maxproxies >= 0 && cluster->numproxies >= 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); - closesocket(sock); - return; - } - - prox = malloc(sizeof(*prox)); - if (!prox) - {//out of mem? - closesocket(sock); - return; - } - memset(prox, 0, sizeof(*prox)); - prox->sock = sock; - prox->file = NULL; - - cluster->numproxies++; - - prox->droptime = cluster->curtime + 5*1000; -#if 1 - prox->defaultstream = defaultqtv; - - prox->next = cluster->pendingproxies; - cluster->pendingproxies = prox; -#else - prox->next = qtv->pendingproxies; - qtv->pendingproxies = prox; - Net_SendConnectionMVD(qtv, prox); -#endif -} - - - -void Net_TryFlushProxyBuffer(cluster_t *cluster, oproxy_t *prox) -{ - char *buffer; - int length; - int bufpos; - - if (prox->drop) - return; - - while (prox->bufferpos >= MAX_PROXY_BUFFER) - { //so we never get any issues with wrapping.. - prox->bufferpos -= MAX_PROXY_BUFFER; - prox->buffersize -= MAX_PROXY_BUFFER; - } - - bufpos = prox->bufferpos&(MAX_PROXY_BUFFER-1); - length = prox->buffersize - prox->bufferpos; - if (length > MAX_PROXY_BUFFER-bufpos) //cap the length correctly. - length = MAX_PROXY_BUFFER-bufpos; - if (!length) - return; //already flushed. - buffer = prox->buffer + bufpos; - -// CheckMVDConsistancy(prox->buffer, prox->bufferpos, prox->buffersize); - - if (bufpos+length > MAX_PROXY_BUFFER) - Sys_Printf(cluster, "oversize flush\n"); - - if (prox->file) - length = fwrite(buffer, 1, length, prox->file); - else - length = send(prox->sock, buffer, length, 0); - - - switch (length) - { - case 0: //eof / they disconnected - prox->drop = true; - break; - case -1: - if (qerrno != EWOULDBLOCK && qerrno != EAGAIN) //not a problem, so long as we can flush it later. - { - Sys_Printf(cluster, "oversize flush\n"); - prox->drop = true; //drop them if we get any errors - } - break; - default: - prox->bufferpos += 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(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 - printf("QTV client is too lagged\n"); - prox->flushing = true; - return; - } - } -#if 1 - //just simple - prox->buffersize+=length; - for (wrap = prox->buffersize-length; wrap < prox->buffersize; wrap++) - prox->buffer[wrap&(MAX_PROXY_BUFFER-1)] = *buffer++; -#else - //we don't do multiple wrappings, the above check cannot succeed if it were required. - - //find the wrap point - wrap = prox->buffersize-(prox->buffersize&(MAX_PROXY_BUFFER-1)) + MAX_PROXY_BUFFER; - wrap = wrap - (prox->buffersize&(MAX_PROXY_BUFFER-1)); //the ammount of data we can fit before wrapping. - - if (wrap > length) - { //we don't wrap afterall - memcpy(prox->buffer+(prox->buffersize)&(MAX_PROXY_BUFFER-1), buffer, length); - prox->buffersize+=length; - return; - } - memcpy(prox->buffer+prox->buffersize&(MAX_PROXY_BUFFER-1), buffer, wrap); - buffer += wrap; - length -= wrap; - memcpy(prox->buffer, buffer, length); - - prox->buffersize+=length; -#endif -} - -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]; - InitNetMsg(&msg, tbuf, sizeof(tbuf)); - WriteByte(&msg, 0); - WriteByte(&msg, dem_type); - WriteLong(&msg, length); - if (dem_type == dem_multiple) - WriteLong(&msg, playermask); - - if (prox->buffersize-prox->bufferpos + length + msg.cursize > MAX_PROXY_BUFFER) - { - Net_TryFlushProxyBuffer(cluster, prox); //try flushing - if (prox->buffersize-prox->bufferpos + length + msg.cursize > MAX_PROXY_BUFFER) //damn, still too big. - { //they're too slow. hopefully it was just momentary lag - prox->flushing = true; - return; - } - } - - - Net_ProxySend(cluster, prox, msg.data, msg.cursize); - - Net_ProxySend(cluster, prox, buf, length); -} - -void Prox_SendPlayerStats(sv_t *qtv, oproxy_t *prox) -{ - char buffer[MAX_MSGLEN]; - netmsg_t msg; - int player, snum; - - InitNetMsg(&msg, buffer, sizeof(buffer)); - - for (player = 0; player < MAX_CLIENTS; player++) - { - for (snum = 0; snum < MAX_STATS; snum++) - { - if (qtv->players[player].stats[snum]) - { - if ((unsigned)qtv->players[player].stats[snum] > 255) - { - WriteByte(&msg, svc_updatestatlong); - WriteByte(&msg, snum); - WriteLong(&msg, qtv->players[player].stats[snum]); - } - else - { - WriteByte(&msg, svc_updatestat); - WriteByte(&msg, snum); - WriteByte(&msg, qtv->players[player].stats[snum]); - } - } - } - - if (msg.cursize) - { -// Prox_SendMessage(prox, msg.data, msg.cursize, dem_stats|(player<<3), (1<players[i].active) // interesting, is this set to false if player disconnect from server? - continue; - - flags = (DF_ORIGIN << 0) | (DF_ORIGIN << 1) | (DF_ORIGIN << 2) - | (DF_ANGLES << 0) | (DF_ANGLES << 1) | (DF_ANGLES << 2) // angles is something what changed frequently, so may be not send it? - | DF_EFFECTS - | DF_SKINNUM // though it rare thingie, so better send it? - | (qtv->players[i].dead ? DF_DEAD : 0) - | (qtv->players[i].gibbed ? DF_GIB : 0) - | DF_WEAPONFRAME // do we so really need it? - | DF_MODEL; // generally, that why we wrote this function, so YES send this - - if (*qtv->players[i].userinfo && atoi(Info_ValueForKey(qtv->players[i].userinfo, "*spectator", buffer, sizeof(buffer)))) - flags = DF_MODEL; // oh, that spec, just sent his model, may be even better ignore him? - - WriteByte (msg, svc_playerinfo); - WriteByte (msg, i); - WriteShort (msg, flags); - - WriteByte (msg, qtv->players[i].current.frame); // always sent - - for (j = 0 ; j < 3 ; j++) - if (flags & (DF_ORIGIN << j)) - WriteShort (msg, qtv->players[i].current.origin[j]); - - for (j = 0 ; j < 3 ; j++) - if (flags & (DF_ANGLES << j)) - WriteShort (msg, qtv->players[i].current.angles[j]); - - if (flags & DF_MODEL) // generally, that why we wrote this function, so YES send this - WriteByte (msg, qtv->players[i].current.modelindex); - - if (flags & DF_SKINNUM) - WriteByte (msg, qtv->players[i].current.skinnum); - - if (flags & DF_EFFECTS) - WriteByte (msg, qtv->players[i].current.effects); - - if (flags & DF_WEAPONFRAME) - WriteByte (msg, qtv->players[i].current.weaponframe); - } -} - -void Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox) -{ - char buffer[MAX_MSGLEN*8]; - netmsg_t msg; - int prespawn; - - if (!*qtv->mapname) - return; - - InitNetMsg(&msg, buffer, sizeof(buffer)); - - prox->flushing = false; - - BuildServerData(qtv, &msg, 0, NULL); - 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(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(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1); - msg.cursize = 0; - } - - 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, MAX_CLIENTS-1); - - Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1); - msg.cursize = 0; - } - - //playerstates are delta-compressed, unfortunatly this isn't qwd (thanks to qqshka for showing my folly) - Prox_SendInitialPlayers(qtv, prox, &msg); - Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1); - msg.cursize = 0; - - //we do need to send entity states. - Prox_SendInitialEnts(qtv, prox, &msg); - 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(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1); - msg.cursize = 0; - - Net_TryFlushProxyBuffer(qtv->cluster, prox); - - Prox_SendPlayerStats(qtv, prox); - Net_TryFlushProxyBuffer(qtv->cluster, prox); - - Net_ProxySend(qtv->cluster, prox, qtv->buffer, qtv->forwardpoint); //send all the info we've not yet processed. - - - if (prox->flushing) - { - Sys_Printf(qtv->cluster, "Connection data is too big, dropping proxy client\n"); - prox->drop = true; //this is unfortunate... - } - else - Net_TryFlushProxyBuffer(qtv->cluster, prox); -} - - -oproxy_t *Net_FileProxy(sv_t *qtv, char *filename) -{ - oproxy_t *prox; - FILE *f; - - f = fopen(filename, "wb"); - if (!f) - return NULL; - - //no full proxy check, this is going to be used by proxy admins, who won't want to have to raise the limit to start recording. - - prox = malloc(sizeof(*prox)); - if (!prox) - return NULL; - memset(prox, 0, sizeof(*prox)); - - prox->sock = INVALID_SOCKET; - prox->file = f; - - prox->next = qtv->proxies; - qtv->proxies = prox; - - qtv->cluster->numproxies++; - - Net_SendConnectionMVD(qtv, prox); - - return prox; -} - -qboolean Net_StopFileProxy(sv_t *qtv) -{ - oproxy_t *prox; - for (prox = qtv->proxies; prox; prox = prox->next) - { - if (prox->file) - { - prox->drop = true; - return true; - } - } - return false; -} - - - -void SV_ForwardStream(sv_t *qtv, char *buffer, int length) -{ //forward the stream on to connected clients - oproxy_t *prox, *next, *fre; - - CheckMVDConsistancy(buffer, 0, length); - - - while (qtv->proxies && qtv->proxies->drop) - { - next = qtv->proxies->next; - fre = qtv->proxies; - if (fre->file) - fclose(fre->file); - else - closesocket(fre->sock); - free(fre); - qtv->cluster->numproxies--; - qtv->proxies = next; - } - - for (prox = qtv->proxies; prox; prox = prox->next) - { - while (prox->next && prox->next->drop) - { - next = prox->next->next; - fre = prox->next; - if (fre->file) - fclose(fre->file); - else - closesocket(fre->sock); - if (fre->srcfile) - fclose(fre->srcfile); - free(fre); - qtv->cluster->numproxies--; - prox->next = next; - } - - if (prox->flushing) //don't send it if we're trying to empty thier buffer. - { - if (prox->buffersize == prox->bufferpos) - { - if (!qtv->parsingconnectiondata) - Net_SendConnectionMVD(qtv, prox); //they're up to date, resend the connection info. - } - else - { - Net_TryFlushProxyBuffer(qtv->cluster, prox); //try and flush it. - continue; - } - } - - if (prox->drop) - continue; - - //add the new data - Net_ProxySend(qtv->cluster, prox, buffer, length); - - Net_TryFlushProxyBuffer(qtv->cluster, prox); -// Net_TryFlushProxyBuffer(qtv->cluster, prox); -// Net_TryFlushProxyBuffer(qtv->cluster, prox); - } -} - -//returns true if the pending proxy should be unlinked -//truth does not imply that it should be freed/released, just unlinked. -qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend) -{ - char tempbuf[512]; - char *s; - char *e; - char *colon; - float clientversion = 0; - int len; - int headersize; - qboolean raw; - sv_t *qtv; - - if (pend->drop) - { - if (pend->srcfile) - fclose(pend->srcfile); - closesocket(pend->sock); - free(pend); - cluster->numproxies--; - return true; - } -#define QTVSVHEADER "QTVSV 1.1\n" - - Net_TryFlushProxyBuffer(cluster, pend); - - if (pend->flushing) - { - if (pend->bufferpos == pend->buffersize) - { - if (pend->srcfile) - { - char buffer[4096]; - len = fread(buffer, 1, sizeof(buffer), pend->srcfile); - if (!len) - { - fclose(pend->srcfile); - pend->srcfile = NULL; - } - Net_ProxySend(cluster, pend, buffer, len); - return false; //don't try reading anything yet - } - else - { - pend->drop = true; - return false; - } - } - else - return false; - } - - if (pend->droptime < cluster->curtime) - { - pend->drop = true; - return false; - } - - len = sizeof(pend->inbuffer) - pend->inbuffersize - 1; - len = recv(pend->sock, pend->inbuffer+pend->inbuffersize, len, 0); - if (len == 0) - { - pend->drop = true; - return false; - } - if (len < 0) - return false; - - pend->inbuffersize += len; - pend->inbuffer[pend->inbuffersize] = '\0'; - - if (pend->inbuffersize >= 4) - { - if (strncmp(pend->inbuffer, "QTV\r", 4) && strncmp(pend->inbuffer, "QTV\n", 4) && strncmp(pend->inbuffer, "GET ", 4) && strncmp(pend->inbuffer, "POST ", 5)) - { //I have no idea what the smeg you are. - pend->drop = true; - return false; - } - } - - //make sure there's a double \n somewhere - - for (s = pend->inbuffer; *s; s++) - { - if (s[0] == '\n' && (s[1] == '\n' || (s[1] == '\r' && s[2] == '\n'))) - break; - } - if (!*s) - return false; //don't have enough yet - s+=3; - headersize = s - pend->inbuffer - 1; - - if (!strncmp(pend->inbuffer, "POST ", 5)) - { - HTTPSV_PostMethod(cluster, pend, s); - - return false; //not keen on this.. - } - else if (!strncmp(pend->inbuffer, "GET ", 4)) - { +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the included (GNU.txt) GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* +This is the file responsible for handling incoming tcp connections. +This includes mvd recording. +Password checks and stuff are implemented here. This i server side stuff. + +*/ + +#include "qtv.h" +#include "time.h" + + +#undef IN +#define IN(x) buffer[(x)&(MAX_PROXY_BUFFER-1)] + +void CheckMVDConsistancy(unsigned char *buffer, int pos, int size) +{ +/* + int length; + int msec, type; + while(pos < size) + { + msec = IN(pos++); + type = IN(pos++); + if (type == dem_set) + { + pos+=8; + continue; + } + if (type == dem_multiple) + pos+=4; + length = (IN(pos+0)<<0) + (IN(pos+1)<<8) + (IN(pos+2)<<16) + (IN(pos+3)<<24); + pos+=4; + if (length > 1450) + printf("too big (%i)\n", length); + pos += length; + } + if (pos != size) + printf("pos != size\n"); + */ +} + + + + + +void SV_FindProxies(SOCKET sock, cluster_t *cluster, sv_t *defaultqtv) +{ + unsigned long nonblocking = true; + oproxy_t *prox; + + sock = accept(sock, NULL, NULL); + if (sock == INVALID_SOCKET) + return; + + if (ioctlsocket (sock, FIONBIO, &nonblocking) == -1) + { + Sys_Printf(cluster, "failed to set client socket to nonblocking. dropping.\n"); + closesocket(sock); //failed... + return; + } + + if (cluster->maxproxies >= 0 && cluster->numproxies >= 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); + closesocket(sock); + return; + } + + prox = malloc(sizeof(*prox)); + if (!prox) + {//out of mem? + closesocket(sock); + return; + } + memset(prox, 0, sizeof(*prox)); + prox->sock = sock; + prox->file = NULL; + + cluster->numproxies++; + + prox->droptime = cluster->curtime + 5*1000; +#if 1 + prox->defaultstream = defaultqtv; + + prox->next = cluster->pendingproxies; + cluster->pendingproxies = prox; +#else + prox->next = qtv->pendingproxies; + qtv->pendingproxies = prox; + Net_SendConnectionMVD(qtv, prox); +#endif +} + + +void Fwd_ParseCommands(cluster_t *cluster, oproxy_t *prox) +{ + netmsg_t buf; + int packetlength; + int bytes; + bytes = recv(prox->sock, prox->inbuffer+prox->inbuffersize, sizeof(prox->inbuffer)-prox->inbuffersize, 0); + if (bytes < 0) + { + if (qerrno != EWOULDBLOCK && qerrno != EAGAIN) //not a problem, so long as we can flush it later. + { + Sys_Printf(cluster, "network error from client proxy\n"); + prox->drop = true; //drop them if we get any errors + return; + } + bytes = 0; + } + else if (bytes == 0) + { + prox->drop = true; + return; + } + + prox->inbuffersize += bytes; + + for(;;) + { + if (prox->inbuffersize < 2) //we do need at least 3 bytes for anything useful + break; + + packetlength = prox->inbuffer[0] + (prox->inbuffer[1]<<8); + if (packetlength+2 > prox->inbuffersize) + break; + + InitNetMsg(&buf, prox->inbuffer+2, packetlength); + buf.cursize = packetlength; + + while(buf.readpos < buf.cursize) + { + switch (ReadByte(&buf)) + { + case qtv_clc_stringcmd: + { + char stringbuf[1024]; + ReadString(&buf, stringbuf, sizeof(stringbuf)); + QTV_Printf(prox->stream, "ds: %s\n", stringbuf); + } + + break; + default: + Sys_Printf(cluster, "Received unrecognised packet type from downstream proxy.\n"); + buf.readpos = buf.cursize; + break; + } + } + packetlength+=2; + memmove(prox->inbuffer, prox->inbuffer+packetlength, prox->inbuffersize - packetlength); + prox->inbuffersize -= packetlength; + } +} + +void Net_TryFlushProxyBuffer(cluster_t *cluster, oproxy_t *prox) +{ + unsigned char *buffer; + int length; + int bufpos; + + if (prox->drop) + return; + + while (prox->bufferpos >= MAX_PROXY_BUFFER) + { //so we never get any issues with wrapping.. + prox->bufferpos -= MAX_PROXY_BUFFER; + prox->buffersize -= MAX_PROXY_BUFFER; + } + + bufpos = prox->bufferpos&(MAX_PROXY_BUFFER-1); + length = prox->buffersize - prox->bufferpos; + if (length > MAX_PROXY_BUFFER-bufpos) //cap the length correctly. + length = MAX_PROXY_BUFFER-bufpos; + if (!length) + return; //already flushed. + buffer = prox->buffer + bufpos; + +// CheckMVDConsistancy(prox->buffer, prox->bufferpos, prox->buffersize); + + if (bufpos+length > MAX_PROXY_BUFFER) + Sys_Printf(cluster, "oversize flush\n"); + + if (prox->file) + length = fwrite(buffer, 1, length, prox->file); + else + length = send(prox->sock, buffer, length, 0); + + + switch (length) + { + case 0: //eof / they disconnected + prox->drop = true; + break; + case -1: + if (qerrno != EWOULDBLOCK && qerrno != EAGAIN) //not a problem, so long as we can flush it later. + { + Sys_Printf(cluster, "network error from client proxy\n"); + prox->drop = true; //drop them if we get any errors + } + break; + default: + prox->bufferpos += length; + } +} + +void Net_ProxySendString(cluster_t *cluster, oproxy_t *prox, void *buffer) +{ + Net_ProxySend(cluster, prox, buffer, strlen(buffer)); +} + +void Net_ProxySend(cluster_t *cluster, oproxy_t *prox, void *buffer, int length) +{ + int wrap; + + if (prox->buffersize-prox->bufferpos + length > MAX_PROXY_BUFFER) + { + 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 + printf("QTV client is too lagged\n"); + prox->flushing = true; + return; + } + } +#if 1 + //just simple + prox->buffersize+=length; + for (wrap = prox->buffersize-length; wrap < prox->buffersize; wrap++) + { + prox->buffer[wrap&(MAX_PROXY_BUFFER-1)] = *(unsigned char*)buffer; + buffer = (char*)buffer+1; + } +#else + //we don't do multiple wrappings, the above check cannot succeed if it were required. + + //find the wrap point + wrap = prox->buffersize-(prox->buffersize&(MAX_PROXY_BUFFER-1)) + MAX_PROXY_BUFFER; + wrap = wrap - (prox->buffersize&(MAX_PROXY_BUFFER-1)); //the ammount of data we can fit before wrapping. + + if (wrap > length) + { //we don't wrap afterall + memcpy(prox->buffer+(prox->buffersize)&(MAX_PROXY_BUFFER-1), buffer, length); + prox->buffersize+=length; + return; + } + memcpy(prox->buffer+prox->buffersize&(MAX_PROXY_BUFFER-1), buffer, wrap); + buffer += wrap; + length -= wrap; + memcpy(prox->buffer, buffer, length); + + prox->buffersize+=length; +#endif +} + +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]; + InitNetMsg(&msg, tbuf, sizeof(tbuf)); + WriteByte(&msg, 0); + WriteByte(&msg, dem_type); + WriteLong(&msg, length); + if (dem_type == dem_multiple) + WriteLong(&msg, playermask); + + if (prox->buffersize-prox->bufferpos + length + msg.cursize > MAX_PROXY_BUFFER) + { + Net_TryFlushProxyBuffer(cluster, prox); //try flushing + if (prox->buffersize-prox->bufferpos + length + msg.cursize > MAX_PROXY_BUFFER) //damn, still too big. + { //they're too slow. hopefully it was just momentary lag + prox->flushing = true; + return; + } + } + + + Net_ProxySend(cluster, prox, msg.data, msg.cursize); + + Net_ProxySend(cluster, prox, buf, length); +} + +void Fwd_SendDownstream(sv_t *qtv, void *buffer, int length) +{ //broadcasts data to all client proxies, with dont-buffer + oproxy_t *prox; + for (prox = qtv->proxies; prox; prox = prox->next) + { + Prox_SendMessage(qtv->cluster, prox, buffer, length, dem_qtvdata, (unsigned int)-1); + } +} + +void Fwd_SayToDownstream(sv_t *qtv, char *message) +{ + netmsg_t msg; + char buffer[1024]; + + InitNetMsg(&msg, buffer, sizeof(buffer)); + WriteByte(&msg, svc_print); + WriteByte(&msg, PRINT_CHAT); + WriteString2(&msg, "[QTV]"); + WriteString(&msg, message); + + Fwd_SendDownstream(qtv, msg.data, msg.cursize); +} + +void Prox_SendPlayerStats(sv_t *qtv, oproxy_t *prox) +{ + char buffer[MAX_MSGLEN]; + netmsg_t msg; + int player, snum; + + InitNetMsg(&msg, buffer, sizeof(buffer)); + + for (player = 0; player < MAX_CLIENTS; player++) + { + for (snum = 0; snum < MAX_STATS; snum++) + { + if (qtv->players[player].stats[snum]) + { + if ((unsigned)qtv->players[player].stats[snum] > 255) + { + WriteByte(&msg, svc_updatestatlong); + WriteByte(&msg, snum); + WriteLong(&msg, qtv->players[player].stats[snum]); + } + else + { + WriteByte(&msg, svc_updatestat); + WriteByte(&msg, snum); + WriteByte(&msg, qtv->players[player].stats[snum]); + } + } + } + + if (msg.cursize) + { +// Prox_SendMessage(prox, msg.data, msg.cursize, dem_stats|(player<<3), (1<players[i].active) // interesting, is this set to false if player disconnect from server? + continue; + + flags = (DF_ORIGIN << 0) | (DF_ORIGIN << 1) | (DF_ORIGIN << 2) + | (DF_ANGLES << 0) | (DF_ANGLES << 1) | (DF_ANGLES << 2) // angles is something what changed frequently, so may be not send it? + | DF_EFFECTS + | DF_SKINNUM // though it rare thingie, so better send it? + | (qtv->players[i].dead ? DF_DEAD : 0) + | (qtv->players[i].gibbed ? DF_GIB : 0) + | DF_WEAPONFRAME // do we so really need it? + | DF_MODEL; // generally, that why we wrote this function, so YES send this + + if (*qtv->players[i].userinfo && atoi(Info_ValueForKey(qtv->players[i].userinfo, "*spectator", buffer, sizeof(buffer)))) + flags = DF_MODEL; // oh, that spec, just sent his model, may be even better ignore him? + + WriteByte (msg, svc_playerinfo); + WriteByte (msg, i); + WriteShort (msg, flags); + + WriteByte (msg, qtv->players[i].current.frame); // always sent + + for (j = 0 ; j < 3 ; j++) + if (flags & (DF_ORIGIN << j)) + WriteShort (msg, qtv->players[i].current.origin[j]); + + for (j = 0 ; j < 3 ; j++) + if (flags & (DF_ANGLES << j)) + WriteShort (msg, qtv->players[i].current.angles[j]); + + if (flags & DF_MODEL) // generally, that why we wrote this function, so YES send this + WriteByte (msg, qtv->players[i].current.modelindex); + + if (flags & DF_SKINNUM) + WriteByte (msg, qtv->players[i].current.skinnum); + + if (flags & DF_EFFECTS) + WriteByte (msg, qtv->players[i].current.effects); + + if (flags & DF_WEAPONFRAME) + WriteByte (msg, qtv->players[i].current.weaponframe); + } +} + +void Net_GreetingMessage(oproxy_t *prox) +{ + char buffer[1024]; + netmsg_t msg; + + InitNetMsg(&msg, buffer, sizeof(buffer)); + WriteByte(&msg, svc_print); + WriteByte(&msg, PRINT_HIGH); + WriteString2(&msg, "Welcome to "); + WriteString2(&msg, prox->stream->cluster->hostname); + WriteString(&msg, "\n"); + + Prox_SendMessage(prox->stream->cluster, prox, msg.data, msg.cursize, dem_qtvdata, (unsigned)-1); +} + +void Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox) +{ + char buffer[MAX_MSGLEN*8]; + netmsg_t msg; + int prespawn; + + if (!*qtv->mapname) + return; + + InitNetMsg(&msg, buffer, sizeof(buffer)); + + prox->flushing = false; + + BuildServerData(qtv, &msg, 0, NULL); + 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(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(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1); + msg.cursize = 0; + } + + 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, MAX_CLIENTS-1); + + Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1); + msg.cursize = 0; + } + + //playerstates are delta-compressed, unfortunatly this isn't qwd (thanks to qqshka for showing my folly) + Prox_SendInitialPlayers(qtv, prox, &msg); + Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1); + msg.cursize = 0; + + //we do need to send entity states. + Prox_SendInitialEnts(qtv, prox, &msg); + 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(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1); + msg.cursize = 0; + + Net_TryFlushProxyBuffer(qtv->cluster, prox); + + Prox_SendPlayerStats(qtv, prox); + Net_TryFlushProxyBuffer(qtv->cluster, prox); + + if (!qtv->cluster->lateforward) + Net_ProxySend(qtv->cluster, prox, qtv->buffer, qtv->forwardpoint); //send all the info we've not yet processed (but have already forwarded). + + + if (prox->flushing) + { + Sys_Printf(qtv->cluster, "Connection data is too big, dropping proxy client\n"); + prox->drop = true; //this is unfortunate... + } + else + Net_TryFlushProxyBuffer(qtv->cluster, prox); +} + + +oproxy_t *Net_FileProxy(sv_t *qtv, char *filename) +{ + oproxy_t *prox; + FILE *f; + + f = fopen(filename, "wb"); + if (!f) + return NULL; + + //no full proxy check, this is going to be used by proxy admins, who won't want to have to raise the limit to start recording. + + prox = malloc(sizeof(*prox)); + if (!prox) + return NULL; + memset(prox, 0, sizeof(*prox)); + + prox->sock = INVALID_SOCKET; + prox->file = f; + + prox->next = qtv->proxies; + qtv->proxies = prox; + + qtv->cluster->numproxies++; + + Net_SendConnectionMVD(qtv, prox); + + return prox; +} + +qboolean Net_StopFileProxy(sv_t *qtv) +{ + oproxy_t *prox; + for (prox = qtv->proxies; prox; prox = prox->next) + { + if (prox->file) + { + prox->drop = true; + return true; + } + } + return false; +} + + + +void SV_ForwardStream(sv_t *qtv, void *buffer, int length) +{ //forward the stream on to connected clients + oproxy_t *prox, *next, *fre; + + CheckMVDConsistancy(buffer, 0, length); + + + while (qtv->proxies && qtv->proxies->drop) + { + next = qtv->proxies->next; + fre = qtv->proxies; + if (fre->file) + fclose(fre->file); + else + closesocket(fre->sock); + free(fre); + qtv->cluster->numproxies--; + qtv->proxies = next; + } + + for (prox = qtv->proxies; prox; prox = prox->next) + { + while (prox->next && prox->next->drop) + { + next = prox->next->next; + fre = prox->next; + if (fre->file) + fclose(fre->file); + else + closesocket(fre->sock); + if (fre->srcfile) + fclose(fre->srcfile); + free(fre); + qtv->cluster->numproxies--; + prox->next = next; + } + + if (prox->flushing) //don't send it if we're trying to empty thier buffer. + { + if (prox->buffersize == prox->bufferpos) + { + if (!qtv->parsingconnectiondata) + Net_SendConnectionMVD(qtv, prox); //they're up to date, resend the connection info. + } + else + { + Net_TryFlushProxyBuffer(qtv->cluster, prox); //try and flush it. + continue; + } + } + + if (prox->drop) + continue; + + //add the new data + Net_ProxySend(qtv->cluster, prox, buffer, length); + + Net_TryFlushProxyBuffer(qtv->cluster, prox); +// Net_TryFlushProxyBuffer(qtv->cluster, prox); +// Net_TryFlushProxyBuffer(qtv->cluster, prox); + + +#warning This is not the place for this + if (prox->sock != INVALID_SOCKET) + { + Fwd_ParseCommands(qtv->cluster, prox); + } + } +} + +//returns true if the pending proxy should be unlinked +//truth does not imply that it should be freed/released, just unlinked. +qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend) +{ + char tempbuf[512]; + unsigned char *s; + unsigned char *e; + char *colon; + float clientversion = 0; + int len; + int headersize; + qboolean raw; + sv_t *qtv; + + if (pend->drop) + { + if (pend->srcfile) + fclose(pend->srcfile); + closesocket(pend->sock); + free(pend); + cluster->numproxies--; + return true; + } +#define QTVSVHEADER "QTVSV 1.1\n" + + Net_TryFlushProxyBuffer(cluster, pend); + + if (pend->flushing) + { + if (pend->bufferpos == pend->buffersize) + { + if (pend->srcfile) + { + char buffer[4096]; + len = fread(buffer, 1, sizeof(buffer), pend->srcfile); + if (!len) + { + fclose(pend->srcfile); + pend->srcfile = NULL; + } + Net_ProxySend(cluster, pend, buffer, len); + return false; //don't try reading anything yet + } + else + { + pend->drop = true; + return false; + } + } + else + return false; + } + + if (pend->droptime < cluster->curtime) + { + pend->drop = true; + return false; + } + + len = sizeof(pend->inbuffer) - pend->inbuffersize - 1; + len = recv(pend->sock, pend->inbuffer+pend->inbuffersize, len, 0); + if (len == 0) + { + pend->drop = true; + return false; + } + if (len < 0) + { + + return false; + } + + pend->inbuffersize += len; + pend->inbuffer[pend->inbuffersize] = '\0'; + + if (pend->inbuffersize >= 4) + { + if (ustrncmp(pend->inbuffer, "QTV\r", 4) && ustrncmp(pend->inbuffer, "QTV\n", 4) && ustrncmp(pend->inbuffer, "GET ", 4) && ustrncmp(pend->inbuffer, "POST ", 5)) + { //I have no idea what the smeg you are. + pend->drop = true; + + pend->inbuffer[16] = 0; + Sys_Printf(cluster, "Connect for unrecognised protocol %s\n", pend->inbuffer); + return false; + } + } + + //make sure there's a double \n somewhere + + for (s = pend->inbuffer; *s; s++) + { + if (s[0] == '\n' && (s[1] == '\n' || (s[1] == '\r' && s[2] == '\n'))) + break; + } + if (!*s) + return false; //don't have enough yet + s+=3; //Fixme: this is wrong + headersize = s - pend->inbuffer - 1; + + if (!ustrncmp(pend->inbuffer, "POST ", 5)) + { + HTTPSV_PostMethod(cluster, pend, (char*)s); + + return false; //not keen on this.. + } + else if (!ustrncmp(pend->inbuffer, "GET ", 4)) + { HTTPSV_GetMethod(cluster, pend); - - pend->flushing = true; - return false; - } - - raw = false; - - qtv = pend->defaultstream; - - e = pend->inbuffer; - s = e; - while(*e) - { - if (*e == '\n' || *e == '\r') - { - *e = '\0'; - colon = strchr(s, ':'); - if (*s) - { - if (!colon) - { - if (!strcmp(s, "QTV")) - { - //just a qtv request (as in, not http or some other protocol) - } - else if (!strcmp(s, "SOURCELIST")) - { //lists sources that are currently playing - s = QTVSVHEADER; - Net_ProxySend(cluster, pend, s, strlen(s)); - if (!cluster->servers) - { - s = "PERROR: No sources currently available\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - } - else - { - for (qtv = cluster->servers; qtv; qtv = qtv->next) - { - s = tempbuf; - if (clientversion > 1) - { - int plyrs = 0; - int i; - for (i = 0; i < MAX_CLIENTS; i++) - { - if (*qtv->players[i].userinfo) - plyrs++; - } - sprintf(tempbuf, "SRCSRV: %s\n", qtv->server); - Net_ProxySend(cluster, pend, s, strlen(s)); - sprintf(tempbuf, "SRCHOST: %s\n", qtv->hostname); - Net_ProxySend(cluster, pend, s, strlen(s)); - sprintf(tempbuf, "SRCPLYRS: %i\n", plyrs); - Net_ProxySend(cluster, pend, s, strlen(s)); - sprintf(tempbuf, "SRCVIEWS: %i\n", qtv->numviewers); - Net_ProxySend(cluster, pend, s, strlen(s)); - sprintf(tempbuf, "SRCID: %i\n", qtv->streamid); //final part of each source - Net_ProxySend(cluster, pend, s, strlen(s)); - } - else - { - sprintf(tempbuf, "ASOURCE: %i: %15s: %15s\n", qtv->streamid, qtv->server, qtv->hostname); - Net_ProxySend(cluster, pend, s, strlen(s)); - } - } - qtv = NULL; - } - s = "\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - pend->flushing = true; - } - else if (!strcmp(s, "REVERSE")) - { //this is actually a server trying to connect to us - //start up a new stream - - //FIXME: does this work? -#if 1 //left disabled until properly tested - qtv = QTV_NewServerConnection(cluster, "reverse"/*server*/, "", true, 2, false, 0); - - s = QTVSVHEADER; Net_ProxySend(cluster, pend, s, strlen(s)); - s = "REVERSED\n"; Net_ProxySend(cluster, pend, s, strlen(s)); - s = "VERSION: 1\n"; Net_ProxySend(cluster, pend, s, strlen(s)); - s = "\n"; Net_ProxySend(cluster, pend, s, strlen(s)); - - //switch over the socket to the actual source connection rather than the pending - Net_TryFlushProxyBuffer(cluster, pend); //flush anything... this isn't ideal, but should be small enough - qtv->sourcesock = pend->sock; - pend->sock = 0; - - memcpy(qtv->buffer, pend->inbuffer + headersize, pend->inbuffersize - headersize); - qtv->parsingqtvheader = true; - return false; -#endif - } - else if (!strcmp(s, "RECEIVE")) - { //a client connection request without a source - if (cluster->numservers == 1) - { //only one stream anyway - qtv = cluster->servers; - } - else - { //try and hunt down an explicit stream (rather than a user-recorded one) - int numfound = 0; - sv_t *suitable; - for (qtv = cluster->servers; qtv; qtv = qtv->next) - { - if (!qtv->disconnectwhennooneiswatching) - { - suitable = qtv; - numfound++; - } - } - if (numfound == 1) - qtv = suitable; - } - if (!qtv) - { - s = QTVSVHEADER; - Net_ProxySend(cluster, pend, s, strlen(s)); - s = "PERROR: Multiple streams are currently playing\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - s = "\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - pend->flushing = true; - } - } - else if (!strcmp(s, "DEMOLIST")) - { //lists sources that are currently playing - int i; - - Cluster_BuildAvailableDemoList(cluster); - - s = QTVSVHEADER; - Net_ProxySend(cluster, pend, s, strlen(s)); - if (!cluster->availdemoscount) - { - s = "PERROR: No demos currently available\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - } - else - { - for (i = 0; i < cluster->availdemoscount; i++) - { - sprintf(tempbuf, "ADEMO: %i: %15s\n", cluster->availdemos[i].size, cluster->availdemos[i].name); - s = tempbuf; - Net_ProxySend(cluster, pend, s, strlen(s)); - } - qtv = NULL; - } - s = "\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - pend->flushing = true; - } - else if (!strcmp(s, "AUTH")) - { //lists the demos available on this proxy - //part of the connection process, can be ignored if there's no password - } - else - printf("Unrecognised token in QTV connection request (%s)\n", s); - } - else - { - *colon++ = '\0'; - if (!strcmp(s, "VERSION")) - { - clientversion = atof(colon); - } - else if (!strcmp(s, "RAW")) - raw = atoi(colon); - /*else if (!strcmp(s, "ROUTE")) - { //pure rewroute... - //is this safe? probably not. - s = "QTVSV 1\n" - "PERROR: ROUTE command not yet implemented\n" - "\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - pend->flushing = true; - } - */ - else if (!strcmp(s, "SOURCE")) - { //connects, creating a new source - while (*colon == ' ') - colon++; - for (s = colon; *s; s++) - if (*s < '0' || *s > '9') - break; - if (*s) - qtv = QTV_NewServerConnection(cluster, colon, "", false, true, true, false); - else - { - //numerical source, use a stream id. - for (qtv = cluster->servers; qtv; qtv = qtv->next) - if (qtv->streamid == atoi(colon)) - break; - } - } - else if (!strcmp(s, "DEMO")) - { //starts a demo off the server... source does the same thing though... - char buf[256]; - - snprintf(buf, sizeof(buf), "demo:%s", colon); - qtv = QTV_NewServerConnection(cluster, buf, "", false, true, true, false); - if (!qtv) - { - s = QTVSVHEADER - "PERROR: couldn't open demo\n" - "\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - pend->flushing = true; - } - } - else if (!strcmp(s, "AUTH")) - { //lists the demos available on this proxy - //part of the connection process, can be ignored if there's no password - } - else - printf("Unrecognised token in QTV connection request (%s)\n", s); - } - } - s = e+1; - } - - e++; - } - - if (!pend->flushing) - { - if (clientversion < 1) - { - s = QTVSVHEADER - "PERROR: Requested protocol version not supported\n" - "\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - pend->flushing = true; - } - if (!qtv) - { - s = QTVSVHEADER - "PERROR: No stream selected\n" - "\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - pend->flushing = true; - } - } - if (pend->flushing) - return false; - - - if (qtv->usequkeworldprotocols) - { - s = QTVSVHEADER - "PERROR: This version of QTV is unable to convert QuakeWorld to QTV protocols\n" - "\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - pend->flushing = true; - return false; - } - if (cluster->maxproxies>=0 && cluster->numproxies >= cluster->maxproxies) - { - s = QTVSVHEADER - "TERROR: This QTV has reached it's connection limit\n" - "\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - pend->flushing = true; - return false; - } - - pend->next = qtv->proxies; - qtv->proxies = pend; - - if (!raw) - { - s = QTVSVHEADER; - Net_ProxySend(cluster, pend, s, strlen(s)); - s = "BEGIN: "; - Net_ProxySend(cluster, pend, s, strlen(s)); - s = qtv->server; - Net_ProxySend(cluster, pend, s, strlen(s)); - s = "\n\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - } -// else if (passwordprotected) //raw mode doesn't support passwords, so reject them -// { -// pend->flushing = true; -// return; -// } - - - Net_SendConnectionMVD(qtv, pend); - - return true; -} + pend->flushing = true; + return false; + } + + raw = false; + + qtv = pend->defaultstream; + + e = pend->inbuffer; + s = e; + while(*e) + { + if (*e == '\n' || *e == '\r') + { + *e = '\0'; + colon = strchr((char*)s, ':'); + if (*s) + { + if (!colon) + { + if (!ustrcmp(s, "QTV")) + { + //just a qtv request (as in, not http or some other protocol) + } + else if (!ustrcmp(s, "SOURCELIST")) + { //lists sources that are currently playing + Net_ProxySendString(cluster, pend, QTVSVHEADER); + if (!cluster->servers) + { + Net_ProxySendString(cluster, pend, "PERROR: No sources currently available\n"); + } + else + { + for (qtv = cluster->servers; qtv; qtv = qtv->next) + { + if (clientversion > 1) + { + int plyrs = 0; + int i; + for (i = 0; i < MAX_CLIENTS; i++) + { + if (*qtv->players[i].userinfo) + plyrs++; + } + sprintf(tempbuf, "SRCSRV: %s\n", qtv->server); + Net_ProxySendString(cluster, pend, tempbuf); + sprintf(tempbuf, "SRCHOST: %s\n", qtv->hostname); + Net_ProxySendString(cluster, pend, tempbuf); + sprintf(tempbuf, "SRCPLYRS: %i\n", plyrs); + Net_ProxySendString(cluster, pend, tempbuf); + sprintf(tempbuf, "SRCVIEWS: %i\n", qtv->numviewers); + Net_ProxySendString(cluster, pend, tempbuf); + sprintf(tempbuf, "SRCID: %i\n", qtv->streamid); //final part of each source + Net_ProxySendString(cluster, pend, tempbuf); + } + else + { + sprintf(tempbuf, "ASOURCE: %i: %15s: %15s\n", qtv->streamid, qtv->server, qtv->hostname); + Net_ProxySendString(cluster, pend, tempbuf); + } + } + qtv = NULL; + } + Net_ProxySendString(cluster, pend, "\n"); + pend->flushing = true; + } + else if (!ustrcmp(s, "REVERSE")) + { //this is actually a server trying to connect to us + //start up a new stream + + //FIXME: does this work? +#if 1 //left disabled until properly tested + qtv = QTV_NewServerConnection(cluster, "reverse"/*server*/, "", true, 2, false, 0); + + Net_ProxySendString(cluster, pend, QTVSVHEADER); + Net_ProxySendString(cluster, pend, "REVERSED\n"); + Net_ProxySendString(cluster, pend, "VERSION: 1\n"); + Net_ProxySendString(cluster, pend, "\n"); + + //switch over the socket to the actual source connection rather than the pending + Net_TryFlushProxyBuffer(cluster, pend); //flush anything... this isn't ideal, but should be small enough + qtv->sourcesock = pend->sock; + pend->sock = 0; + + memcpy(qtv->buffer, pend->inbuffer + headersize, pend->inbuffersize - headersize); + qtv->parsingqtvheader = true; + return false; +#endif + } + else if (!ustrcmp(s, "RECEIVE")) + { //a client connection request without a source + if (cluster->numservers == 1) + { //only one stream anyway + qtv = cluster->servers; + } + else + { //try and hunt down an explicit stream (rather than a user-recorded one) + int numfound = 0; + sv_t *suitable = NULL; //shush noisy compilers + for (qtv = cluster->servers; qtv; qtv = qtv->next) + { + if (!qtv->disconnectwhennooneiswatching) + { + suitable = qtv; + numfound++; + } + } + if (numfound == 1) + qtv = suitable; + } + if (!qtv) + { + Net_ProxySendString(cluster, pend, QTVSVHEADER); + Net_ProxySendString(cluster, pend, "PERROR: Multiple streams are currently playing\n"); + Net_ProxySendString(cluster, pend, "\n"); + pend->flushing = true; + } + } + else if (!ustrcmp(s, "DEMOLIST")) + { //lists sources that are currently playing + int i; + + Cluster_BuildAvailableDemoList(cluster); + + Net_ProxySendString(cluster, pend, QTVSVHEADER); + if (!cluster->availdemoscount) + { + Net_ProxySendString(cluster, pend, "PERROR: No demos currently available\n"); + } + else + { + for (i = 0; i < cluster->availdemoscount; i++) + { + sprintf(tempbuf, "ADEMO: %i: %15s\n", cluster->availdemos[i].size, cluster->availdemos[i].name); + Net_ProxySendString(cluster, pend, tempbuf); + } + qtv = NULL; + } + Net_ProxySendString(cluster, pend, "\n"); + pend->flushing = true; + } + else if (!ustrcmp(s, "AUTH")) + { //lists the demos available on this proxy + //part of the connection process, can be ignored if there's no password + } + else + printf("Unrecognised token in QTV connection request (%s)\n", s); + } + else + { + *colon++ = '\0'; + if (!ustrcmp(s, "VERSION")) + { + clientversion = atof(colon); + } + else if (!ustrcmp(s, "RAW")) + raw = atoi(colon); + /*else if (!ustrcmp(s, "ROUTE")) + { //pure rewroute... + //is this safe? probably not. + s = QTVSVHEADER + "PERROR: ROUTE command not yet implemented\n" + "\n"; + Net_ProxySend(cluster, pend, s, ustrlen(s)); + pend->flushing = true; + } + */ + else if (!ustrcmp(s, "SOURCE")) + { //connects, creating a new source + char *t; + while (*colon == ' ') + colon++; + for (t = colon; *t; t++) + if (*t < '0' || *t > '9') + break; + if (*t) + qtv = QTV_NewServerConnection(cluster, colon, "", false, true, true, false); + else + { + //numerical source, use a stream id. + for (qtv = cluster->servers; qtv; qtv = qtv->next) + if (qtv->streamid == atoi(colon)) + break; + } + } + else if (!ustrcmp(s, "DEMO")) + { //starts a demo off the server... source does the same thing though... + char buf[256]; + + snprintf(buf, sizeof(buf), "demo:%s", colon); + qtv = QTV_NewServerConnection(cluster, buf, "", false, true, true, false); + if (!qtv) + { + Net_ProxySendString(cluster, pend, QTVSVHEADER + "PERROR: couldn't open demo\n" + "\n"); + pend->flushing = true; + } + } + else if (!ustrcmp(s, "AUTH")) + { //lists the demos available on this proxy + //part of the connection process, can be ignored if there's no password + } + else + printf("Unrecognised token in QTV connection request (%s)\n", s); + } + } + s = e+1; + } + + e++; + } + + if (!pend->flushing) + { + if (clientversion < 1) + { + Net_ProxySendString(cluster, pend, QTVSVHEADER + "PERROR: Requested protocol version not supported\n" + "\n"); + pend->flushing = true; + } + else if (!qtv) + { + Net_ProxySendString(cluster, pend, QTVSVHEADER + "PERROR: No stream selected\n" + "\n"); + pend->flushing = true; + } + } + if (pend->flushing) + return false; + + + if (qtv->usequakeworldprotocols) + { + Net_ProxySendString(cluster, pend, QTVSVHEADER + "PERROR: This version of QTV is unable to convert QuakeWorld to QTV protocols\n" + "\n"); + pend->flushing = true; + return false; + } + if (cluster->maxproxies>=0 && cluster->numproxies >= cluster->maxproxies) + { + Net_ProxySendString(cluster, pend, QTVSVHEADER + "TERROR: This QTV has reached it's connection limit\n" + "\n"); + pend->flushing = true; + return false; + } + + pend->next = qtv->proxies; + qtv->proxies = pend; + + if (!raw) + { + Net_ProxySendString(cluster, pend, QTVSVHEADER); + Net_ProxySendString(cluster, pend, "BEGIN: "); + Net_ProxySendString(cluster, pend, qtv->server); + Net_ProxySendString(cluster, pend, "\n\n"); + } +// else if (passwordprotected) //raw mode doesn't support passwords, so reject them +// { +// pend->flushing = true; +// return; +// } + + pend->stream = qtv; + + memmove(pend->inbuffer, pend->inbuffer+headersize, pend->inbuffersize-headersize); + pend->inbuffersize -= headersize; + + Net_GreetingMessage(pend); + Net_SendConnectionMVD(qtv, pend); + + return true; +} diff --git a/fteqtv/httpsv.c b/fteqtv/httpsv.c index 5a56fcd62..aba172f92 100644 --- a/fteqtv/httpsv.c +++ b/fteqtv/httpsv.c @@ -1,741 +1,741 @@ -#include "qtv.h" - -//main reason to use connection close is because we're lazy and don't want to give sizes in advance (yes, we could use chunks..) - -//#define ALLOWDOWNLOADS - - - - -static const char qfont_table[256] = { - '\0', '#', '#', '#', '#', '.', '#', '#', - '#', 9, 10, '#', ' ', 13, '.', '.', - '[', ']', '0', '1', '2', '3', '4', '5', - '6', '7', '8', '9', '.', '<', '=', '>', - ' ', '!', '"', '#', '$', '%', '&', '\'', - '(', ')', '*', '+', ',', '-', '.', '/', - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', ':', ';', '<', '=', '>', '?', - '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', - 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', - 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', - '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', - 'x', 'y', 'z', '{', '|', '}', '~', '<', - - '<', '=', '>', '#', '#', '.', '#', '#', - '#', '#', ' ', '#', ' ', '>', '.', '.', - '[', ']', '0', '1', '2', '3', '4', '5', - '6', '7', '8', '9', '.', '<', '=', '>', - ' ', '!', '"', '#', '$', '%', '&', '\'', - '(', ')', '*', '+', ',', '-', '.', '/', - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', ':', ';', '<', '=', '>', '?', - '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', - 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', - 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', - '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', - 'x', 'y', 'z', '{', '|', '}', '~', '<' -}; - -static void HTMLprintf(char *outb, int outl, char *fmt, ...) -{ - va_list val; - char qfmt[8192*4]; - char *inb = qfmt; - - va_start(val, fmt); - vsnprintf(qfmt, sizeof(qfmt), fmt, val); - va_end(val); - qfmt[sizeof(qfmt)-1] = 0; - - outl--; - outl -= 5; - while (outl > 0 && *inb) - { - if (*inb == '<') - { - *outb++ = '&'; - *outb++ = 'l'; - *outb++ = 't'; - *outb++ = ';'; - outl -= 4; - } - else if (*inb == '>') - { - *outb++ = '&'; - *outb++ = 'g'; - *outb++ = 't'; - *outb++ = ';'; - outl -= 4; - } - else if (*inb == '\n') - { - *outb++ = '<'; - *outb++ = 'b'; - *outb++ = 'r'; - *outb++ = '/'; - *outb++ = '>'; - outl -= 5; - } - else if (*inb == '&') - { - *outb++ = '&'; - *outb++ = 'a'; - *outb++ = 'm'; - *outb++ = 'p'; - *outb++ = ';'; - outl -= 5; - } - else - { - *outb++ = qfont_table[*(unsigned char*)inb]; - } - inb++; - } - *outb++ = 0; -} - -static void HTTPSV_SendHTTPHeader(cluster_t *cluster, oproxy_t *dest, char *error_code, char *content_type, qboolean nocache) -{ - char *s; - char buffer[2048]; - - if (nocache) - { - s = "HTTP/1.1 %s OK\n" - "Content-Type: %s\n" - "Cache-Control: no-cache, must-revalidate\n" - "Expires: Mon, 26 Jul 1997 05:00:00 GMT\n" - "Connection: close\n" - "\n"; - } - else - { - s = "HTTP/1.1 %s OK\n" - "Content-Type: %s\n" - "Connection: close\n" - "\n"; - } - - snprintf(buffer, sizeof(buffer), s, error_code, content_type); - - Net_ProxySend(cluster, dest, buffer, strlen(buffer)); -} - -static void HTTPSV_SendHTMLHeader(cluster_t *cluster, oproxy_t *dest, char *title) -{ - char *s; - char buffer[2048]; - - s = "\n" - "\n" - "\n" - " \n" - " %s\n" - " \n" - "\n" - "
"; - - snprintf(buffer, sizeof(buffer), s, title); - - Net_ProxySend(cluster, dest, buffer, strlen(buffer)); -} - -static void HTTPSV_SendHTMLFooter(cluster_t *cluster, oproxy_t *dest) -{ - char *s; - char buffer[2048]; - - sprintf(buffer, "
QTV Version: %i www.fteqw.com
", cluster->buildnumber); - Net_ProxySend(cluster, dest, buffer, strlen(buffer)); - - s = "\n" - "\n"; - Net_ProxySend(cluster, dest, s, strlen(s)); -} - -#define HTMLPRINT(str) Net_ProxySend(cluster, dest, str "\n", strlen(str "\n")) - -static void HTTPSV_GenerateNowPlaying(cluster_t *cluster, oproxy_t *dest) -{ - int player; - char *s; - char buffer[1024]; - char plname[64]; - sv_t *streams; - - HTTPSV_SendHTTPHeader(cluster, dest, "200", "text/html", true); - HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Now Playing"); - - if (!strcmp(cluster->hostname, DEFAULT_HOSTNAME)) - snprintf(buffer, sizeof(buffer), "

QuakeTV: Now Playing

"); //don't show the hostname if its set to the default - else - snprintf(buffer, sizeof(buffer), "

QuakeTV: Now Playing on %s

", cluster->hostname); - Net_ProxySend(cluster, dest, buffer, strlen(buffer)); - - HTMLPRINT("
"); - for (streams = cluster->servers; streams; streams = streams->next) - { - HTMLPRINT("
"); - HTMLprintf(buffer, sizeof(buffer), "%s (%s: %s)", streams->server, streams->gamedir, streams->mapname); - Net_ProxySend(cluster, dest, buffer, strlen(buffer)); - sprintf(buffer, " [ Watch Now ]", streams->streamid); - Net_ProxySend(cluster, dest, buffer, strlen(buffer)); - HTMLPRINT("
    "); - - for (player = 0; player < MAX_CLIENTS; player++) - { - if (*streams->players[player].userinfo) - { - Info_ValueForKey(streams->players[player].userinfo, "name", plname, sizeof(plname)); - - if (streams->players[player].frags < -90) - { - HTMLPRINT("
  • "); - } - else - { - HTMLPRINT("
  • "); - } - - HTMLprintf(buffer, sizeof(buffer), "%s", plname); - Net_ProxySend(cluster, dest, buffer, strlen(buffer)); - HTMLPRINT("
  • "); - } - } - HTMLPRINT("
"); - } - HTMLPRINT("
"); - if (!cluster->servers) - { - s = "No streams are currently being played
"; - Net_ProxySend(cluster, dest, s, strlen(s)); - } - - HTTPSV_SendHTMLFooter(cluster, dest); -} - -static void HTTPSV_GenerateCSSFile(cluster_t *cluster, oproxy_t *dest) -{ - HTTPSV_SendHTTPHeader(cluster, dest, "200", "text/css", false); - - HTMLPRINT("* { font-family: Verdana, Helvetica, sans-serif; }"); - HTMLPRINT("body { color: #000; background-color: #fff; padding: 0 40px; }"); - HTMLPRINT("a { color: #00f; }"); - HTMLPRINT("a.qtvfile { font-weight: bold; }"); - HTMLPRINT("a:visited { color: #00f; }"); - HTMLPRINT("a:hover { background-color: black; color: yellow; }"); - HTMLPRINT("li.spectator { color: #666; font-size: 0.9ex; }"); - HTMLPRINT("dl.nowplaying dd { margin: 0 0 2em 0; }"); - HTMLPRINT("dl.nowplaying dt { margin: 1em 0 0 0; font-size: 1.1em; font-weight: bold; }"); - HTMLPRINT("dl.nowplaying li { list-style: none; margin: 0 0 0 1em; padding: 0; }"); - HTMLPRINT("dl.nowplaying ul { margin: 0 0 0 1em; padding: 0; }"); - HTMLPRINT("#navigation { background-color: #eef; }"); - HTMLPRINT("#navigation li { display: inline; list-style: none; margin: 0 3em; }"); -} - -static qboolean HTTPSV_GetHeaderField(char *s, char *field, char *buffer, int buffersize) -{ - char *e; - char *colon; - int fieldnamelen = strlen(field); - - buffer[0] = 0; - - e = s; - while(*e) - { - if (*e == '\n') - { - *e = '\0'; - colon = strchr(s, ':'); - if (!colon) - { - if (!strncmp(field, s, fieldnamelen)) - { - if (s[fieldnamelen] <= ' ') - { - return true; - } - } - } - else - { - if (fieldnamelen == colon - s) - { - if (!strncmp(field, s, colon-s)) - { - colon++; - while (*colon == ' ') - colon++; - while (buffersize > 1) - { - if (*colon == '\r' || *colon == '\n') - break; - *buffer++ = *colon++; - } - *buffer = 0; - return true; - } - } - - } - s = e+1; - } - - e++; - } - return false; -} - -static void HTTPSV_GenerateQTVStub(cluster_t *cluster, oproxy_t *dest, char *streamtype, char *streamid) -{ - char *s; - char hostname[64]; - char buffer[1024]; - - - char fname[256]; - s = fname; - while (*streamid > ' ') - { - if (s > fname + sizeof(fname)-4) //4 cos I'm too lazy to work out what the actual number should be - break; - if (*streamid == '%') - { - *s = 0; - streamid++; - if (*streamid <= ' ') - break; - else if (*streamid >= 'a' && *streamid <= 'f') - *s += 10 + *streamid-'a'; - else if (*streamid >= 'A' && *streamid <= 'F') - *s += 10 + *streamid-'A'; - else if (*streamid >= '0' && *streamid <= '9') - *s += *streamid-'0'; - - *s <<= 4; - - streamid++; - if (*streamid <= ' ') - break; - else if (*streamid >= 'a' && *streamid <= 'f') - *s += 10 + *streamid-'a'; - else if (*streamid >= 'A' && *streamid <= 'F') - *s += 10 + *streamid-'A'; - else if (*streamid >= '0' && *streamid <= '9') - *s += *streamid-'0'; - - s++; - } - else - *s++ = *streamid++; - } - *s = 0; - streamid = fname; - - - if (!HTTPSV_GetHeaderField(dest->inbuffer, "Host", hostname, sizeof(hostname))) - { - HTTPSV_SendHTTPHeader(cluster, dest, "400", "text/html", true); - HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Error"); - - s = "Your client did not send a Host field, which is required in HTTP/1.1\n
" - "Please try a different browser.\n" - "" - ""; - - Net_ProxySend(cluster, dest, s, strlen(s)); - return; - } - - HTTPSV_SendHTTPHeader(cluster, dest, "200", "text/x-quaketvident", false); - - sprintf(buffer, "[QTV]\r\n" - "Stream: %s%s@%s\r\n" - "", - streamtype, streamid, hostname); - - - Net_ProxySend(cluster, dest, buffer, strlen(buffer)); -} - -static char *HTTPSV_ParsePOST(char *post, char *buffer, int buffersize) -{ - while(*post && *post != '&') - { - if (--buffersize>0) - { - if (*post == '+') - *buffer++ = ' '; - else if (*post == '%') - { - *buffer = 0; - post++; - if (*post == '\0' || *post == '&') - break; - else if (*post >= 'a' && *post <= 'f') - *buffer += 10 + *post-'a'; - else if (*post >= 'A' && *post <= 'F') - *buffer += 10 + *post-'A'; - else if (*post >= '0' && *post <= '9') - *buffer += *post-'0'; - - *buffer <<= 4; - - post++; - if (*post == '\0' || *post == '&') - break; - else if (*post >= 'a' && *post <= 'f') - *buffer += 10 + *post-'a'; - else if (*post >= 'A' && *post <= 'F') - *buffer += 10 + *post-'A'; - else if (*post >= '0' && *post <= '9') - *buffer += *post-'0'; - - buffer++; - } - else - *buffer++ = *post; - } - post++; - } - *buffer = 0; - - return post; -} -static void HTTPSV_GenerateAdmin(cluster_t *cluster, oproxy_t *dest, int streamid, char *postbody) -{ - char pwd[64]; - char cmd[256]; - char result[8192]; - char *s; - char *o; - int passwordokay = false; - - if (!*cluster->adminpassword) - { - HTTPSV_SendHTTPHeader(cluster, dest, "403", "text/html", true); - HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Admin Error"); - - s = "The admin password is disabled. You may not log in remotely.\n"; - Net_ProxySend(cluster, dest, s, strlen(s)); - return; - } - - - pwd[0] = 0; - cmd[0] = 0; - if (postbody) - while (*postbody) - { - if (!strncmp(postbody, "pwd=", 4)) - { - postbody = HTTPSV_ParsePOST(postbody+4, pwd, sizeof(pwd)); - } - else if (!strncmp(postbody, "cmd=", 4)) - { - postbody = HTTPSV_ParsePOST(postbody+4, cmd, sizeof(cmd)); - } - else - { - while(*postbody && *postbody != '&') - { - postbody++; - } - if (*postbody == '&') - postbody++; - } - } - - if (!*pwd) - { - if (postbody) - o = "No Password"; - else - o = ""; - } - else if (!strcmp(pwd, cluster->adminpassword)) - { - passwordokay = true; - //small hack (as http connections are considered non-connected proxies) - cluster->numproxies--; - if (*cmd) - o = Rcon_Command(cluster, NULL, cmd, result, sizeof(result), false); - else - o = ""; - cluster->numproxies++; - } - else - { - o = "Bad Password"; - } - if (o != result) - { - strcpy(result, o); - o = result; - } - - HTTPSV_SendHTTPHeader(cluster, dest, "200", "text/html", true); - HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Admin"); - - s = "

QuakeTV Admin: "; - Net_ProxySend(cluster, dest, s, strlen(s)); - s = cluster->hostname; - Net_ProxySend(cluster, dest, s, strlen(s)); - s = "

"; - Net_ProxySend(cluster, dest, s, strlen(s)); - - - s = "
" - "
" - "Password " - "
" - "Command " - "" - "
" - "
"; - Net_ProxySend(cluster, dest, s, strlen(s)); - - if (passwordokay) - HTMLPRINT(""); - else - HTMLPRINT(""); - - while(*o) - { - s = strchr(o, '\n'); - if (s) - *s = 0; - HTMLprintf(cmd, sizeof(cmd), "%s", o); - Net_ProxySend(cluster, dest, cmd, strlen(cmd)); - Net_ProxySend(cluster, dest, "
", 6); - if (!s) - break; - o = s+1; - } - - HTTPSV_SendHTMLFooter(cluster, dest); -} - -static void HTTPSV_GenerateDemoListing(cluster_t *cluster, oproxy_t *dest) -{ - int i; - char link[256]; - char *s; - - HTTPSV_SendHTTPHeader(cluster, dest, "200", "text/html", true); - HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Demos"); - - s = "

QuakeTV: Demo Listing

"; - Net_ProxySend(cluster, dest, s, strlen(s)); - - Cluster_BuildAvailableDemoList(cluster); - for (i = 0; i < cluster->availdemoscount; i++) - { - snprintf(link, sizeof(link), "%s (%ikb)
", cluster->availdemos[i].name, cluster->availdemos[i].name, cluster->availdemos[i].size/1024); - Net_ProxySend(cluster, dest, link, strlen(link)); - } - - sprintf(link, "

Total: %i demos

", cluster->availdemoscount); - Net_ProxySend(cluster, dest, link, strlen(link)); - - HTTPSV_SendHTMLFooter(cluster, dest); -} - -static void HTTPSV_GenerateDownload(cluster_t *cluster, oproxy_t *dest, char *filename) -{ -#ifdef ALLOWDOWNLOADS - char fname[256]; - char *s; - - if (cluster->allowdownloads) -#endif - { - HTTPSV_SendHTTPHeader(cluster, dest, "403", "text/html", true); - HTTPSV_SendHTMLHeader(cluster, dest, "Permission denied"); - HTMLPRINT("

403: Forbidden

"); - HTMLPRINT("File downloads from this proxy are currently not permitted."); - HTTPSV_SendHTMLFooter(cluster, dest); - return; - } -#ifdef ALLOWDOWNLOADS - s = fname; - while (*filename > ' ') - { - if (s > fname + sizeof(fname)-4) //4 cos I'm too lazy to work out what the actual number should be - break; - if (*filename == '%') - { - *s = 0; - filename++; - if (*filename <= ' ') - break; - else if (*filename >= 'a' && *filename <= 'f') - *s += 10 + *filename-'a'; - else if (*filename >= 'A' && *filename <= 'F') - *s += 10 + *filename-'A'; - else if (*filename >= '0' && *filename <= '9') - *s += *filename-'0'; - - *s <<= 4; - - filename++; - if (*filename <= ' ') - break; - else if (*filename >= 'a' && *filename <= 'f') - *s += 10 + *filename-'a'; - else if (*filename >= 'A' && *filename <= 'F') - *s += 10 + *filename-'A'; - else if (*filename >= '0' && *filename <= '9') - *s += *filename-'0'; - - s++; - } - else - *s++ = *filename++; - } - *s = 0; - dest->srcfile = fopen(fname, "rb"); - - if (dest->srcfile) - { - HTTPSV_SendHTTPHeader(cluster, dest, "200", "application/x-forcedownload", false); - } - else - { - HTTPSV_SendHTTPHeader(cluster, dest, "404", "text/html", true); - HTTPSV_SendHTMLHeader(cluster, dest, "File not found"); - HTMLPRINT("

404: File not found

"); - HTTPSV_SendHTMLFooter(cluster, dest); - } -#endif -} - - - - - - - -void HTTPSV_PostMethod(cluster_t *cluster, oproxy_t *pend, char *postdata) -{ - char tempbuf[512]; - char *s; - int len; - - if (!HTTPSV_GetHeaderField(pend->inbuffer, "Content-Length", tempbuf, sizeof(tempbuf))) - { - s = "HTTP/1.1 411 OK\n" - "Content-Type: text/html\n" - "Connection: close\n" - "\n" - "QuakeTVNo Content-Length was provided.\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - pend->flushing = true; - return; - } - len = atoi(tempbuf); - if (pend->inbuffersize + len >= sizeof(pend->inbuffer)-20) - { //too much data - pend->flushing = true; - return; - } - len = postdata - (char*)pend->inbuffer + len; - if (len > pend->inbuffersize) - return; //still need the body - -// if (len <= pend->inbuffersize) - { - if (!strncmp(pend->inbuffer+5, "/admin", 6)) - { - HTTPSV_GenerateAdmin(cluster, pend, 0, postdata); - } - else - { - s = "HTTP/1.1 404 OK\n" - "Content-Type: text/html\n" - "Connection: close\n" - "\n" - "QuakeTVThat HTTP method is not supported for that URL.\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - - } - pend->flushing = true; - return; - } -} - -void HTTPSV_GetMethod(cluster_t *cluster, oproxy_t *pend) -{ - char *s; - if (!strncmp(pend->inbuffer+4, "/nowplaying", 11)) - { - HTTPSV_GenerateNowPlaying(cluster, pend); - } - else if (!strncmp(pend->inbuffer+4, "/watch.qtv?sid=", 15)) - { - HTTPSV_GenerateQTVStub(cluster, pend, "", pend->inbuffer+19); - } - else if (!strncmp(pend->inbuffer+4, "/watch.qtv?demo=", 16)) - { - HTTPSV_GenerateQTVStub(cluster, pend, "file:", pend->inbuffer+20); - } -// else if (!strncmp(pend->inbuffer+4, "/demo/", 6)) -// { //fixme: make this send the demo as an http download -// HTTPSV_GenerateQTVStub(cluster, pend, "file:", pend->inbuffer+10); -// } - else if (!strncmp(pend->inbuffer+4, "/about", 6)) - { //redirect them to our funky website - s = "HTTP/1.0 302 Found\n" - "Location: http://www.fteqw.com/\n" - "\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - } - else if (!strncmp(pend->inbuffer+4, "/admin", 6)) - { - HTTPSV_GenerateAdmin(cluster, pend, 0, NULL); - } - else if (!strncmp(pend->inbuffer+4, "/ ", 2)) - { - s = "HTTP/1.0 302 Found\n" - "Location: /nowplaying/\n" - "\n"; - Net_ProxySend(cluster, pend, s, strlen(s)); - } - else if (!strncmp(pend->inbuffer+4, "/demos", 6)) - { - HTTPSV_GenerateDemoListing(cluster, pend); - } - else if (!strncmp(pend->inbuffer+4, "/file/", 6)) - { - HTTPSV_GenerateDownload(cluster, pend, pend->inbuffer+10); - } - else if (!strncmp(pend->inbuffer+4, "/style.css", 10)) - { - HTTPSV_GenerateCSSFile(cluster, pend); - } - else - { -#define dest pend - HTTPSV_SendHTTPHeader(cluster, dest, "404", "text/html", true); - HTTPSV_SendHTMLHeader(cluster, dest, "Address not recognised"); - HTMLPRINT("

Address not recognised

"); - HTTPSV_SendHTMLFooter(cluster, dest); - } -} +#include "qtv.h" + +//main reason to use connection close is because we're lazy and don't want to give sizes in advance (yes, we could use chunks..) + +//#define ALLOWDOWNLOADS + + + + +static const char qfont_table[256] = { + '\0', '#', '#', '#', '#', '.', '#', '#', + '#', 9, 10, '#', ' ', 13, '.', '.', + '[', ']', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '.', '<', '=', '>', + ' ', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', '{', '|', '}', '~', '<', + + '<', '=', '>', '#', '#', '.', '#', '#', + '#', '#', ' ', '#', ' ', '>', '.', '.', + '[', ']', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '.', '<', '=', '>', + ' ', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', '{', '|', '}', '~', '<' +}; + +static void HTMLprintf(char *outb, int outl, char *fmt, ...) +{ + va_list val; + char qfmt[8192*4]; + char *inb = qfmt; + + va_start(val, fmt); + vsnprintf(qfmt, sizeof(qfmt), fmt, val); + va_end(val); + qfmt[sizeof(qfmt)-1] = 0; + + outl--; + outl -= 5; + while (outl > 0 && *inb) + { + if (*inb == '<') + { + *outb++ = '&'; + *outb++ = 'l'; + *outb++ = 't'; + *outb++ = ';'; + outl -= 4; + } + else if (*inb == '>') + { + *outb++ = '&'; + *outb++ = 'g'; + *outb++ = 't'; + *outb++ = ';'; + outl -= 4; + } + else if (*inb == '\n') + { + *outb++ = '<'; + *outb++ = 'b'; + *outb++ = 'r'; + *outb++ = '/'; + *outb++ = '>'; + outl -= 5; + } + else if (*inb == '&') + { + *outb++ = '&'; + *outb++ = 'a'; + *outb++ = 'm'; + *outb++ = 'p'; + *outb++ = ';'; + outl -= 5; + } + else + { + *outb++ = qfont_table[*(unsigned char*)inb]; + } + inb++; + } + *outb++ = 0; +} + +static void HTTPSV_SendHTTPHeader(cluster_t *cluster, oproxy_t *dest, char *error_code, char *content_type, qboolean nocache) +{ + char *s; + char buffer[2048]; + + if (nocache) + { + s = "HTTP/1.1 %s OK\n" + "Content-Type: %s\n" + "Cache-Control: no-cache, must-revalidate\n" + "Expires: Mon, 26 Jul 1997 05:00:00 GMT\n" + "Connection: close\n" + "\n"; + } + else + { + s = "HTTP/1.1 %s OK\n" + "Content-Type: %s\n" + "Connection: close\n" + "\n"; + } + + snprintf(buffer, sizeof(buffer), s, error_code, content_type); + + Net_ProxySend(cluster, dest, buffer, strlen(buffer)); +} + +static void HTTPSV_SendHTMLHeader(cluster_t *cluster, oproxy_t *dest, char *title) +{ + char *s; + char buffer[2048]; + + s = "\n" + "\n" + "\n" + " \n" + " %s\n" + " \n" + "\n" + "
"; + + snprintf(buffer, sizeof(buffer), s, title); + + Net_ProxySend(cluster, dest, buffer, strlen(buffer)); +} + +static void HTTPSV_SendHTMLFooter(cluster_t *cluster, oproxy_t *dest) +{ + char *s; + char buffer[2048]; + + sprintf(buffer, "
QTV Version: %i www.fteqw.com
", cluster->buildnumber); + Net_ProxySend(cluster, dest, buffer, strlen(buffer)); + + s = "\n" + "\n"; + Net_ProxySend(cluster, dest, s, strlen(s)); +} + +#define HTMLPRINT(str) Net_ProxySend(cluster, dest, str "\n", strlen(str "\n")) + +static void HTTPSV_GenerateNowPlaying(cluster_t *cluster, oproxy_t *dest) +{ + int player; + char *s; + char buffer[1024]; + char plname[64]; + sv_t *streams; + + HTTPSV_SendHTTPHeader(cluster, dest, "200", "text/html", true); + HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Now Playing"); + + if (!strcmp(cluster->hostname, DEFAULT_HOSTNAME)) + snprintf(buffer, sizeof(buffer), "

QuakeTV: Now Playing

"); //don't show the hostname if its set to the default + else + snprintf(buffer, sizeof(buffer), "

QuakeTV: Now Playing on %s

", cluster->hostname); + Net_ProxySend(cluster, dest, buffer, strlen(buffer)); + + HTMLPRINT("
"); + for (streams = cluster->servers; streams; streams = streams->next) + { + HTMLPRINT("
"); + HTMLprintf(buffer, sizeof(buffer), "%s (%s: %s)", streams->server, streams->gamedir, streams->mapname); + Net_ProxySend(cluster, dest, buffer, strlen(buffer)); + sprintf(buffer, " [ Watch Now ]", streams->streamid); + Net_ProxySend(cluster, dest, buffer, strlen(buffer)); + HTMLPRINT("
    "); + + for (player = 0; player < MAX_CLIENTS; player++) + { + if (*streams->players[player].userinfo) + { + Info_ValueForKey(streams->players[player].userinfo, "name", plname, sizeof(plname)); + + if (streams->players[player].frags < -90) + { + HTMLPRINT("
  • "); + } + else + { + HTMLPRINT("
  • "); + } + + HTMLprintf(buffer, sizeof(buffer), "%s", plname); + Net_ProxySend(cluster, dest, buffer, strlen(buffer)); + HTMLPRINT("
  • "); + } + } + HTMLPRINT("
"); + } + HTMLPRINT("
"); + if (!cluster->servers) + { + s = "No streams are currently being played
"; + Net_ProxySend(cluster, dest, s, strlen(s)); + } + + HTTPSV_SendHTMLFooter(cluster, dest); +} + +static void HTTPSV_GenerateCSSFile(cluster_t *cluster, oproxy_t *dest) +{ + HTTPSV_SendHTTPHeader(cluster, dest, "200", "text/css", false); + + HTMLPRINT("* { font-family: Verdana, Helvetica, sans-serif; }"); + HTMLPRINT("body { color: #000; background-color: #fff; padding: 0 40px; }"); + HTMLPRINT("a { color: #00f; }"); + HTMLPRINT("a.qtvfile { font-weight: bold; }"); + HTMLPRINT("a:visited { color: #00f; }"); + HTMLPRINT("a:hover { background-color: black; color: yellow; }"); + HTMLPRINT("li.spectator { color: #666; font-size: 0.9ex; }"); + HTMLPRINT("dl.nowplaying dd { margin: 0 0 2em 0; }"); + HTMLPRINT("dl.nowplaying dt { margin: 1em 0 0 0; font-size: 1.1em; font-weight: bold; }"); + HTMLPRINT("dl.nowplaying li { list-style: none; margin: 0 0 0 1em; padding: 0; }"); + HTMLPRINT("dl.nowplaying ul { margin: 0 0 0 1em; padding: 0; }"); + HTMLPRINT("#navigation { background-color: #eef; }"); + HTMLPRINT("#navigation li { display: inline; list-style: none; margin: 0 3em; }"); +} + +static qboolean HTTPSV_GetHeaderField(char *s, char *field, char *buffer, int buffersize) +{ + char *e; + char *colon; + int fieldnamelen = strlen(field); + + buffer[0] = 0; + + e = s; + while(*e) + { + if (*e == '\n') + { + *e = '\0'; + colon = strchr(s, ':'); + if (!colon) + { + if (!strncmp(field, s, fieldnamelen)) + { + if (s[fieldnamelen] <= ' ') + { + return true; + } + } + } + else + { + if (fieldnamelen == colon - s) + { + if (!strncmp(field, s, colon-s)) + { + colon++; + while (*colon == ' ') + colon++; + while (buffersize > 1) + { + if (*colon == '\r' || *colon == '\n') + break; + *buffer++ = *colon++; + } + *buffer = 0; + return true; + } + } + + } + s = e+1; + } + + e++; + } + return false; +} + +static void HTTPSV_GenerateQTVStub(cluster_t *cluster, oproxy_t *dest, char *streamtype, char *streamid) +{ + char *s; + char hostname[64]; + char buffer[1024]; + + + char fname[256]; + s = fname; + while (*streamid > ' ') + { + if (s > fname + sizeof(fname)-4) //4 cos I'm too lazy to work out what the actual number should be + break; + if (*streamid == '%') + { + *s = 0; + streamid++; + if (*streamid <= ' ') + break; + else if (*streamid >= 'a' && *streamid <= 'f') + *s += 10 + *streamid-'a'; + else if (*streamid >= 'A' && *streamid <= 'F') + *s += 10 + *streamid-'A'; + else if (*streamid >= '0' && *streamid <= '9') + *s += *streamid-'0'; + + *s <<= 4; + + streamid++; + if (*streamid <= ' ') + break; + else if (*streamid >= 'a' && *streamid <= 'f') + *s += 10 + *streamid-'a'; + else if (*streamid >= 'A' && *streamid <= 'F') + *s += 10 + *streamid-'A'; + else if (*streamid >= '0' && *streamid <= '9') + *s += *streamid-'0'; + + s++; + } + else + *s++ = *streamid++; + } + *s = 0; + streamid = fname; + + + if (!HTTPSV_GetHeaderField((char*)dest->inbuffer, "Host", hostname, sizeof(hostname))) + { + HTTPSV_SendHTTPHeader(cluster, dest, "400", "text/html", true); + HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Error"); + + s = "Your client did not send a Host field, which is required in HTTP/1.1\n
" + "Please try a different browser.\n" + "" + ""; + + Net_ProxySend(cluster, dest, s, strlen(s)); + return; + } + + HTTPSV_SendHTTPHeader(cluster, dest, "200", "text/x-quaketvident", false); + + sprintf(buffer, "[QTV]\r\n" + "Stream: %s%s@%s\r\n" + "", + streamtype, streamid, hostname); + + + Net_ProxySend(cluster, dest, buffer, strlen(buffer)); +} + +static char *HTTPSV_ParsePOST(char *post, char *buffer, int buffersize) +{ + while(*post && *post != '&') + { + if (--buffersize>0) + { + if (*post == '+') + *buffer++ = ' '; + else if (*post == '%') + { + *buffer = 0; + post++; + if (*post == '\0' || *post == '&') + break; + else if (*post >= 'a' && *post <= 'f') + *buffer += 10 + *post-'a'; + else if (*post >= 'A' && *post <= 'F') + *buffer += 10 + *post-'A'; + else if (*post >= '0' && *post <= '9') + *buffer += *post-'0'; + + *buffer <<= 4; + + post++; + if (*post == '\0' || *post == '&') + break; + else if (*post >= 'a' && *post <= 'f') + *buffer += 10 + *post-'a'; + else if (*post >= 'A' && *post <= 'F') + *buffer += 10 + *post-'A'; + else if (*post >= '0' && *post <= '9') + *buffer += *post-'0'; + + buffer++; + } + else + *buffer++ = *post; + } + post++; + } + *buffer = 0; + + return post; +} +static void HTTPSV_GenerateAdmin(cluster_t *cluster, oproxy_t *dest, int streamid, char *postbody) +{ + char pwd[64]; + char cmd[256]; + char result[8192]; + char *s; + char *o; + int passwordokay = false; + + if (!*cluster->adminpassword) + { + HTTPSV_SendHTTPHeader(cluster, dest, "403", "text/html", true); + HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Admin Error"); + + s = "The admin password is disabled. You may not log in remotely.\n"; + Net_ProxySend(cluster, dest, s, strlen(s)); + return; + } + + + pwd[0] = 0; + cmd[0] = 0; + if (postbody) + while (*postbody) + { + if (!strncmp(postbody, "pwd=", 4)) + { + postbody = HTTPSV_ParsePOST(postbody+4, pwd, sizeof(pwd)); + } + else if (!strncmp(postbody, "cmd=", 4)) + { + postbody = HTTPSV_ParsePOST(postbody+4, cmd, sizeof(cmd)); + } + else + { + while(*postbody && *postbody != '&') + { + postbody++; + } + if (*postbody == '&') + postbody++; + } + } + + if (!*pwd) + { + if (postbody) + o = "No Password"; + else + o = ""; + } + else if (!strcmp(pwd, cluster->adminpassword)) + { + passwordokay = true; + //small hack (as http connections are considered non-connected proxies) + cluster->numproxies--; + if (*cmd) + o = Rcon_Command(cluster, NULL, cmd, result, sizeof(result), false); + else + o = ""; + cluster->numproxies++; + } + else + { + o = "Bad Password"; + } + if (o != result) + { + strcpy(result, o); + o = result; + } + + HTTPSV_SendHTTPHeader(cluster, dest, "200", "text/html", true); + HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Admin"); + + s = "

QuakeTV Admin: "; + Net_ProxySend(cluster, dest, s, strlen(s)); + s = cluster->hostname; + Net_ProxySend(cluster, dest, s, strlen(s)); + s = "

"; + Net_ProxySend(cluster, dest, s, strlen(s)); + + + s = "
" + "
" + "Password " + "
" + "Command " + "" + "
" + "
"; + Net_ProxySend(cluster, dest, s, strlen(s)); + + if (passwordokay) + HTMLPRINT(""); + else + HTMLPRINT(""); + + while(*o) + { + s = strchr(o, '\n'); + if (s) + *s = 0; + HTMLprintf(cmd, sizeof(cmd), "%s", o); + Net_ProxySend(cluster, dest, cmd, strlen(cmd)); + Net_ProxySend(cluster, dest, "
", 6); + if (!s) + break; + o = s+1; + } + + HTTPSV_SendHTMLFooter(cluster, dest); +} + +static void HTTPSV_GenerateDemoListing(cluster_t *cluster, oproxy_t *dest) +{ + int i; + char link[256]; + char *s; + + HTTPSV_SendHTTPHeader(cluster, dest, "200", "text/html", true); + HTTPSV_SendHTMLHeader(cluster, dest, "QuakeTV: Demos"); + + s = "

QuakeTV: Demo Listing

"; + Net_ProxySend(cluster, dest, s, strlen(s)); + + Cluster_BuildAvailableDemoList(cluster); + for (i = 0; i < cluster->availdemoscount; i++) + { + snprintf(link, sizeof(link), "%s (%ikb)
", cluster->availdemos[i].name, cluster->availdemos[i].name, cluster->availdemos[i].size/1024); + Net_ProxySend(cluster, dest, link, strlen(link)); + } + + sprintf(link, "

Total: %i demos

", cluster->availdemoscount); + Net_ProxySend(cluster, dest, link, strlen(link)); + + HTTPSV_SendHTMLFooter(cluster, dest); +} + +static void HTTPSV_GenerateDownload(cluster_t *cluster, oproxy_t *dest, char *filename) +{ +#ifdef ALLOWDOWNLOADS + char fname[256]; + char *s; + + if (cluster->allowdownloads) +#endif + { + HTTPSV_SendHTTPHeader(cluster, dest, "403", "text/html", true); + HTTPSV_SendHTMLHeader(cluster, dest, "Permission denied"); + HTMLPRINT("

403: Forbidden

"); + HTMLPRINT("File downloads from this proxy are currently not permitted."); + HTTPSV_SendHTMLFooter(cluster, dest); + return; + } +#ifdef ALLOWDOWNLOADS + s = fname; + while (*filename > ' ') + { + if (s > fname + sizeof(fname)-4) //4 cos I'm too lazy to work out what the actual number should be + break; + if (*filename == '%') + { + *s = 0; + filename++; + if (*filename <= ' ') + break; + else if (*filename >= 'a' && *filename <= 'f') + *s += 10 + *filename-'a'; + else if (*filename >= 'A' && *filename <= 'F') + *s += 10 + *filename-'A'; + else if (*filename >= '0' && *filename <= '9') + *s += *filename-'0'; + + *s <<= 4; + + filename++; + if (*filename <= ' ') + break; + else if (*filename >= 'a' && *filename <= 'f') + *s += 10 + *filename-'a'; + else if (*filename >= 'A' && *filename <= 'F') + *s += 10 + *filename-'A'; + else if (*filename >= '0' && *filename <= '9') + *s += *filename-'0'; + + s++; + } + else + *s++ = *filename++; + } + *s = 0; + dest->srcfile = fopen(fname, "rb"); + + if (dest->srcfile) + { + HTTPSV_SendHTTPHeader(cluster, dest, "200", "application/x-forcedownload", false); + } + else + { + HTTPSV_SendHTTPHeader(cluster, dest, "404", "text/html", true); + HTTPSV_SendHTMLHeader(cluster, dest, "File not found"); + HTMLPRINT("

404: File not found

"); + HTTPSV_SendHTMLFooter(cluster, dest); + } +#endif +} + + + + + + + +void HTTPSV_PostMethod(cluster_t *cluster, oproxy_t *pend, char *postdata) +{ + char tempbuf[512]; + char *s; + int len; + + if (!HTTPSV_GetHeaderField((char*)pend->inbuffer, "Content-Length", tempbuf, sizeof(tempbuf))) + { + s = "HTTP/1.1 411 OK\n" + "Content-Type: text/html\n" + "Connection: close\n" + "\n" + "QuakeTVNo Content-Length was provided.\n"; + Net_ProxySend(cluster, pend, s, strlen(s)); + pend->flushing = true; + return; + } + len = atoi(tempbuf); + if (pend->inbuffersize + len >= sizeof(pend->inbuffer)-20) + { //too much data + pend->flushing = true; + return; + } + len = postdata - (char*)pend->inbuffer + len; + if (len > pend->inbuffersize) + return; //still need the body + +// if (len <= pend->inbuffersize) + { + if (!strncmp((char*)pend->inbuffer+5, "/admin", 6)) + { + HTTPSV_GenerateAdmin(cluster, pend, 0, postdata); + } + else + { + s = "HTTP/1.1 404 OK\n" + "Content-Type: text/html\n" + "Connection: close\n" + "\n" + "QuakeTVThat HTTP method is not supported for that URL.\n"; + Net_ProxySend(cluster, pend, s, strlen(s)); + + } + pend->flushing = true; + return; + } +} + +void HTTPSV_GetMethod(cluster_t *cluster, oproxy_t *pend) +{ + char *s; + if (!strncmp((char*)pend->inbuffer+4, "/nowplaying", 11)) + { + HTTPSV_GenerateNowPlaying(cluster, pend); + } + else if (!strncmp((char*)pend->inbuffer+4, "/watch.qtv?sid=", 15)) + { + HTTPSV_GenerateQTVStub(cluster, pend, "", (char*)pend->inbuffer+19); + } + else if (!strncmp((char*)pend->inbuffer+4, "/watch.qtv?demo=", 16)) + { + HTTPSV_GenerateQTVStub(cluster, pend, "file:", (char*)pend->inbuffer+20); + } +// else if (!strncmp((char*)pend->inbuffer+4, "/demo/", 6)) +// { //fixme: make this send the demo as an http download +// HTTPSV_GenerateQTVStub(cluster, pend, "file:", (char*)pend->inbuffer+10); +// } + else if (!strncmp((char*)pend->inbuffer+4, "/about", 6)) + { //redirect them to our funky website + s = "HTTP/1.0 302 Found\n" + "Location: http://www.fteqw.com/\n" + "\n"; + Net_ProxySend(cluster, pend, s, strlen(s)); + } + else if (!strncmp((char*)pend->inbuffer+4, "/admin", 6)) + { + HTTPSV_GenerateAdmin(cluster, pend, 0, NULL); + } + else if (!strncmp((char*)pend->inbuffer+4, "/ ", 2)) + { + s = "HTTP/1.0 302 Found\n" + "Location: /nowplaying/\n" + "\n"; + Net_ProxySend(cluster, pend, s, strlen(s)); + } + else if (!strncmp((char*)pend->inbuffer+4, "/demos", 6)) + { + HTTPSV_GenerateDemoListing(cluster, pend); + } + else if (!strncmp((char*)pend->inbuffer+4, "/file/", 6)) + { + HTTPSV_GenerateDownload(cluster, pend, (char*)pend->inbuffer+10); + } + else if (!strncmp((char*)pend->inbuffer+4, "/style.css", 10)) + { + HTTPSV_GenerateCSSFile(cluster, pend); + } + else + { +#define dest pend + HTTPSV_SendHTTPHeader(cluster, dest, "404", "text/html", true); + HTTPSV_SendHTMLHeader(cluster, dest, "Address not recognised"); + HTMLPRINT("

Address not recognised

"); + HTTPSV_SendHTMLFooter(cluster, dest); + } +} diff --git a/fteqtv/msg.c b/fteqtv/msg.c new file mode 100644 index 000000000..3bc54dbca --- /dev/null +++ b/fteqtv/msg.c @@ -0,0 +1,146 @@ +#include "qtv.h" + +void InitNetMsg(netmsg_t *b, void *buffer, int bufferlength) +{ + b->data = buffer; + b->maxsize = bufferlength; + b->readpos = 0; + b->cursize = 0; +} + +unsigned char ReadByte(netmsg_t *b) +{ + if (b->readpos >= b->cursize) + { + b->readpos = b->cursize+1; + return 0; + } + return ((unsigned char *)b->data)[b->readpos++]; +} +unsigned short ReadShort(netmsg_t *b) +{ + int b1, b2; + b1 = ReadByte(b); + b2 = ReadByte(b); + + return b1 | (b2<<8); +} +unsigned int ReadLong(netmsg_t *b) +{ + int s1, s2; + s1 = ReadShort(b); + s2 = ReadShort(b); + + return s1 | (s2<<16); +} + +unsigned int BigLong(unsigned int val) +{ + union { + unsigned int i; + unsigned char c[4]; + } v; + + v.i = val; + return (v.c[0]<<24) | (v.c[1] << 16) | (v.c[2] << 8) | (v.c[3] << 0); +} + +unsigned int SwapLong(unsigned int val) +{ + union { + unsigned int i; + unsigned char c[4]; + } v; + unsigned char s; + + v.i = val; + s = v.c[0]; + v.c[0] = v.c[3]; + v.c[3] = s; + s = v.c[1]; + v.c[1] = v.c[2]; + v.c[2] = s; + + return v.i; +} + +float ReadFloat(netmsg_t *b) +{ + union { + unsigned int i; + float f; + } u; + + u.i = ReadLong(b); + return u.f; +} +void ReadString(netmsg_t *b, char *string, int maxlen) +{ + maxlen--; //for null terminator + while(maxlen) + { + *string = ReadByte(b); + if (!*string) + return; + string++; + maxlen--; + } + *string++ = '\0'; //add the null + + printf("ReadString: buffer is too small\n"); + while(ReadByte(b)) //finish reading the string, even if we will loose part of it + ; +} + +void WriteByte(netmsg_t *b, unsigned char c) +{ + if (b->cursize>=b->maxsize) + return; + ((unsigned char*)b->data)[b->cursize++] = c; +} +void WriteShort(netmsg_t *b, unsigned short l) +{ + WriteByte(b, (l&0x00ff)>>0); + WriteByte(b, (l&0xff00)>>8); +} +void WriteLong(netmsg_t *b, unsigned int l) +{ + WriteByte(b, (l&0x000000ff)>>0); + WriteByte(b, (l&0x0000ff00)>>8); + WriteByte(b, (l&0x00ff0000)>>16); + WriteByte(b, (l&0xff000000)>>24); +} +void WriteFloat(netmsg_t *b, float f) +{ + union { + unsigned int i; + float f; + } u; + + u.f = f; + WriteLong(b, u.i); +} +void WriteString2(netmsg_t *b, const char *str) +{ //no null terminator, convienience function. + while(*str) + WriteByte(b, *str++); +} +void WriteString(netmsg_t *b, const char *str) +{ + while(*str) + WriteByte(b, *str++); + WriteByte(b, 0); +} +void WriteData(netmsg_t *b, const void *data, int length) +{ + int i; + unsigned char *buf; + + if (b->cursize + length > b->maxsize) //urm, that's just too big. :( + return; + buf = b->data+b->cursize; + for (i = 0; i < length; i++) + *buf++ = ((unsigned char*)data)[i]; + b->cursize+=length; +} + diff --git a/fteqtv/netchan.c b/fteqtv/netchan.c index 1fb2125cb..b592ea07c 100644 --- a/fteqtv/netchan.c +++ b/fteqtv/netchan.c @@ -25,7 +25,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, char *data, netadr_t adr) +void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, void *data, netadr_t adr) { int ret; @@ -120,7 +120,7 @@ Netchan_OutOfBand Sends an out-of-band datagram ================ */ -void Netchan_OutOfBand (cluster_t *cluster, SOCKET sock, netadr_t adr, int length, unsigned char *data) +void Netchan_OutOfBand (cluster_t *cluster, SOCKET sock, netadr_t adr, int length, void *data) { netmsg_t send; unsigned char send_buf[MAX_MSGLEN + PACKET_HEADER]; @@ -205,7 +205,6 @@ Netchan_CanPacket Returns true if the bandwidth choke isn't active ================ */ -#define MAX_BACKUP 200 qboolean Netchan_CanPacket (netchan_t *chan) { unsigned int t; @@ -214,7 +213,7 @@ qboolean Netchan_CanPacket (netchan_t *chan) // return true; t = curtime; - if (chan->cleartime < t + MAX_BACKUP*chan->rate) + if (chan->cleartime < t) return true; return false; } @@ -244,7 +243,7 @@ transmition / retransmition of the reliable messages. A 0 length will still generate a packet and deal with the reliable messages. ================ */ -void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const unsigned char *data) +void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const void *data) { unsigned int t; netmsg_t send; @@ -291,9 +290,9 @@ void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const un NET_SendPacket(cluster, chan->sock, send.cursize, send.data, chan->remote_address); if (chan->cleartime < curtime) - chan->cleartime = curtime + send.cursize*chan->rate; + chan->cleartime = curtime + (int)(send.cursize*chan->rate); else - chan->cleartime += send.cursize*chan->rate; + chan->cleartime += (int)(send.cursize*chan->rate); } //send out the unreliable (if still unsent) @@ -309,9 +308,9 @@ void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const un NET_SendPacket (cluster, chan->sock, send.cursize, send.data, chan->remote_address); if (chan->cleartime < curtime) - chan->cleartime = curtime + send.cursize*chan->rate; + chan->cleartime = (int)(curtime + send.cursize*chan->rate); else - chan->cleartime += send.cursize*chan->rate; + chan->cleartime += (int)(send.cursize*chan->rate); send.cursize = 0; } @@ -386,9 +385,9 @@ void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const un t = curtime; if (chan->cleartime < t) - chan->cleartime = t + send.cursize*chan->rate; + chan->cleartime = t + (int)(send.cursize*chan->rate); else - chan->cleartime += send.cursize*chan->rate; + chan->cleartime += (int)(send.cursize*chan->rate); #ifndef CLIENTONLY // if (chan->sock == NS_SERVER && sv_paused.value) // chan->cleartime = curtime; diff --git a/fteqtv/parse.c b/fteqtv/parse.c index 5ee953aa1..08a91b7b0 100644 --- a/fteqtv/parse.c +++ b/fteqtv/parse.c @@ -22,147 +22,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define ParseError(m) (m)->cursize = (m)->cursize+1 // -void InitNetMsg(netmsg_t *b, char *buffer, int bufferlength) -{ - b->data = buffer; - b->maxsize = bufferlength; - b->readpos = 0; - b->cursize = 0; -} - -//probably not the place for these any more.. -unsigned char ReadByte(netmsg_t *b) -{ - if (b->readpos >= b->cursize) - { - b->readpos = b->cursize+1; - return 0; - } - return b->data[b->readpos++]; -} -unsigned short ReadShort(netmsg_t *b) -{ - int b1, b2; - b1 = ReadByte(b); - b2 = ReadByte(b); - - return b1 | (b2<<8); -} -unsigned int ReadLong(netmsg_t *b) -{ - int s1, s2; - s1 = ReadShort(b); - s2 = ReadShort(b); - - return s1 | (s2<<16); -} - -unsigned int BigLong(unsigned int val) -{ - union { - unsigned int i; - unsigned char c[4]; - } v; - - v.i = val; - return (v.c[0]<<24) | (v.c[1] << 16) | (v.c[2] << 8) | (v.c[3] << 0); -} - -unsigned int SwapLong(unsigned int val) -{ - union { - unsigned int i; - unsigned char c[4]; - } v; - unsigned char s; - - v.i = val; - s = v.c[0]; - v.c[0] = v.c[3]; - v.c[3] = s; - s = v.c[1]; - v.c[1] = v.c[2]; - v.c[2] = s; - - return v.i; -} - -float ReadFloat(netmsg_t *b) -{ - union { - unsigned int i; - float f; - } u; - - u.i = ReadLong(b); - return u.f; -} -void ReadString(netmsg_t *b, char *string, int maxlen) -{ - maxlen--; //for null terminator - while(maxlen) - { - *string = ReadByte(b); - if (!*string) - return; - string++; - maxlen--; - } - *string++ = '\0'; //add the null -} - -void WriteByte(netmsg_t *b, unsigned char c) -{ - if (b->cursize>=b->maxsize) - return; - b->data[b->cursize++] = c; -} -void WriteShort(netmsg_t *b, unsigned short l) -{ - WriteByte(b, (l&0x00ff)>>0); - WriteByte(b, (l&0xff00)>>8); -} -void WriteLong(netmsg_t *b, unsigned int l) -{ - WriteByte(b, (l&0x000000ff)>>0); - WriteByte(b, (l&0x0000ff00)>>8); - WriteByte(b, (l&0x00ff0000)>>16); - WriteByte(b, (l&0xff000000)>>24); -} -void WriteFloat(netmsg_t *b, float f) -{ - union { - unsigned int i; - float f; - } u; - - u.f = f; - WriteLong(b, u.i); -} -void WriteString2(netmsg_t *b, const char *str) -{ //no null terminator, convienience function. - while(*str) - WriteByte(b, *str++); -} -void WriteString(netmsg_t *b, const char *str) -{ - while(*str) - WriteByte(b, *str++); - WriteByte(b, 0); -} -void WriteData(netmsg_t *b, const char *data, int length) -{ - int i; - unsigned char *buf; - - if (b->cursize + length > b->maxsize) //urm, that's just too big. :( - return; - buf = b->data+b->cursize; - for (i = 0; i < length; i++) - *buf++ = *data++; - b->cursize+=length; -} - void SendBufferToViewer(viewer_t *v, const char *buffer, int length, qboolean reliable) { if (reliable) @@ -176,8 +35,11 @@ void SendBufferToViewer(viewer_t *v, const char *buffer, int length, qboolean re { v->netchan.message.cursize = 0; WriteByte(&v->netchan.message, svc_print); + if (!v->netchan.isnqprotocol) + WriteByte(&v->netchan.message, PRINT_HIGH); WriteString(&v->netchan.message, "backbuffer overflow\n"); - Sys_Printf(NULL, "%s backbuffers overflowed\n", v->name); //FIXME + if (!v->drop) + Sys_Printf(NULL, "%s backbuffers overflowed\n", v->name); //FIXME v->drop = true; //we would need too many backbuffers. } else @@ -194,7 +56,7 @@ void SendBufferToViewer(viewer_t *v, const char *buffer, int length, qboolean re } } -void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playermask, int suitablefor) +void Multicast(sv_t *tv, void *buffer, int length, int to, unsigned int playermask, int suitablefor) { viewer_t *v; switch(to) @@ -227,7 +89,7 @@ void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playerma break; } } -void Broadcast(cluster_t *cluster, char *buffer, int length, int suitablefor) +void Broadcast(cluster_t *cluster, void *buffer, int length, int suitablefor) { viewer_t *v; for (v = cluster->viewers; v; v = v->next) @@ -237,6 +99,12 @@ void Broadcast(cluster_t *cluster, char *buffer, int length, int suitablefor) } } +void ConnectionData(sv_t *tv, void *buffer, int length, int to, unsigned int playermask, int suitablefor) +{ + if (!tv->parsingconnectiondata) + Multicast(tv, buffer, length, to, playermask, suitablefor); +} + static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playermask) { int i; @@ -258,7 +126,7 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma ReadString(m, tv->gamedir, sizeof(tv->gamedir)); - if (tv->usequkeworldprotocols) + if (tv->usequakeworldprotocols) tv->thisplayer = ReadByte(m)&~128; else { @@ -306,7 +174,7 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma tv->frame[i].numents = 0; } - if (tv->usequkeworldprotocols) + if (tv->usequakeworldprotocols) { tv->netchan.message.cursize = 0; //mvdsv sucks SendClientCommand(tv, "soundlist %i 0\n", tv->clservercount); @@ -319,15 +187,12 @@ static void ParseCDTrack(sv_t *tv, netmsg_t *m, int to, unsigned int mask) char nqversion[3]; tv->cdtrack = ReadByte(m); - if (!tv->parsingconnectiondata) - { - Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, QW); + ConnectionData(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, QW); - nqversion[0] = svc_cdtrack; - nqversion[1] = tv->cdtrack; - nqversion[2] = tv->cdtrack; - Multicast(tv, nqversion, 3, to, mask, NQ); - } + nqversion[0] = svc_cdtrack; + nqversion[1] = tv->cdtrack; + nqversion[2] = tv->cdtrack; + ConnectionData(tv, nqversion, 3, to, mask, NQ); } static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) { @@ -351,7 +216,13 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) QW_SetMenu(tv->controller, atoi(text+18)?MENU_FORWARDING:MENU_NONE); } else if (strstr(text, "screenshot")) + { + if (tv->controller) + { //let it through to the controller + SendBufferToViewer(tv->controller, m->data+m->startpos, m->readpos - m->startpos, true); + } return; //this was generating far too many screenshots when watching demos + } else if (!strcmp(text, "skins\n")) { const char newcmd[10] = {svc_stufftext, 'c', 'm', 'd', ' ', 'n','e','w','\n','\0'}; @@ -368,7 +239,7 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) } } - if (tv->usequkeworldprotocols) + if (tv->usequakeworldprotocols) SendClientCommand(tv, "begin %i\n", tv->clservercount); return; } @@ -415,22 +286,25 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) } else if (!strncmp(text, "cmd ", 4)) { - if (tv->usequkeworldprotocols) + if (tv->usequakeworldprotocols) SendClientCommand(tv, "%s", text+4); return; //commands the game server asked for are pointless. } else if (!strncmp(text, "reconnect", 9)) { - if (tv->usequkeworldprotocols) + if (tv->usequakeworldprotocols) SendClientCommand(tv, "new\n"); return; } else if (!strncmp(text, "packet ", 7)) { - if(tv->usequkeworldprotocols) - {//eeeevil hack - -#define ARG_LEN 256 + if (tv->controller) + { //if we're acting as a proxy, forward the realip packets, and ONLY to the controller + SendBufferToViewer(tv->controller, m->data+m->startpos, m->readpos - m->startpos, true); + return; + } + if(tv->usequakeworldprotocols) + {//eeeevil hack for proxy-spectating char *ptr; char arg[3][ARG_LEN]; netadr_t adr; @@ -438,7 +312,7 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) ptr = COM_ParseToken(ptr, arg[0], ARG_LEN, ""); ptr = COM_ParseToken(ptr, arg[1], ARG_LEN, ""); ptr = COM_ParseToken(ptr, arg[2], ARG_LEN, ""); - NET_StringToAddr(arg[1], &adr, 27500); + NET_StringToAddr(arg[1], &adr, PROX_DEFAULTSERVERPORT); Netchan_OutOfBand(tv->cluster, tv->sourcesock, adr, strlen(arg[2]), arg[2]); //this is an evil hack @@ -448,7 +322,7 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) tv->drop = true; //this shouldn't ever happen return; } - else if (tv->usequkeworldprotocols && !strncmp(text, "setinfo ", 8)) + else if (tv->usequakeworldprotocols && !strncmp(text, "setinfo ", 8)) { Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1); SendClientCommand(tv, text); @@ -472,8 +346,7 @@ static void ParseSetInfo(sv_t *tv, netmsg_t *m) 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, QW); + ConnectionData(tv, m->data+m->startpos, m->readpos - m->startpos, dem_all, (unsigned)-1, QW); } static void ParseServerinfo(sv_t *tv, netmsg_t *m) @@ -486,8 +359,7 @@ static void ParseServerinfo(sv_t *tv, netmsg_t *m) if (strcmp(key, "hostname")) //don't allow the hostname to change, but allow the server to change other serverinfos. Info_SetValueForStarKey(tv->serverinfo, key, value, sizeof(tv->serverinfo)); - if (!tv->parsingconnectiondata) - Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, dem_all, (unsigned)-1, QW); + ConnectionData(tv, m->data+m->startpos, m->readpos - m->startpos, dem_all, (unsigned)-1, QW); } static void ParsePrint(sv_t *tv, netmsg_t *m, int to, unsigned int mask) @@ -510,7 +382,7 @@ static void ParsePrint(sv_t *tv, netmsg_t *m, int to, unsigned int mask) } buffer[0] = svc_print; - if (to == dem_all || to == dem_read) + if ((to&dem_mask) == dem_all || to == dem_read) { if (level > 1) { @@ -619,8 +491,8 @@ static void ParseStaticSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask) tv->staticsound[tv->staticsound_count].attenuation = ReadByte(m); tv->staticsound_count++; - if (!tv->parsingconnectiondata) - Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1); + + ConnectionData(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1); } static void ParseIntermission(sv_t *tv, netmsg_t *m, int to, unsigned int mask) @@ -647,8 +519,7 @@ void ParseSpawnStatic(sv_t *tv, netmsg_t *m, int to, unsigned int mask) tv->spawnstatic_count++; - if (!tv->parsingconnectiondata) - Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1); + ConnectionData(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1); } extern const usercmd_t nullcmd; @@ -676,7 +547,7 @@ static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers) } tv->players[num].old = tv->players[num].current; - if (tv->usequkeworldprotocols) + if (tv->usequakeworldprotocols) { tv->players[num].old = tv->players[num].current; flags = (unsigned short)ReadShort (m); @@ -790,7 +661,6 @@ static int readentitynum(netmsg_t *m, unsigned int *retflags) { int entnum; unsigned int flags; - unsigned short moreflags = 0; flags = ReadShort(m); if (!flags) { @@ -925,7 +795,7 @@ static void ParsePacketEntities(sv_t *tv, netmsg_t *m, int deltaframe) if (deltaframe != -1) deltaframe &= (ENTITY_FRAMES-1); - if (tv->usequkeworldprotocols) + if (tv->usequakeworldprotocols) { newframe = &tv->frame[tv->netchan.incoming_sequence & (ENTITY_FRAMES-1)]; @@ -1457,7 +1327,7 @@ void ParseNails(sv_t *tv, netmsg_t *m, qboolean nails2) int i; count = (unsigned char)ReadByte(m); while(count > sizeof(tv->nails) / sizeof(tv->nails[0])) - { + {//they sent too many, suck it out. count--; if (nails2) ReadByte(m); @@ -1537,7 +1407,7 @@ void ParseDownload(sv_t *tv, netmsg_t *m) } } -void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) +void ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask) { int i; netmsg_t buf; @@ -1571,8 +1441,16 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) //the client is meant to read that and disconnect without reading the intentionally corrupt packet following it. //however, our demo playback is chained and looping and buffered. //so we've already found the end of the source file and restarted parsing. - //so there's very little we can do except crash ourselves on the EndOfDemo text following the svc_disconnect - //that's a bad plan, so just stop reading this packet. + + //in fte at least, the server does give the packet the correct length + //I hope mvdsv is the same + if (tv->sourcetype != SRC_DEMO) + tv->drop = true; + else + { + while(ReadByte(&buf)) + ; + } return; case svc_updatestat: @@ -1580,11 +1458,17 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) break; //#define svc_version 4 // [long] server version -//#define svc_setview 5 // [short] entity number + case svc_nqsetview: + ReadShort(&buf); +//no actual handling is done! + break; case svc_sound: ParseSound(tv, &buf, to, mask); break; -//#define svc_time 7 // [float] server time + case svc_nqtime: + ReadFloat(&buf); +//no actual handling is done! + break; case svc_print: ParsePrint(tv, &buf, to, mask); @@ -1595,13 +1479,13 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) break; case svc_setangle: - if (!tv->usequkeworldprotocols) + if (!tv->usequakeworldprotocols) ReadByte(&buf); tv->proxyplayerangles[0] = ReadByte(&buf)*360.0/255; tv->proxyplayerangles[1] = ReadByte(&buf)*360.0/255; tv->proxyplayerangles[2] = ReadByte(&buf)*360.0/255; - if (tv->usequkeworldprotocols && tv->controller) + if (tv->usequakeworldprotocols && tv->controller) SendBufferToViewer(tv->controller, buf.data+buf.startpos, buf.readpos - buf.startpos, true); { @@ -1672,9 +1556,6 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) ParseCenterprint(tv, &buf, to, mask); break; -//#define svc_killedmonster 27 -//#define svc_foundsecret 28 - case svc_spawnstaticsound: ParseStaticSound(tv, &buf, to, mask); break; @@ -1683,13 +1564,19 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) ParseIntermission(tv, &buf, to, mask); break; -//#define svc_finale 31 // [string] text + case svc_finale: + while(ReadByte(&buf)) + ; + Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, dem_read, (unsigned)-1, Q1); + break; case svc_cdtrack: ParseCDTrack(tv, &buf, to, mask); break; -//#define svc_sellscreen 33 + case svc_sellscreen: + Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, dem_read, (unsigned)-1, Q1); + break; //#define svc_cutscene 34 //hmm... nq only... added after qw tree splitt? @@ -1757,7 +1644,7 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) } strcpy(tv->status, "Prespawning\n"); } - if (tv->usequkeworldprotocols) + if (tv->usequakeworldprotocols) { if (i) SendClientCommand(tv, "modellist %i %i\n", tv->clservercount, i); @@ -1795,7 +1682,7 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) i = ParseList(tv, &buf, tv->soundlist, to, mask); if (!i) strcpy(tv->status, "Receiving modellist\n"); - if (tv->usequkeworldprotocols) + if (tv->usequakeworldprotocols) { if (i) SendClientCommand(tv, "soundlist %i %i\n", tv->clservercount, i); diff --git a/fteqtv/pmove.c b/fteqtv/pmove.c index 37db2d792..9a65a7131 100644 --- a/fteqtv/pmove.c +++ b/fteqtv/pmove.c @@ -1,161 +1,164 @@ -#include "qtv.h" -#include - -#define M_PI 3.1415926535897932384626433832795 -void AngleVectors (vec3_t angles, float *forward, float *right, float *up) -{ - float angle; - float sr, sp, sy, cr, cp, cy; - - angle = angles[1] * (M_PI*2 / 360); - sy = sin(angle); - cy = cos(angle); - angle = angles[0] * (M_PI*2 / 360); - sp = sin(angle); - cp = cos(angle); - angle = angles[2] * (M_PI*2 / 360); - sr = sin(angle); - cr = cos(angle); - - forward[0] = cp*cy; - forward[1] = cp*sy; - forward[2] = -sp; - right[0] = (-1*sr*sp*cy+-1*cr*-sy); - right[1] = (-1*sr*sp*sy+-1*cr*cy); - right[2] = -1*sr*cp; - up[0] = (cr*sp*cy+-sr*-sy); - up[1] = (cr*sp*sy+-sr*cy); - up[2] = cr*cp; -} - - - - -#define DotProduct(a,b) ((a[0]*b[0]) + (a[1]*b[1]) + (a[2]*b[2])) -#define VectorCopy(a,b) do{b[0]=a[0];b[1]=a[1];b[2]=a[2];}while(0) -#define VectorClear(v) do{v[0]=0;v[1]=0;v[2]=0;}while(0) -#define VectorScale(i,s,o) do{o[0]=i[0]*s;o[1]=i[1]*s;o[2]=i[2]*s;}while(0) -#define Length(v) sqrt(DotProduct(v, v)) -#define VectorMA(base,s,m,out) do{out[0]=base[0]+s*m[0];out[1]=base[1]+s*m[1];out[2]=base[2]+s*m[2];}while(0) -#define SHORT2ANGLE(s) ((s*360.0f)/65536) - -float VectorNormalize(vec3_t v) -{ - float len, ilen; - len = Length(v); - if (len) - { - ilen = 1/len; - v[0] *= ilen; - v[1] *= ilen; - v[2] *= ilen; - } - return len; -} - - -void PM_SpectatorMove (pmove_t *pmove) -{ - float speed, drop, friction, control, newspeed; - float currentspeed, addspeed, accelspeed; - int i; - vec3_t wishvel; - float fmove, smove; - vec3_t wishdir; - float wishspeed; - - // friction - - speed = Length (pmove->velocity); - if (speed < 1) - { - VectorClear (pmove->velocity); - } - else - { - drop = 0; - - friction = pmove->movevars.friction*1.5; // extra friction - control = speed < pmove->movevars.stopspeed ? pmove->movevars.stopspeed : speed; - drop += control*friction*pmove->frametime; - - // scale the velocity - newspeed = speed - drop; - if (newspeed < 0) - newspeed = 0; - newspeed /= speed; - - VectorScale (pmove->velocity, newspeed, pmove->velocity); - } - - // accelerate - fmove = pmove->cmd.forwardmove; - smove = pmove->cmd.sidemove; - - VectorNormalize (pmove->forward); - VectorNormalize (pmove->right); - - for (i=0 ; i<3 ; i++) - wishvel[i] = pmove->forward[i]*fmove + pmove->right[i]*smove; - wishvel[2] += pmove->cmd.upmove; - - VectorCopy (wishvel, wishdir); - wishspeed = VectorNormalize(wishdir); - - // - // clamp to server defined max speed - // - if (wishspeed > pmove->movevars.spectatormaxspeed) - { - VectorScale (wishvel, pmove->movevars.spectatormaxspeed/wishspeed, wishvel); - wishspeed = pmove->movevars.spectatormaxspeed; - } - - currentspeed = DotProduct(pmove->velocity, wishdir); - addspeed = wishspeed - currentspeed; - - // Buggy QW spectator mode, kept for compatibility -// if (pmove->pm_type == PM_OLD_SPECTATOR) - { - if (addspeed <= 0) - return; - } - - if (addspeed > 0) - { - accelspeed = pmove->movevars.accelerate*pmove->frametime*wishspeed; - if (accelspeed > addspeed) - accelspeed = addspeed; - - for (i=0 ; i<3 ; i++) - pmove->velocity[i] += accelspeed*wishdir[i]; - } - - // move - VectorMA (pmove->origin, pmove->frametime, pmove->velocity, pmove->origin); -} - -void PM_PlayerMove (pmove_t *pmove) -{ - pmove->frametime = pmove->cmd.msec * 0.001; -/* - if (pmove.pm_type == PM_NONE || pmove.pm_type == PM_FREEZE) - { - PM_CategorizePosition (); - return; - } -*/ - // take angles directly from command - pmove->angles[0] = SHORT2ANGLE(pmove->cmd.angles[0]); - pmove->angles[1] = SHORT2ANGLE(pmove->cmd.angles[1]); - pmove->angles[2] = SHORT2ANGLE(pmove->cmd.angles[2]); - - AngleVectors (pmove->angles, pmove->forward, pmove->right, pmove->up); - -// if (pmove->pm_type == PM_SPECTATOR || pmove->pm_type == PM_OLD_SPECTATOR) - { - PM_SpectatorMove (pmove); -// pmove->onground = false; - return; - } -} \ No newline at end of file +#include "qtv.h" +#include + +#ifndef M_PI +#define M_PI 3.1415926535897932384626433832795 +#endif + +void AngleVectors (vec3_t angles, float *forward, float *right, float *up) +{ + float angle; + float sr, sp, sy, cr, cp, cy; + + angle = angles[1] * (M_PI*2 / 360); + sy = sin(angle); + cy = cos(angle); + angle = angles[0] * (M_PI*2 / 360); + sp = sin(angle); + cp = cos(angle); + angle = angles[2] * (M_PI*2 / 360); + sr = sin(angle); + cr = cos(angle); + + forward[0] = cp*cy; + forward[1] = cp*sy; + forward[2] = -sp; + right[0] = (-1*sr*sp*cy+-1*cr*-sy); + right[1] = (-1*sr*sp*sy+-1*cr*cy); + right[2] = -1*sr*cp; + up[0] = (cr*sp*cy+-sr*-sy); + up[1] = (cr*sp*sy+-sr*cy); + up[2] = cr*cp; +} + + + + +#define DotProduct(a,b) ((a[0]*b[0]) + (a[1]*b[1]) + (a[2]*b[2])) +#define VectorCopy(a,b) do{b[0]=a[0];b[1]=a[1];b[2]=a[2];}while(0) +#define VectorClear(v) do{v[0]=0;v[1]=0;v[2]=0;}while(0) +#define VectorScale(i,s,o) do{o[0]=i[0]*s;o[1]=i[1]*s;o[2]=i[2]*s;}while(0) +#define Length(v) sqrt(DotProduct(v, v)) +#define VectorMA(base,s,m,out) do{out[0]=base[0]+s*m[0];out[1]=base[1]+s*m[1];out[2]=base[2]+s*m[2];}while(0) +#define SHORT2ANGLE(s) ((s*360.0f)/65536) + +float VectorNormalize(vec3_t v) +{ + float len, ilen; + len = Length(v); + if (len) + { + ilen = 1/len; + v[0] *= ilen; + v[1] *= ilen; + v[2] *= ilen; + } + return len; +} + + +void PM_SpectatorMove (pmove_t *pmove) +{ + float speed, drop, friction, control, newspeed; + float currentspeed, addspeed, accelspeed; + int i; + vec3_t wishvel; + float fmove, smove; + vec3_t wishdir; + float wishspeed; + + // friction + + speed = Length (pmove->velocity); + if (speed < 1) + { + VectorClear (pmove->velocity); + } + else + { + drop = 0; + + friction = pmove->movevars.friction*1.5; // extra friction + control = speed < pmove->movevars.stopspeed ? pmove->movevars.stopspeed : speed; + drop += control*friction*pmove->frametime; + + // scale the velocity + newspeed = speed - drop; + if (newspeed < 0) + newspeed = 0; + newspeed /= speed; + + VectorScale (pmove->velocity, newspeed, pmove->velocity); + } + + // accelerate + fmove = pmove->cmd.forwardmove; + smove = pmove->cmd.sidemove; + + VectorNormalize (pmove->forward); + VectorNormalize (pmove->right); + + for (i=0 ; i<3 ; i++) + wishvel[i] = pmove->forward[i]*fmove + pmove->right[i]*smove; + wishvel[2] += pmove->cmd.upmove; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + // + // clamp to server defined max speed + // + if (wishspeed > pmove->movevars.spectatormaxspeed) + { + VectorScale (wishvel, pmove->movevars.spectatormaxspeed/wishspeed, wishvel); + wishspeed = pmove->movevars.spectatormaxspeed; + } + + currentspeed = DotProduct(pmove->velocity, wishdir); + addspeed = wishspeed - currentspeed; + + // Buggy QW spectator mode, kept for compatibility +// if (pmove->pm_type == PM_OLD_SPECTATOR) + { + if (addspeed <= 0) + return; + } + + if (addspeed > 0) + { + accelspeed = pmove->movevars.accelerate*pmove->frametime*wishspeed; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i=0 ; i<3 ; i++) + pmove->velocity[i] += accelspeed*wishdir[i]; + } + + // move + VectorMA (pmove->origin, pmove->frametime, pmove->velocity, pmove->origin); +} + +void PM_PlayerMove (pmove_t *pmove) +{ + pmove->frametime = pmove->cmd.msec * 0.001; +/* + if (pmove.pm_type == PM_NONE || pmove.pm_type == PM_FREEZE) + { + PM_CategorizePosition (); + return; + } +*/ + // take angles directly from command + pmove->angles[0] = SHORT2ANGLE(pmove->cmd.angles[0]); + pmove->angles[1] = SHORT2ANGLE(pmove->cmd.angles[1]); + pmove->angles[2] = SHORT2ANGLE(pmove->cmd.angles[2]); + + AngleVectors (pmove->angles, pmove->forward, pmove->right, pmove->up); + +// if (pmove->pm_type == PM_SPECTATOR || pmove->pm_type == PM_OLD_SPECTATOR) + { + PM_SpectatorMove (pmove); +// pmove->onground = false; + return; + } +} diff --git a/fteqtv/protocol.h b/fteqtv/protocol.h new file mode 100644 index 000000000..56bffab8e --- /dev/null +++ b/fteqtv/protocol.h @@ -0,0 +1,256 @@ + + +//limitations of the protocol +#define MAX_SERVERINFO_STRING 1024 //standard quake has 512 here. +#define MAX_USERINFO 192 +#define MAX_CLIENTS 32 +#define MAX_LIST 256 +#define MAX_MODELS MAX_LIST +#define MAX_SOUNDS MAX_LIST +#define MAX_ENTITIES 512 +#define MAX_STATICSOUNDS 64 +#define MAX_STATICENTITIES 128 +#define MAX_LIGHTSTYLES 64 + +#define MAX_PROXY_INBUFFER 4096 +#define MAX_PROXY_BUFFER (1<<14) //must be power-of-two +#define PREFERED_PROXY_BUFFER 4096 //the ammount of data we try to leave in our input buffer (must be large enough to contain any single mvd frame) + +#define ENTS_PER_FRAME 64 //max number of entities per frame (OUCH!). +#define ENTITY_FRAMES 64 //number of frames to remember for deltaing + + +#define Z_EXT_SERVERTIME (1<<3) // STAT_TIME +#define Z_EXT_STRING "8" + +//qw specific +#define PRINT_CHAT 3 +#define PRINT_HIGH 2 +#define PRINT_MEDIUM 1 +#define PRINT_LOW 0 + + +#define MAX_STATS 32 +#define STAT_HEALTH 0 +#define STAT_FRAGS 1 +#define STAT_WEAPON 2 +#define STAT_AMMO 3 +#define STAT_ARMOR 4 +#define STAT_WEAPONFRAME 5 +#define STAT_SHELLS 6 +#define STAT_NAILS 7 +#define STAT_ROCKETS 8 +#define STAT_CELLS 9 +#define STAT_ACTIVEWEAPON 10 +#define STAT_TOTALSECRETS 11 +#define STAT_TOTALMONSTERS 12 +#define STAT_SECRETS 13 // bumped on client side by svc_foundsecret +#define STAT_MONSTERS 14 // bumped by svc_killedmonster +#define STAT_ITEMS 15 + +#define STAT_TIME 17 //A ZQ hack, sending time via a stat. + //this allows l33t engines to interpolate properly without spamming at a silly high fps. + + + + +//limits +#define NQ_PACKETS_PER_SECOND 20 +#define MAX_NQMSGLEN 8000 +#define MAX_MSGLEN 1400 +#define MAX_NQDATAGRAM 1024 +#define MAX_BACKBUF_SIZE 1000 //this is smaller so we don't loose entities when lagging + + +//NQ transport layer defines +#define NETFLAG_LENGTH_MASK 0x0000ffff +#define NETFLAG_DATA 0x00010000 +#define NETFLAG_ACK 0x00020000 +#define NETFLAG_NAK 0x00040000 +#define NETFLAG_EOM 0x00080000 +#define NETFLAG_UNRELIABLE 0x00100000 +#define NETFLAG_CTL 0x80000000 + +#define CCREQ_CONNECT 0x01 +#define CCREQ_SERVER_INFO 0x02 + +#define CCREP_ACCEPT 0x81 +#define CCREP_REJECT 0x82 +#define CCREP_SERVER_INFO 0x83 + +#define NET_GAMENAME_NQ "QUAKE" +#define NET_PROTOCOL_VERSION 3 +//end NQ specific + + + + +//the clcs sent via the udp connections +enum { + clc_bad = 0, + clc_nop = 1, + clc_disconnect = 2, //NQ only + clc_move = 3, // [[usercmd_t] + clc_stringcmd = 4, // [string] message + clc_delta = 5, // [byte] sequence number, requests delta compression of message + clc_tmove = 6, // teleport request, spectator only + clc_upload = 7 // teleport request, spectator only +}; + +//these are the clcs sent upstream via the tcp streams +enum { + qtv_clc_bad = 0, + qtv_clc_stringcmd = 1, + qtv_clc_commentarydata = 8 +}; + + + + + + +#define svc_bad 0 +#define svc_nop 1 +#define svc_disconnect 2 +#define svc_updatestat 3 // [qbyte] [qbyte] +//#define svc_version 4 // [long] server version (not used anywhere) +#define svc_nqsetview 5 // [short] entity number +#define svc_sound 6 // +#define svc_nqtime 7 // [float] server time +#define svc_print 8 // [qbyte] id [string] null terminated string +#define svc_stufftext 9 // [string] stuffed into client's console buffer + // the string should be \n terminated +#define svc_setangle 10 // [angle3] set the view angle to this absolute value + +#define svc_serverdata 11 // [long] protocol ... +#define svc_lightstyle 12 // [qbyte] [string] +#define svc_nqupdatename 13 // [qbyte] [string] +#define svc_updatefrags 14 // [qbyte] [short] +#define svc_nqclientdata 15 // +//#define svc_stopsound 16 // (not used anywhere) +#define svc_nqupdatecolors 17 // [qbyte] [qbyte] [qbyte] +#define svc_particle 18 // [vec3] +#define svc_damage 19 + +#define svc_spawnstatic 20 +//#define svc_spawnstatic2 21 (not used anywhere) +#define svc_spawnbaseline 22 + +#define svc_temp_entity 23 // variable +#define svc_setpause 24 // [qbyte] on / off +#define svc_nqsignonnum 25 // [qbyte] used for the signon sequence + +#define svc_centerprint 26 // [string] to put in center of the screen + +#define svc_killedmonster 27 +#define svc_foundsecret 28 + +#define svc_spawnstaticsound 29 // [coord3] [qbyte] samp [qbyte] vol [qbyte] aten + +#define svc_intermission 30 // [vec3_t] origin [vec3_t] angle (show scoreboard and stuff) +#define svc_finale 31 // [string] text ('congratulations blah blah') + +#define svc_cdtrack 32 // [qbyte] track +#define svc_sellscreen 33 + +//#define svc_cutscene 34 //hmm... nq only... added after qw tree splitt? (intermission without the scores) + +#define svc_smallkick 34 // set client punchangle to 2 +#define svc_bigkick 35 // set client punchangle to 4 + +#define svc_updateping 36 // [qbyte] [short] +#define svc_updateentertime 37 // [qbyte] [float] + +#define svc_updatestatlong 38 // [qbyte] [long] + +#define svc_muzzleflash 39 // [short] entity + +#define svc_updateuserinfo 40 // [qbyte] slot [long] uid + // [string] userinfo + +#define svc_download 41 // [short] size [size bytes] +#define svc_playerinfo 42 // variable +#define svc_nails 43 // [qbyte] num [48 bits] xyzpy 12 12 12 4 8 +#define svc_chokecount 44 // [qbyte] packets choked +#define svc_modellist 45 // [strings] +#define svc_soundlist 46 // [strings] +#define svc_packetentities 47 // [...] +#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_serverinfo 52 // serverinfo +#define svc_updatepl 53 // [qbyte] [qbyte] +#define svc_nails2 54 //mvd only - [qbyte] num [52 bits] nxyzpy 8 12 12 12 4 8 + + + + + +#define dem_audio 0 + +#define dem_cmd 0 +#define dem_read 1 +#define dem_set 2 +#define dem_multiple 3 +#define dem_single 4 +#define dem_stats 5 +#define dem_all 6 + +#define dem_qtvdata (dem_all | (1<<4)) //special packet that contains qtv data (spectator chat, etc. clients need to parse this as soon as it is sent to them, which might or might not be awkward for them) + +#define dem_mask 7 + + +#define PROTOCOL_VERSION_NQ 15 +#define PROTOCOL_VERSION 28 + + +//flags on entities +#define U_ORIGIN1 (1<<9) +#define U_ORIGIN2 (1<<10) +#define U_ORIGIN3 (1<<11) +#define U_ANGLE2 (1<<12) +#define U_FRAME (1<<13) +#define U_REMOVE (1<<14) // REMOVE this entity, don't add it +#define U_MOREBITS (1<<15) + +// if MOREBITS is set, these additional flags are read in next +#define U_ANGLE1 (1<<0) +#define U_ANGLE3 (1<<1) +#define U_MODEL (1<<2) +#define U_COLORMAP (1<<3) +#define U_SKIN (1<<4) +#define U_EFFECTS (1<<5) +#define U_SOLID (1<<6) // the entity should be solid for prediction + + + + + +//flags on players +#define PF_MSEC (1<<0) +#define PF_COMMAND (1<<1) +#define PF_VELOCITY1 (1<<2) +#define PF_VELOCITY2 (1<<3) +#define PF_VELOCITY3 (1<<4) +#define PF_MODEL (1<<5) +#define PF_SKINNUM (1<<6) +#define PF_EFFECTS (1<<7) +#define PF_WEAPONFRAME (1<<8) // only sent for view player +#define PF_DEAD (1<<9) // don't block movement any more +#define PF_GIB (1<<10) // offset the view height differently + +//flags on players in mvds +#define DF_ORIGIN 1 +#define DF_ANGLES (1<<3) +#define DF_EFFECTS (1<<6) +#define DF_SKINNUM (1<<7) +#define DF_DEAD (1<<8) +#define DF_GIB (1<<9) +#define DF_WEAPONFRAME (1<<10) +#define DF_MODEL (1<<11) + + + + diff --git a/fteqtv/qtv.h b/fteqtv/qtv.h index 492dd0178..de36f1d62 100644 --- a/fteqtv/qtv.h +++ b/fteqtv/qtv.h @@ -141,8 +141,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define strnicmp strncasecmp #endif +#define ustrlen(s) strlen((char*)(s)) +#define ustrcmp(s1,s2) strcmp((char*)(s1),(char*)(s2)) +#define ustrncmp(s1,s2,l) strncmp((char*)(s1),(char*)(s2),l) -//linux and other systems have strlcat / strlcpy +//some systems have strlcat / strlcpy //we support windows and can't use those #define Q_strncatz(dest, src, sizeofdest) \ do { \ @@ -161,54 +164,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define VERSION "0.01" //this will be added to the serverinfo +#define PROX_DEFAULTSERVERPORT 27500 #define PROX_DEFAULTLISTENPORT 27501 #define PROX_DEFAULTSERVER "localhost:27500" -#define MAX_SERVERINFO_STRING 1024 //standard quake has 512 here. -#define MAX_USERINFO 192 -#define MAX_CLIENTS 32 -#define MAX_LIST 256 -#define MAX_MODELS MAX_LIST -#define MAX_SOUNDS MAX_LIST -#define MAX_ENTITIES 512 -#define MAX_STATICSOUNDS 64 -#define MAX_STATICENTITIES 128 -#define MAX_LIGHTSTYLES 64 #define DEFAULT_HOSTNAME "FTEQTV" -#define MAX_PROXY_INBUFFER 4096 -#define MAX_PROXY_BUFFER (1<<14) //must be power-of-two -#define PREFERED_PROXY_BUFFER 4096 //the ammount of data we try to leave in our input buffer (must be large enough to contain any single mvd frame) - #define MAX_ENTITY_LEAFS 32 -#define ENTS_PER_FRAME 64 //max number of entities per frame (OUCH!). -#define ENTITY_FRAMES 64 //number of frames to remember for deltaing -#define Z_EXT_SERVERTIME (1<<3) // STAT_TIME -#define Z_EXT_STRING "8" - - -#define MAX_STATS 32 -#define STAT_HEALTH 0 -#define STAT_FRAGS 1 -#define STAT_WEAPON 2 -#define STAT_AMMO 3 -#define STAT_ARMOR 4 -#define STAT_WEAPONFRAME 5 -#define STAT_SHELLS 6 -#define STAT_NAILS 7 -#define STAT_ROCKETS 8 -#define STAT_CELLS 9 -#define STAT_ACTIVEWEAPON 10 -#define STAT_TOTALSECRETS 11 -#define STAT_TOTALMONSTERS 12 -#define STAT_SECRETS 13 // bumped on client side by svc_foundsecret -#define STAT_MONSTERS 14 // bumped by svc_killedmonster -#define STAT_ITEMS 15 - -#define STAT_TIME 17 //A ZQ hack, sending time via a stat. - //this allows l33t engines to interpolate properly without spamming at a silly high fps. +#include "protocol.h" @@ -222,31 +187,6 @@ extern "C" { typedef unsigned char netadr_t[64]; -#define NQ_PACKETS_PER_SECOND 20 -#define MAX_NQMSGLEN 8000 -#define MAX_MSGLEN 1400 -#define MAX_NQDATAGRAM 1024 -#define MAX_BACKBUF_SIZE 1000 //this is smaller so we don't loose entities when lagging - - -//NQ transport layer defines -#define NETFLAG_LENGTH_MASK 0x0000ffff -#define NETFLAG_DATA 0x00010000 -#define NETFLAG_ACK 0x00020000 -#define NETFLAG_NAK 0x00040000 -#define NETFLAG_EOM 0x00080000 -#define NETFLAG_UNRELIABLE 0x00100000 -#define NETFLAG_CTL 0x80000000 - -#define CCREQ_CONNECT 0x01 -#define CCREQ_SERVER_INFO 0x02 - -#define CCREP_ACCEPT 0x81 -#define CCREP_REJECT 0x82 -#define CCREP_SERVER_INFO 0x83 - -#define NET_GAMENAME_NQ "QUAKE" -#define NET_PROTOCOL_VERSION 3 #ifdef COMMENTARY typedef struct soundcapt_s { @@ -263,7 +203,7 @@ typedef struct { unsigned int readpos; unsigned int cursize; unsigned int maxsize; - char *data; + void *data; unsigned int startpos; qboolean overflowed; qboolean allowoverflow; @@ -456,6 +396,7 @@ typedef struct oproxy_s { qboolean drop; sv_t *defaultstream; + sv_t *stream; FILE *srcfile; //buffer is padded with data from this file when its empty FILE *file; //recording a demo (written to) @@ -508,26 +449,38 @@ typedef struct { float angle[3]; } intermission_t; +typedef enum { + SRC_BAD, + SRC_DEMO, + SRC_UDP, + SRC_TCP +} sourcetype_t; + struct sv_s { //details about a server connection (also known as stream) char connectpassword[64]; //password given to server netadr_t serveraddress; netchan_t netchan; qboolean serverquery; + sourcetype_t sourcetype; + //proxy chaining qboolean serverisproxy; qboolean proxyisselected; + qboolean upstreamacceptschat; + qboolean upstreamacceptsdownload; // unsigned char buffer[MAX_PROXY_BUFFER]; //this doesn't cycle. int buffersize; //it memmoves down - int forwardpoint; //the point in the stream that we're forwarded up to. + int forwardpoint; //the point in the stream that we've forwarded up to. qboolean parsingqtvheader; unsigned char upstreambuffer[2048]; int upstreambuffersize; unsigned int parsetime; + unsigned int parsespeed; char gamedir[MAX_QPATH]; char mapname[256]; @@ -561,7 +514,7 @@ struct sv_s { //details about a server connection (also known as stream) qboolean silentstream; - qboolean usequkeworldprotocols; + qboolean usequakeworldprotocols; int challenge; unsigned short qport; int isconnected; @@ -658,6 +611,7 @@ struct cluster_s { char qtvpassword[256]; //password required to connect a proxy char hostname[256]; char master[MAX_QPATH]; + char demodir[MAX_QPATH]; qboolean chokeonnotupdated; qboolean lateforward; qboolean notalking; @@ -676,7 +630,6 @@ struct cluster_s { oproxy_t *pendingproxies; - char demodir[128]; availdemo_t availdemos[2048]; int availdemoscount; }; @@ -722,170 +675,16 @@ unsigned int BigLong(unsigned int val); -#define clc_bad 0 -#define clc_nop 1 -#define clc_disconnect 2 //NQ only -#define clc_move 3 // [[usercmd_t] -#define clc_stringcmd 4 // [string] message -#define clc_delta 5 // [byte] sequence number, requests delta compression of message -#define clc_tmove 6 // teleport request, spectator only -#define clc_upload 7 // teleport request, spectator only - - - - - - -#define svc_bad 0 -#define svc_nop 1 -#define svc_disconnect 2 -#define svc_updatestat 3 // [qbyte] [qbyte] -//#define svc_version 4 // [long] server version -#define svc_nqsetview 5 // [short] entity number -#define svc_sound 6 // -#define svc_nqtime 7 // [float] server time -#define svc_print 8 // [qbyte] id [string] null terminated string -#define svc_stufftext 9 // [string] stuffed into client's console buffer - // the string should be \n terminated -#define svc_setangle 10 // [angle3] set the view angle to this absolute value - -#define svc_serverdata 11 // [long] protocol ... -#define svc_lightstyle 12 // [qbyte] [string] -#define svc_nqupdatename 13 // [qbyte] [string] -#define svc_updatefrags 14 // [qbyte] [short] -#define svc_nqclientdata 15 // -//#define svc_stopsound 16 // -#define svc_nqupdatecolors 17 // [qbyte] [qbyte] [qbyte] -#define svc_particle 18 // [vec3] -#define svc_damage 19 - -#define svc_spawnstatic 20 -//#define svc_spawnstatic2 21 -#define svc_spawnbaseline 22 - -#define svc_temp_entity 23 // variable -#define svc_setpause 24 // [qbyte] on / off -#define svc_nqsignonnum 25 // [qbyte] used for the signon sequence - -#define svc_centerprint 26 // [string] to put in center of the screen - -#define svc_killedmonster 27 -#define svc_foundsecret 28 - -#define svc_spawnstaticsound 29 // [coord3] [qbyte] samp [qbyte] vol [qbyte] aten - -#define svc_intermission 30 // [vec3_t] origin [vec3_t] angle -//#define svc_finale 31 // [string] text - -#define svc_cdtrack 32 // [qbyte] track -//#define svc_sellscreen 33 - -//#define svc_cutscene 34 //hmm... nq only... added after qw tree splitt? - -#define svc_smallkick 34 // set client punchangle to 2 -#define svc_bigkick 35 // set client punchangle to 4 - -#define svc_updateping 36 // [qbyte] [short] -#define svc_updateentertime 37 // [qbyte] [float] - -#define svc_updatestatlong 38 // [qbyte] [long] - -#define svc_muzzleflash 39 // [short] entity - -#define svc_updateuserinfo 40 // [qbyte] slot [long] uid - // [string] userinfo - -#define svc_download 41 // [short] size [size bytes] -#define svc_playerinfo 42 // variable -#define svc_nails 43 // [qbyte] num [48 bits] xyzpy 12 12 12 4 8 -#define svc_chokecount 44 // [qbyte] packets choked -#define svc_modellist 45 // [strings] -#define svc_soundlist 46 // [strings] -#define svc_packetentities 47 // [...] -#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_serverinfo 52 // serverinfo -#define svc_updatepl 53 // [qbyte] [qbyte] -#define svc_nails2 54 //qwe - [qbyte] num [52 bits] nxyzpy 8 12 12 12 4 8 - - - - - -#define dem_audio 0 - -#define dem_cmd 0 -#define dem_read 1 -#define dem_set 2 -#define dem_multiple 3 -#define dem_single 4 -#define dem_stats 5 -#define dem_all 6 - -#define dem_mask 7 - - -#define PROTOCOL_VERSION_NQ 15 -#define PROTOCOL_VERSION 28 - - -//flags on entities -#define U_ORIGIN1 (1<<9) -#define U_ORIGIN2 (1<<10) -#define U_ORIGIN3 (1<<11) -#define U_ANGLE2 (1<<12) -#define U_FRAME (1<<13) -#define U_REMOVE (1<<14) // REMOVE this entity, don't add it -#define U_MOREBITS (1<<15) - -// if MOREBITS is set, these additional flags are read in next -#define U_ANGLE1 (1<<0) -#define U_ANGLE3 (1<<1) -#define U_MODEL (1<<2) -#define U_COLORMAP (1<<3) -#define U_SKIN (1<<4) -#define U_EFFECTS (1<<5) -#define U_SOLID (1<<6) // the entity should be solid for prediction - - - - - -//flags on players -#define PF_MSEC (1<<0) -#define PF_COMMAND (1<<1) -#define PF_VELOCITY1 (1<<2) -#define PF_VELOCITY2 (1<<3) -#define PF_VELOCITY3 (1<<4) -#define PF_MODEL (1<<5) -#define PF_SKINNUM (1<<6) -#define PF_EFFECTS (1<<7) -#define PF_WEAPONFRAME (1<<8) // only sent for view player -#define PF_DEAD (1<<9) // don't block movement any more -#define PF_GIB (1<<10) // offset the view height differently - -//flags on players in mvds -#define DF_ORIGIN 1 -#define DF_ANGLES (1<<3) -#define DF_EFFECTS (1<<6) -#define DF_SKINNUM (1<<7) -#define DF_DEAD (1<<8) -#define DF_GIB (1<<9) -#define DF_WEAPONFRAME (1<<10) -#define DF_MODEL (1<<11) - //flags for where a message can be sent, for easy broadcasting #define Q1 (NQ|QW) #define QW 1 #define NQ 2 #define CONNECTING 4 +#include "cmd.h" - -void InitNetMsg(netmsg_t *b, char *buffer, int bufferlength); +void InitNetMsg(netmsg_t *b, void *buffer, int bufferlength); unsigned char ReadByte(netmsg_t *b); unsigned short ReadShort(netmsg_t *b); unsigned int ReadLong(netmsg_t *b); @@ -897,11 +696,11 @@ void WriteLong(netmsg_t *b, unsigned int l); void WriteFloat(netmsg_t *b, float f); void WriteString2(netmsg_t *b, const char *str); void WriteString(netmsg_t *b, const char *str); -void WriteData(netmsg_t *b, const char *data, int length); +void WriteData(netmsg_t *b, const void *data, int length); -void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playermask,int suitablefor); -void Broadcast(cluster_t *cluster, char *buffer, int length, int suitablefor); -void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask); +void Multicast(sv_t *tv, void *buffer, int length, int to, unsigned int playermask,int suitablefor); +void Broadcast(cluster_t *cluster, void *buffer, int length, int suitablefor); +void ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask); void BuildServerData(sv_t *tv, netmsg_t *msg, int servercount, viewer_t *spectatorflag); void BuildNQServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount); SOCKET QW_InitUDPSocket(int port); @@ -911,22 +710,26 @@ void Prox_SendInitialEnts(sv_t *qtv, oproxy_t *prox, netmsg_t *msg); qboolean QTV_Connect(sv_t *qtv, char *serverurl); void QTV_Shutdown(sv_t *qtv); qboolean NET_StringToAddr (char *s, netadr_t *sadr, int defaultport); +void QTV_Printf(sv_t *qtv, char *format, ...); void SendBufferToViewer(viewer_t *v, const char *buffer, int length, qboolean reliable); void QW_PrintfToViewer(viewer_t *v, char *format, ...); void QW_StuffcmdToViewer(viewer_t *v, char *format, ...); +void QW_StreamPrint(cluster_t *cluster, sv_t *server, viewer_t *allbut, char *message); +void QW_StreamStuffcmd(cluster_t *cluster, sv_t *server, char *fmt, ...); +void QTV_SayCommand(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *fullcommand); //execute a command from a view void PM_PlayerMove (pmove_t *pmove); void Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport, qboolean isclient); void Netchan_OutOfBandPrint (cluster_t *cluster, SOCKET sock, netadr_t adr, char *format, ...); int Netchan_IsLocal (netadr_t adr); -void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, char *data, netadr_t adr); +void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, void *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); qboolean NQNetchan_Process(cluster_t *cluster, netchan_t *chan, netmsg_t *msg); -void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const unsigned char *data); -void Netchan_OutOfBand (cluster_t *cluster, SOCKET sock, netadr_t adr, int length, unsigned char *data); +void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const void *data); +void Netchan_OutOfBand (cluster_t *cluster, SOCKET sock, netadr_t adr, int length, void *data); qboolean Netchan_CanPacket (netchan_t *chan); int SendList(sv_t *qtv, int first, const filename_t *list, int svc, netmsg_t *msg); @@ -942,7 +745,7 @@ void BSP_SetupForPosition(bsp_t *bsp, float x, float y, float z); const intermission_t *BSP_IntermissionSpot(bsp_t *bsp); void QW_SetViewersServer(cluster_t *cluster, viewer_t *viewer, sv_t *sv); -unsigned short QCRC_Block (unsigned char *start, int count); +unsigned short QCRC_Block (void *start, int count); unsigned short QCRC_Value(unsigned short crcvalue); void WriteDeltaUsercmd (netmsg_t *m, const usercmd_t *from, usercmd_t *move); void SendClientCommand(sv_t *qtv, char *fmt, ...); @@ -950,7 +753,6 @@ void QTV_Run(sv_t *qtv); void QW_FreeViewer(cluster_t *cluster, viewer_t *viewer); void QW_SetMenu(viewer_t *v, int menunum); -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); @@ -961,7 +763,7 @@ void Cluster_BuildAvailableDemoList(cluster_t *cluster); void Sys_Printf(cluster_t *cluster, char *fmt, ...); -void Net_ProxySend(cluster_t *cluster, oproxy_t *prox, char *buffer, int length); +void Net_ProxySend(cluster_t *cluster, oproxy_t *prox, void *buffer, int length); oproxy_t *Net_FileProxy(sv_t *qtv, char *filename); sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, char *password, qboolean force, qboolean autoclose, qboolean noduplicates, qboolean query); SOCKET Net_MVDListen(int port); @@ -970,7 +772,7 @@ qboolean Net_StopFileProxy(sv_t *qtv); void SV_FindProxies(SOCKET sock, cluster_t *cluster, sv_t *defaultqtv); qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend); -void SV_ForwardStream(sv_t *qtv, char *buffer, int length); +void SV_ForwardStream(sv_t *qtv, void *buffer, int length); unsigned char *FS_ReadFile(char *gamedir, char *filename, unsigned int *size); diff --git a/fteqtv/qw.c b/fteqtv/qw.c index 9113adf87..3a790a4bd 100644 --- a/fteqtv/qw.c +++ b/fteqtv/qw.c @@ -818,20 +818,20 @@ void NewNQClient(cluster_t *cluster, netadr_t *addr) if (cluster->numviewers >= cluster->maxviewers && cluster->maxviewers) { buffer[4] = CCREP_REJECT; - strcpy(buffer+5, "Sorry, proxy is full.\n"); - len = strlen(buffer+5)+5; + strcpy((char*)buffer+5, "Sorry, proxy is full.\n"); + len = strlen((char*)buffer+5)+5; } /* else { buffer[4] = CCREP_REJECT; - strcpy(buffer+5, "NQ not supported yet\n"); - len = strlen(buffer+5)+5; + strcpy((char*)buffer+5, "NQ not supported yet\n"); + len = strlen((char*)buffer+5)+5; }*/ else if (!(viewer = malloc(sizeof(viewer_t)))) { buffer[4] = CCREP_REJECT; - strcpy(buffer+5, "Out of memory\n"); - len = strlen(buffer+5)+5; + strcpy((char*)buffer+5, "Out of memory\n"); + len = strlen((char*)buffer+5)+5; } else { @@ -1124,7 +1124,7 @@ void QTV_Status(cluster_t *cluster, netadr_t *from) { sprintf(elem, "\\%i\\", sv->streamid); WriteString2(&msg, elem); - WriteString2(&msg, sv->serveraddress); + WriteString2(&msg, (char*)sv->serveraddress); sprintf(elem, " (%s)", sv->serveraddress); WriteString2(&msg, elem); } @@ -1885,7 +1885,6 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) short interp; float lerp; int track; - int runaway = 10; int snapdist = 128; //in quake units @@ -1930,7 +1929,7 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) if (!v->commentator && track >= 0 && !v->backbuffered) { - if (v->trackplayer != tv->trackplayer && tv->usequkeworldprotocols) + if (v->trackplayer != tv->trackplayer && tv->usequakeworldprotocols) if (!tv->players[v->trackplayer].active && tv->players[tv->trackplayer].active) { QW_StuffcmdToViewer (v, "track %i\n", tv->trackplayer); @@ -2056,7 +2055,7 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) int newnum, oldnum; frame_t *frompacket, *topacket; topacket = &tv->frame[tv->netchan.incoming_sequence&(ENTITY_FRAMES-1)]; - if (tv->usequkeworldprotocols) + if (tv->usequakeworldprotocols) { frompacket = &tv->frame[(topacket->oldframe)&(ENTITY_FRAMES-1)]; } @@ -2338,7 +2337,7 @@ void QTV_SayCommand(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *fullcomman unsigned char *ip; gethostname(buf, sizeof(buf)); //ask the operating system for the local dns name NET_StringToAddr(buf, &addr, 0); //look that up - ip = (char*)&((struct sockaddr_in *)&addr)->sin_addr; + ip = (unsigned char*)&((struct sockaddr_in *)&addr)->sin_addr; QW_PrintfToViewer(v, "[QuakeTV] %s | %i.%i.%i.%i\n", cluster->hostname, ip[0], ip[1], ip[2], ip[3]); } else if (!strcmp(command, "menu")) @@ -2678,7 +2677,7 @@ tuiadmin: if (!strcmp(command, "join") || !strcmp(command, "connect")) isjoin = true; - + snprintf(buf, sizeof(buf), "udp:%s", args); qtv = QTV_NewServerConnection(cluster, buf, "", false, true, !isjoin, false); if (qtv) @@ -2689,6 +2688,8 @@ tuiadmin: qtv->controller = v; QW_PrintfToViewer(v, "Connected to %s\n", qtv->server); } + else if (cluster->nouserconnects) + QW_PrintfToViewer(v, "you may not do that here\n"); else QW_PrintfToViewer(v, "Failed to connect to server \"%s\", connection aborted\n", buf); } @@ -2704,6 +2705,8 @@ tuiadmin: QW_SetViewersServer(cluster, v, qtv); QW_PrintfToViewer(v, "Connected to %s\n", qtv->server); } + else if (cluster->nouserconnects) + QW_PrintfToViewer(v, "Ask an admin to connect first\n"); else QW_PrintfToViewer(v, "Failed to connect to server \"%s\", connection aborted\n", buf); } @@ -2741,7 +2744,7 @@ tuiadmin: QW_PrintfToViewer(v, "Streaming from %s\n", qtv->server); } else - QW_PrintfToViewer(v, "Demo is not exist on proxy\n", buf); + QW_PrintfToViewer(v, "Demo does not exist on proxy\n", buf); } else if (!strcmp(command, "disconnect")) { @@ -3039,7 +3042,7 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean message++; *v->expectcommand = '\0'; - if (qtv && qtv->usequkeworldprotocols && !noupwards) + if (qtv && qtv->usequakeworldprotocols && !noupwards) { if (qtv->controller == v || !*v->name) { @@ -3063,6 +3066,8 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean if (cluster->notalking) return; + SV_SayToUpstream(v->server, message); + for (ov = cluster->viewers; ov; ov = ov->next) { if (ov->server != v->server) @@ -3227,18 +3232,14 @@ void ParseNQC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) else if (!strncmp(buf, "setinfo", 5)) { #define TOKENIZE_PUNCTUATION "" - #define MAX_ARGS 3 - #define ARG_LEN 256 int i; - char arg[MAX_ARGS][ARG_LEN]; - char *argptrs[MAX_ARGS]; + char arg[3][ARG_LEN]; char *command = buf; for (i = 0; i < MAX_ARGS; i++) { command = COM_ParseToken(command, arg[i], ARG_LEN, TOKENIZE_PUNCTUATION); - argptrs[i] = arg[i]; } Info_SetValueForStarKey(v->userinfo, arg[1], arg[2], sizeof(v->userinfo)); @@ -3672,18 +3673,14 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) else if (!strncmp(buf, "setinfo", 5)) { #define TOKENIZE_PUNCTUATION "" - #define MAX_ARGS 3 - #define ARG_LEN 256 int i; - char arg[MAX_ARGS][ARG_LEN]; - char *argptrs[MAX_ARGS]; + char arg[3][ARG_LEN]; char *command = buf; - for (i = 0; i < MAX_ARGS; i++) + for (i = 0; i < 3; i++) { command = COM_ParseToken(command, arg[i], ARG_LEN, TOKENIZE_PUNCTUATION); - argptrs[i] = arg[i]; } Info_SetValueForStarKey(v->userinfo, arg[1], arg[2], sizeof(v->userinfo)); @@ -4048,7 +4045,6 @@ void Menu_Draw(cluster_t *cluster, viewer_t *viewer) case MENU_MAIN: { - int o = 8; WriteString2(&m, "\n\x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f\n"); while (viewer->menuop < 0) viewer->menuop += MENU_MAIN_ITEMCOUNT; @@ -4406,7 +4402,9 @@ void SendViewerPackets(cluster_t *cluster, viewer_t *v) v->maysend = (v->nextpacket < cluster->curtime); } if (!Netchan_CanPacket(&v->netchan)) + { return; + } if (v->maysend) //don't send incompleate connection data. { // printf("maysend (%i, %i)\n", cluster->curtime, v->nextpacket); @@ -4438,6 +4436,7 @@ void SendViewerPackets(cluster_t *cluster, viewer_t *v) WriteByte(&m, svc_centerprint); WriteString(&m, v->server->status); } + //printf("in %i, out %i, ", v->netchan.incoming_sequence, v->netchan.outgoing_sequence); //if (v->netchan.incoming_sequence != v->netchan.outgoing_sequence) //printf("%s: in %i, out %i\n", v->name, v->netchan.incoming_sequence, v->netchan.outgoing_sequence); @@ -4496,7 +4495,7 @@ void QW_UpdateUDPStuff(cluster_t *cluster) for (;;) { - read = recvfrom(cluster->qwdsocket, buffer, sizeof(buffer), 0, (struct sockaddr*)from, &fromsize); + read = recvfrom(cluster->qwdsocket, buffer, sizeof(buffer), 0, (struct sockaddr*)from, (unsigned*)&fromsize); if (read <= 5) //otherwise it's a runt or bad. { diff --git a/fteqtv/rcon.c b/fteqtv/rcon.c index 8160af755..7584bc002 100644 --- a/fteqtv/rcon.c +++ b/fteqtv/rcon.c @@ -20,6 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "qtv.h" + #define MAX_INFO_KEY 64 //I apologise for this if it breaks your formatting or anything @@ -221,7 +222,13 @@ void Info_SetValueForStarKey (char *s, const char *key, const char *value, int m } - +void qstrncpyz(char *out, char *in, int length) +{ + if (length < 1) + return; + strncpy(out, in, length-1); + out[length-1] = 0; +} @@ -321,189 +328,214 @@ skipwhite: - -#define MAX_ARGS 8 -#define ARG_LEN 512 -typedef char *(*dispatchrconcommand_t)(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand); - -char *Cmd_Hostname(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_Printf(cmdctxt_t *ctx, char *fmt, ...) { - if (!*arg[1]) - { - buffer[0] = 0; - if (*cluster->hostname) - snprintf(buffer, sizeofbuffer, "Current hostname is %s\n", cluster->hostname); - else - return "No master server is currently set.\n"; - return buffer; - } - strncpy(cluster->hostname, arg[1], sizeof(cluster->hostname)-1); + va_list argptr; + char string[2048]; + + va_start (argptr, fmt); + vsnprintf (string, sizeof(string)-1, fmt,argptr); + string[sizeof(string)-1] = 0; + va_end (argptr); - snprintf(buffer, sizeofbuffer, "hostname set to \"%s\"\n", cluster->hostname); - return buffer; + if (ctx->printfunc) + ctx->printfunc(ctx, string); + else if (ctx->qtv) + QTV_Printf(ctx->qtv, "%s", string); + else + Sys_Printf(ctx->cluster, "%s", string); } -char *Cmd_Master(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) + +void Cmd_Hostname(cmdctxt_t *ctx) { + if (Cmd_Argc(ctx) < 2) + { + if (*ctx->cluster->hostname) + Cmd_Printf(ctx, "Current hostname is %s\n", ctx->cluster->hostname); + else + Cmd_Printf(ctx, "No master server is currently set.\n"); + } + else + { + qstrncpyz(ctx->cluster->hostname, Cmd_Argv(ctx, 1), sizeof(ctx->cluster->hostname)); + + Cmd_Printf(ctx, "hostname set to \"%s\"\n", ctx->cluster->hostname); + } +} + +void Cmd_Master(cmdctxt_t *ctx) +{ + char *newval = Cmd_Argv(ctx, 1); netadr_t addr; - if (!*arg[1]) + if (Cmd_Argc(ctx) < 2) { - if (*cluster->master) - return "Subscribed to a master server (use '-' to clear)\n"; + if (*ctx->cluster->master) + Cmd_Printf(ctx, "Subscribed to a master server (use '-' to clear)\n"); else - return "No master server is currently set.\n"; + Cmd_Printf(ctx, "No master server is currently set.\n"); + return; } - if (!strcmp(arg[1], "-")) + if (!strcmp(newval, "-")) { - strncpy(cluster->master, "", sizeof(cluster->master)-1); - return "Master server cleared\n"; + qstrncpyz(ctx->cluster->master, "", sizeof(ctx->cluster->master)); + Cmd_Printf(ctx, "Master server cleared\n"); + return; } - if (!NET_StringToAddr(arg[1], &addr, 27000)) //send a ping like a qw server does. this is kinda pointless of course. + if (!NET_StringToAddr(newval, &addr, 27000)) //send a ping like a qw server does. this is kinda pointless of course. { - return "Couldn't resolve address\n"; + Cmd_Printf(ctx, "Couldn't resolve address\n"); + return; } - strncpy(cluster->master, arg[1], sizeof(cluster->master)-1); - cluster->mastersendtime = cluster->curtime; + qstrncpyz(ctx->cluster->master, newval, sizeof(ctx->cluster->master)); + ctx->cluster->mastersendtime = ctx->cluster->curtime; - if (cluster->qwdsocket != INVALID_SOCKET) - NET_SendPacket (cluster, cluster->qwdsocket, 1, "k", addr); - return "Master server set.\n"; + if (ctx->cluster->qwdsocket != INVALID_SOCKET) + NET_SendPacket (ctx->cluster, ctx->cluster->qwdsocket, 1, "k", addr); + Cmd_Printf(ctx, "Master server set.\n"); } - - -char *Cmd_UDPPort(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_UDPPort(cmdctxt_t *ctx) { int news; - int newp = atoi(arg[1]); + int newp = atoi(Cmd_Argv(ctx, 1)); news = QW_InitUDPSocket(newp); if (news != INVALID_SOCKET) { - cluster->mastersendtime = cluster->curtime; - closesocket(cluster->qwdsocket); - cluster->qwdsocket = news; - cluster->qwlistenportnum = newp; + ctx->cluster->mastersendtime = ctx->cluster->curtime; + closesocket(ctx->cluster->qwdsocket); + ctx->cluster->qwdsocket = news; + ctx->cluster->qwlistenportnum = newp; - snprintf(buffer, sizeofbuffer, "Opened udp port %i (all connected qw clients will time out)\n", newp); + Cmd_Printf(ctx, "Opened udp port %i (all connected qw clients will time out)\n", newp); } else - snprintf(buffer, sizeofbuffer, "Failed to open udp port %i\n", newp); - return buffer; + Cmd_Printf(ctx, "Failed to open udp port %i\n", newp); } -char *Cmd_AdminPassword(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_AdminPassword(cmdctxt_t *ctx) { - if (!localcommand) - return "Rejecting remote password change.\n"; - - if (!*arg[1]) + if (!Cmd_IsLocal(ctx)) { - if (*cluster->adminpassword) - return "An admin password is currently set\n"; - else - return "No admin passsword is currently set\n"; + Cmd_Printf(ctx, "Rejecting remote password change.\n"); + return; } - strncpy(cluster->adminpassword, arg[1], sizeof(cluster->adminpassword)-1); - return "Password changed.\n"; + if (Cmd_Argc(ctx) < 2) + { + if (*ctx->cluster->adminpassword) + Cmd_Printf(ctx, "An admin password is currently set\n"); + else + Cmd_Printf(ctx, "No admin passsword is currently set\n"); + } + else + { + qstrncpyz(ctx->cluster->adminpassword, Cmd_Argv(ctx, 1), sizeof(ctx->cluster->adminpassword)); + Cmd_Printf(ctx, "Password changed.\n"); + } } -char *Cmd_QTVList(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_GenericQuery(cmdctxt_t *ctx, int dataset) { - if (!*arg[1]) - return "connect requires an ip:port parameter\n"; + char *method = "tcp:"; + char *address, *password; + if (Cmd_Argc(ctx) < 2) + { + Cmd_Printf(ctx, "%s requires an ip:port parameter\n", Cmd_Argv(ctx, 0)); + return; + } - memmove(arg[1]+4, arg[1], ARG_LEN-5); - strncpy(arg[1], "tcp:", 4); + address = Cmd_Argv(ctx, 1); + password = Cmd_Argv(ctx, 2); + //this is evil + memmove(address+strlen(method), address, ARG_LEN-(1+strlen(method))); + strncpy(address, method, strlen(method)); - qtv = QTV_NewServerConnection(cluster, arg[1], arg[2], false, false, false, true); - if (!qtv) - return "Failed to connect to server, connection aborted\n"; - return "Querying proxy\n"; + if (!QTV_NewServerConnection(ctx->cluster, address, password, false, false, false, dataset)) + Cmd_Printf(ctx, "Failed to connect to \"%s\", connection aborted\n", address); + + Cmd_Printf(ctx, "Querying \"%s\"\n", address); } -char *Cmd_QTVDemoList(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) + + +void Cmd_QTVList(cmdctxt_t *ctx) { - 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); - - qtv = QTV_NewServerConnection(cluster, arg[1], arg[2], false, false, false, 2); - if (!qtv) - return "Failed to connect to server, connection aborted\n"; - return "Querying proxy\n"; + Cmd_GenericQuery(ctx, 1); } -char *Cmd_QTVConnect(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_QTVDemoList(cmdctxt_t *ctx) { - 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], arg[2], false, false, false, false)) - return "Failed to connect to server, connection aborted\n"; - - snprintf(buffer, sizeofbuffer, "Source registered \"%s\"\n", arg[1]); - return buffer; + Cmd_GenericQuery(ctx, 2); } -char *Cmd_QWConnect(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) + +void Cmd_GenericConnect(cmdctxt_t *ctx, char *method) { - if (!*arg[1]) - return "connect requires an ip:port parameter\n"; + char *address, *password; + if (Cmd_Argc(ctx) < 2) + { + if (!strncmp(method, "file", 4)) + Cmd_Printf(ctx, "%s requires a demo name parameter\n", Cmd_Argv(ctx, 0)); + else + Cmd_Printf(ctx, "%s requires an ip:port parameter\n", Cmd_Argv(ctx, 0)); + return; + } - memmove(arg[1]+4, arg[1], ARG_LEN-5); - strncpy(arg[1], "udp:", 4); + address = Cmd_Argv(ctx, 1); + password = Cmd_Argv(ctx, 2); + //this is evil + memmove(address+strlen(method), address, ARG_LEN-(1+strlen(method))); + strncpy(address, method, strlen(method)); - if (!QTV_NewServerConnection(cluster, arg[1], arg[2], false, false, false, false)) - return "Failed to connect to server, connection aborted\n"; + if (!QTV_NewServerConnection(ctx->cluster, address, password, false, false, false, false)) + Cmd_Printf(ctx, "Failed to connect to \"%s\", connection aborted\n", address); - snprintf(buffer, sizeofbuffer, "Source registered \"%s\"\n", arg[1]); - return buffer; + Cmd_Printf(ctx, "Source registered \"%s\"\n", address); } -char *Cmd_MVDConnect(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) + +void Cmd_QTVConnect(cmdctxt_t *ctx) { - 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], arg[2], false, false, false, false)) - return "Failed to connect to server, connection aborted\n"; - - snprintf(buffer, sizeofbuffer, "Source registered \"%s\"\n", arg[1]); - return buffer; + Cmd_GenericConnect(ctx, "tcp:"); } -char *Cmd_Exec(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_QWConnect(cmdctxt_t *ctx) +{ + Cmd_GenericConnect(ctx, "udp:"); +} +void Cmd_MVDConnect(cmdctxt_t *ctx) +{ + Cmd_GenericConnect(ctx, "file:"); +} + +void Cmd_Exec(cmdctxt_t *ctx) { FILE *f; - char line[512], *res, *l; + char line[512], *l; + char *fname = Cmd_Argv(ctx, 1); - if (!localcommand) + if (!Cmd_IsLocal(ctx)) { - if (*arg[1] == '\\' || *arg[1] == '/' || strstr(arg[1], "..") || arg[1][1] == ':') - return "Absolute paths are prohibited.\n"; - if (!strncmp(arg[1], "usercfg/", 8)) //this is how we stop users from execing a 50gb pk3.. - return "Remote-execed configs must be in the usercfg directory\n"; + if (*fname == '\\' || *fname == '/' || strstr(fname, "..") || fname[1] == ':') + { + Cmd_Printf(ctx, "Absolute paths are prohibited.\n"); + return; + } + if (!strncmp(fname, "usercfg/", 8)) //this is how we stop users from execing a 50gb pk3.. + { + Cmd_Printf(ctx, "Remote-execed configs must be in the usercfg directory\n"); + return; + } } - f = fopen(arg[1], "rt"); + f = fopen(fname, "rt"); if (!f) { - snprintf(buffer, sizeofbuffer, "Couldn't exec \"%s\"\n", arg[1]); - return buffer; + Cmd_Printf(ctx, "Couldn't exec \"%s\"\n", fname); + return; } else { + Cmd_Printf(ctx, "Execing \"%s\"\n", fname); while(fgets(line, sizeof(line)-1, f)) { l = line; @@ -511,14 +543,10 @@ char *Cmd_Exec(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, l++; if (*l && l[0] != '/' && l[1] != '/') { - res = Rcon_Command(cluster, qtv, l, buffer, sizeofbuffer, localcommand); - Sys_Printf(cluster, "%s", res); //this is perhaps wrong. + Cmd_ExecuteNow(ctx, l); } } fclose(f); - - snprintf(buffer, sizeofbuffer, "Execed \"%s\"\n", arg[1]); - return buffer; } } @@ -534,219 +562,249 @@ void catbuffer(char *buffer, int bufsize, char *format, ...) Q_strncatz(buffer, string, bufsize); } -char *Cmd_Say(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_Say(cmdctxt_t *ctx) { int i; viewer_t *v; char message[8192]; message[0] = '\0'; - for (i = 1; i < MAX_ARGS; i++) - catbuffer(message, sizeof(message)-1, "%s%s", i==1?"":" ", arg[i]); - for (v = cluster->viewers; v; v = v->next) + for (i = 1; i < Cmd_Argc(ctx); i++) + catbuffer(message, sizeof(message)-1, "%s%s", i==1?"":" ", Cmd_Argv(ctx, i)); + + if (ctx->qtv) { - if (v->server == qtv || !qtv) + if (!SV_SayToUpstream(ctx->qtv, message)) + SV_SayToViewers(ctx->qtv, message); + } + else + { + //we don't have to remember the client proxies here... no streams = no active client proxies + for (v = ctx->cluster->viewers; v; v = v->next) + { QW_PrintfToViewer(v, "proxy: %s\n", message); + } } - buffer[0] = '\0'; - catbuffer(buffer, sizeofbuffer, "proxy: %s\n", message); - return buffer; + Cmd_Printf(ctx, "proxy: %s\n", message); } -char *Cmd_Status(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_Status(cmdctxt_t *ctx) { + Cmd_Printf(ctx, "%i sources\n", ctx->cluster->numservers); + Cmd_Printf(ctx, "%i viewers\n", ctx->cluster->numviewers); + Cmd_Printf(ctx, "%i proxies\n", ctx->cluster->numproxies); - buffer[0] = '\0'; - - catbuffer(buffer, sizeofbuffer, "%i sources\n", cluster->numservers); - catbuffer(buffer, sizeofbuffer, "%i viewers\n", cluster->numviewers); - catbuffer(buffer, sizeofbuffer, "%i proxies\n", cluster->numproxies); - - catbuffer(buffer, sizeofbuffer, "Options:\n"); - catbuffer(buffer, sizeofbuffer, " Hostname %s\n", cluster->hostname); + Cmd_Printf(ctx, "Options:\n"); + Cmd_Printf(ctx, " Hostname %s\n", ctx->cluster->hostname); - if (cluster->chokeonnotupdated) - catbuffer(buffer, sizeofbuffer, " Choke\n"); - if (cluster->lateforward) - catbuffer(buffer, sizeofbuffer, " Late forwarding\n"); - if (!cluster->notalking) - catbuffer(buffer, sizeofbuffer, " Talking allowed\n"); - if (cluster->nobsp) - catbuffer(buffer, sizeofbuffer, " No BSP loading\n"); - if (cluster->tcpsocket != INVALID_SOCKET) + if (ctx->cluster->chokeonnotupdated) + Cmd_Printf(ctx, " Choke\n"); + if (ctx->cluster->lateforward) + Cmd_Printf(ctx, " Late forwarding\n"); + if (!ctx->cluster->notalking) + Cmd_Printf(ctx, " Talking allowed\n"); + if (ctx->cluster->nobsp) + Cmd_Printf(ctx, " No BSP loading\n"); + if (ctx->cluster->tcpsocket != INVALID_SOCKET) { - catbuffer(buffer, sizeofbuffer, " tcp port %i\n", cluster->tcplistenportnum); + Cmd_Printf(ctx, " tcp port %i\n", ctx->cluster->tcplistenportnum); } - if (cluster->tcpsocket != INVALID_SOCKET) + if (ctx->cluster->tcpsocket != INVALID_SOCKET) { - catbuffer(buffer, sizeofbuffer, " udp port %i\n", cluster->qwlistenportnum); + Cmd_Printf(ctx, " udp port %i\n", ctx->cluster->qwlistenportnum); } - catbuffer(buffer, sizeofbuffer, " user connections are %sallowed\n", cluster->nouserconnects?"":"NOT "); - catbuffer(buffer, sizeofbuffer, "\n"); + Cmd_Printf(ctx, " user connections are %sallowed\n", ctx->cluster->nouserconnects?"NOT ":""); + Cmd_Printf(ctx, "\n"); - if (qtv) + if (ctx->qtv) { - catbuffer(buffer, sizeofbuffer, "Selected server: %s\n", qtv->server); - if (qtv->sourcefile) - catbuffer(buffer, sizeofbuffer, "Playing from file\n"); - if (qtv->sourcesock != INVALID_SOCKET) - catbuffer(buffer, sizeofbuffer, "Connected\n"); - if (qtv->parsingqtvheader || qtv->parsingconnectiondata) - catbuffer(buffer, sizeofbuffer, "Waiting for gamestate\n"); - if (qtv->usequkeworldprotocols) + Cmd_Printf(ctx, "Selected server: %s\n", ctx->qtv->server); + if (ctx->qtv->sourcefile) + Cmd_Printf(ctx, "Playing from file\n"); + if (ctx->qtv->sourcesock != INVALID_SOCKET) + Cmd_Printf(ctx, "Connected\n"); + if (ctx->qtv->parsingqtvheader || ctx->qtv->parsingconnectiondata) + Cmd_Printf(ctx, "Waiting for gamestate\n"); + if (ctx->qtv->usequakeworldprotocols) { - catbuffer(buffer, sizeofbuffer, "QuakeWorld protocols\n"); - if (qtv->controller) + Cmd_Printf(ctx, "QuakeWorld protocols\n"); + if (ctx->qtv->controller) { - catbuffer(buffer, sizeofbuffer, "Controlled by %s\n", qtv->controller->name); + Cmd_Printf(ctx, "Controlled by %s\n", ctx->qtv->controller->name); } } - else if (qtv->sourcesock == INVALID_SOCKET && !qtv->sourcefile) - catbuffer(buffer, sizeofbuffer, "Connection not established\n"); + else if (ctx->qtv->sourcesock == INVALID_SOCKET && !ctx->qtv->sourcefile) + Cmd_Printf(ctx, "Connection not established\n"); - if (*qtv->modellist[1].name) + if (*ctx->qtv->modellist[1].name) { - catbuffer(buffer, sizeofbuffer, "Map name %s\n", qtv->modellist[1].name); + Cmd_Printf(ctx, "Map name %s\n", ctx->qtv->modellist[1].name); } - if (*qtv->connectpassword) - catbuffer(buffer, sizeofbuffer, "Using a password\n"); + if (*ctx->qtv->connectpassword) + Cmd_Printf(ctx, "Using a password\n"); - if (qtv->disconnectwhennooneiswatching) - catbuffer(buffer, sizeofbuffer, "Stream is temporary\n"); + if (ctx->qtv->disconnectwhennooneiswatching) + Cmd_Printf(ctx, "Stream is temporary\n"); -/* if (qtv->tcpsocket != INVALID_SOCKET) +/* if (ctx->qtv->tcpsocket != INVALID_SOCKET) { - catbuffer(buffer, sizeofbuffer, "Listening for proxies (%i)\n", qtv->tcplistenportnum); + Cmd_Printf(ctx, "Listening for proxies (%i)\n", ctx->qtv->tcplistenportnum); } */ - if (qtv->bsp) + if (ctx->qtv->bsp) { - catbuffer(buffer, sizeofbuffer, "BSP (%s) is loaded\n", qtv->mapname); + Cmd_Printf(ctx, "BSP (%s) is loaded\n", ctx->qtv->mapname); } } - return buffer; -} -char *Cmd_Choke(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) -{ - if (!*arg[1]) - { - if (cluster->chokeonnotupdated) - return "proxy will not interpolate packets\n"; - else - return "proxy will smooth action at the expense of extra packets\n"; - } - cluster->chokeonnotupdated = !!atoi(arg[1]); - return "choke-until-update set\n"; -} -char *Cmd_Late(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) -{ - if (!*arg[1]) - { - if (cluster->lateforward) - return "forwarded streams will be artificially delayed\n"; - else - return "forwarded streams are forwarded immediatly\n"; - } - cluster->lateforward = !!atoi(arg[1]); - return "late forwarding set\n"; -} -char *Cmd_Talking(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) -{ - if (!*arg[1]) - { - if (cluster->notalking) - return "viewers may not talk\n"; - else - return "viewers may talk freely\n"; - } - cluster->notalking = !atoi(arg[1]); - return "talking permissions set\n"; -} -char *Cmd_NoBSP(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) -{ - if (!*arg[1]) - { - if (cluster->nobsp) - return "no bsps will be loaded\n"; - else - return "attempting to load bsp files\n"; - } - cluster->nobsp = !!atoi(arg[1]); - return "nobsp will change at start of next map\n"; } -char *Cmd_MaxViewers(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_UserConnects(cmdctxt_t *ctx) { - if (!*arg[1]) + if (Cmd_Argc(ctx) < 2) { - buffer[0] = '\0'; - if (cluster->maxviewers) - snprintf(buffer, sizeofbuffer, "maxviewers is currently %i\n", cluster->maxviewers); - else - return "maxviewers is currently unlimited\n"; - return buffer; + Cmd_Printf(ctx, "userconnects is set to %i\n", !ctx->cluster->nouserconnects); + } + else + { + ctx->cluster->nouserconnects = !atoi(Cmd_Argv(ctx, 1)); + Cmd_Printf(ctx, "userconnects is now %i\n", !ctx->cluster->nouserconnects); } - cluster->maxviewers = atoi(arg[1]); - return "maxviewers set\n"; } -char *Cmd_AllowNQ(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_Choke(cmdctxt_t *ctx) { - if (!*arg[1]) + if (Cmd_Argc(ctx) < 2) { - buffer[0] = '\0'; - snprintf(buffer, sizeofbuffer, "allownq is currently %i\n", cluster->allownqclients); - return buffer; - } - cluster->allownqclients = !!atoi(arg[1]); - return "allownq set\n"; -} -char *Cmd_MaxProxies(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) -{ - if (!*arg[1]) - { - buffer[0] = '\0'; - if (cluster->maxproxies) - snprintf(buffer, sizeofbuffer, "maxproxies is currently %i\n", cluster->maxproxies); + if (ctx->cluster->chokeonnotupdated) + Cmd_Printf(ctx, "proxy will not interpolate packets\n"); else - return "maxproxies is currently unlimited\n"; - return buffer; + Cmd_Printf(ctx, "proxy will smooth action at the expense of extra packets\n"); + return; + } + ctx->cluster->chokeonnotupdated = !!atoi(Cmd_Argv(ctx, 1)); + Cmd_Printf(ctx, "choke-until-update set to %i\n", ctx->cluster->chokeonnotupdated); +} +void Cmd_Late(cmdctxt_t *ctx) +{ + if (Cmd_Argc(ctx) < 2) + { + if (ctx->cluster->lateforward) + Cmd_Printf(ctx, "forwarded streams will be artificially delayed\n"); + else + Cmd_Printf(ctx, "forwarded streams are forwarded immediatly\n"); + return; + } + ctx->cluster->lateforward = !!atoi(Cmd_Argv(ctx, 1)); + Cmd_Printf(ctx, "late forwarding set\n"); +} +void Cmd_Talking(cmdctxt_t *ctx) +{ + if (Cmd_Argc(ctx) < 2) + { + if (ctx->cluster->notalking) + Cmd_Printf(ctx, "viewers may not talk\n"); + else + Cmd_Printf(ctx, "viewers may talk freely\n"); + return; + } + ctx->cluster->notalking = !atoi(Cmd_Argv(ctx, 1)); + Cmd_Printf(ctx, "talking permissions set\n"); +} +void Cmd_NoBSP(cmdctxt_t *ctx) +{ + char *val = Cmd_Argv(ctx, 1); + if (!*val) + { + if (ctx->cluster->nobsp) + Cmd_Printf(ctx, "no bsps will be loaded\n"); + else + Cmd_Printf(ctx, "attempting to load bsp files\n"); + } + else + { + ctx->cluster->nobsp = !!atoi(val); + Cmd_Printf(ctx, "nobsp will change at start of next map\n"); + } +} + +void Cmd_MaxViewers(cmdctxt_t *ctx) +{ + char *val = Cmd_Argv(ctx, 1); + if (!*val) + { + if (ctx->cluster->maxviewers) + Cmd_Printf(ctx, "maxviewers is currently %i\n", ctx->cluster->maxviewers); + else + Cmd_Printf(ctx, "maxviewers is currently unlimited\n"); + } + else + { + ctx->cluster->maxviewers = atoi(val); + Cmd_Printf(ctx, "maxviewers set\n"); + } +} +void Cmd_AllowNQ(cmdctxt_t *ctx) +{ + char *val = Cmd_Argv(ctx, 1); + if (!*val) + { + Cmd_Printf(ctx, "allownq is currently %i\n", ctx->cluster->allownqclients); + } + else + { + ctx->cluster->allownqclients = !!atoi(val); + Cmd_Printf(ctx, "allownq set\n"); + } +} +void Cmd_MaxProxies(cmdctxt_t *ctx) +{ + char *val = Cmd_Argv(ctx, 1); + if (!*val) + { + if (ctx->cluster->maxproxies) + Cmd_Printf(ctx, "maxproxies is currently %i\n", ctx->cluster->maxproxies); + else + Cmd_Printf(ctx, "maxproxies is currently unlimited\n"); + } + else + { + ctx->cluster->maxproxies = atoi(val); + Cmd_Printf(ctx, "maxproxies set\n"); } - cluster->maxproxies = atoi(arg[1]); - return "maxproxies set\n"; } -char *Cmd_Ping(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_Ping(cmdctxt_t *ctx) { netadr_t addr; - if (NET_StringToAddr(arg[1], &addr, 27500)) + char *val = Cmd_Argv(ctx, 1); + if (NET_StringToAddr(val, &addr, 27500)) { - NET_SendPacket (cluster, cluster->qwdsocket, 1, "k", addr); - return "pinged\n"; + NET_SendPacket (ctx->cluster, ctx->cluster->qwdsocket, 1, "k", addr); + Cmd_Printf(ctx, "pinged\n"); } - return "couldn't resolve\n"; + Cmd_Printf(ctx, "couldn't resolve\n"); } -char *Cmd_Help(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_Help(cmdctxt_t *ctx) { - return HELPSTRING; + Cmd_Printf(ctx, HELPSTRING); } -char *Cmd_Echo(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_Echo(cmdctxt_t *ctx) { - return "Poly wants a cracker.\n"; + Cmd_Printf(ctx, "%s", Cmd_Argv(ctx, 1)); } -char *Cmd_Quit(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_Quit(cmdctxt_t *ctx) { - if (!localcommand) - return "Remote shutdown refused.\n"; - cluster->wanttoexit = true; - return "Shutting down.\n"; + if (!Cmd_IsLocal(ctx)) + Cmd_Printf(ctx, "Remote shutdown refused.\n"); + ctx->cluster->wanttoexit = true; + Cmd_Printf(ctx, "Shutting down.\n"); } @@ -765,74 +823,94 @@ char *Cmd_Quit(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, -char *Cmd_Streams(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_Streams(cmdctxt_t *ctx) { - catbuffer(buffer, sizeofbuffer, "Streams:\n"); + sv_t *qtv; + Cmd_Printf(ctx, "Streams:\n"); - for (qtv = cluster->servers; qtv; qtv = qtv->next) + for (qtv = ctx->cluster->servers; qtv; qtv = qtv->next) { - catbuffer(buffer, sizeofbuffer, "%i: %s\n", qtv->streamid, qtv->server); + Cmd_Printf(ctx, "%i: %s\n", qtv->streamid, qtv->server); } - return buffer; } -char *Cmd_Disconnect(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_DemoSpeed(cmdctxt_t *ctx) { - QTV_Shutdown(qtv); - return "Disconnected\n"; -} - -char *Cmd_Record(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) -{ - if (!*arg[1]) - return "record requires a filename on the proxy's machine\n"; - - if (!localcommand) - if (*arg[1] == '\\' || *arg[1] == '/' || strstr(arg[1], "..") || arg[1][1] == ':') - return "Absolute paths are prohibited.\n"; - - if (Net_FileProxy(qtv, arg[1])) - return "Recording to disk\n"; + char *val = Cmd_Argv(ctx, 1); + if (*val) + { + ctx->qtv->parsespeed = atof(val)*1000; + Cmd_Printf(ctx, "Setting demo speed to %f\n", ctx->qtv->parsespeed/1000.0f); + } else - return "Failed to open file\n"; -} -char *Cmd_Stop(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) -{ - if (Net_StopFileProxy(qtv)) - return "stopped\n"; - else - return "not recording to disk\n"; + Cmd_Printf(ctx, "Playing demo at %f speed\n", ctx->qtv->parsespeed/1000.0f); } -char *Cmd_Reconnect(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_Disconnect(cmdctxt_t *ctx) { - if (qtv->disconnectwhennooneiswatching == 2) - return "Stream is a reverse connection (command rejected)\n"; - else if (QTV_Connect(qtv, qtv->server)) - return "Reconnected\n"; - else - return "Failed to reconnect (will keep trying)\n"; + QTV_Shutdown(ctx->qtv); + Cmd_Printf(ctx, "Disconnected\n"); } -char *Cmd_MVDPort(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_Record(cmdctxt_t *ctx) { + char *fname = Cmd_Argv(ctx, 1); + if (!*fname) + Cmd_Printf(ctx, "record requires a filename on the proxy's machine\n"); + + if (!Cmd_IsLocal(ctx)) + { + if (*fname == '\\' || *fname == '/' || strstr(fname, "..") || fname[1] == ':') + { + Cmd_Printf(ctx, "Absolute paths are prohibited.\n"); + return; + } + } + + if (Net_FileProxy(ctx->qtv, fname)) + Cmd_Printf(ctx, "Recording to disk\n"); + else + Cmd_Printf(ctx, "Failed to open file\n"); +} +void Cmd_Stop(cmdctxt_t *ctx) +{ + if (Net_StopFileProxy(ctx->qtv)) + Cmd_Printf(ctx, "stopped\n"); + else + Cmd_Printf(ctx, "not recording to disk\n"); +} + +void Cmd_Reconnect(cmdctxt_t *ctx) +{ + if (ctx->qtv->disconnectwhennooneiswatching == 2) + Cmd_Printf(ctx, "Stream is a reverse connection (command rejected)\n"); + else if (QTV_Connect(ctx->qtv, ctx->qtv->server)) + Cmd_Printf(ctx, "Reconnected\n"); + else + Cmd_Printf(ctx, "Failed to reconnect (will keep trying)\n"); +} + +void Cmd_MVDPort(cmdctxt_t *ctx) +{ + char *val = Cmd_Argv(ctx, 1); int news; - int newp = atoi(arg[1]); + int newp = atoi(val); if (!newp) { - if (cluster->tcpsocket != INVALID_SOCKET) + if (ctx->cluster->tcpsocket != INVALID_SOCKET) { - closesocket(cluster->tcpsocket); - cluster->tcpsocket = INVALID_SOCKET; - cluster->tcplistenportnum = 0; + closesocket(ctx->cluster->tcpsocket); + ctx->cluster->tcpsocket = INVALID_SOCKET; + ctx->cluster->tcplistenportnum = 0; - return "mvd port is now closed\n"; + Cmd_Printf(ctx, "mvd port is now closed\n"); } - return "Already closed\n"; + else + Cmd_Printf(ctx, "Already closed\n"); } else { @@ -840,47 +918,101 @@ char *Cmd_MVDPort(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buff if (news != INVALID_SOCKET) { - if (cluster->tcpsocket != INVALID_SOCKET) - closesocket(cluster->tcpsocket); - cluster->tcpsocket = news; - cluster->tcplistenportnum = newp; + if (ctx->cluster->tcpsocket != INVALID_SOCKET) + closesocket(ctx->cluster->tcpsocket); + ctx->cluster->tcpsocket = news; + ctx->cluster->tcplistenportnum = newp; - snprintf(buffer, sizeofbuffer, "Opened tcp port %i\n", newp); + Cmd_Printf(ctx, "Opened tcp port %i\n", newp); } else - snprintf(buffer, sizeofbuffer, "Failed to open tcp port %i\n", newp); - return buffer; + Cmd_Printf(ctx, "Failed to open tcp port %i\n", newp); } } +void Cmd_DemoList(cmdctxt_t *ctx) +{ + int i; + int count; + Cluster_BuildAvailableDemoList(ctx->cluster); + + count = ctx->cluster->availdemoscount; + Cmd_Printf(ctx, "%i demos\n", count); + for (i = 0; i < count; i++) + { + Cmd_Printf(ctx, " %7i %s\n", ctx->cluster->availdemos[i].size, ctx->cluster->availdemos[i].name); + } +} + +void Cmd_DemoDir(cmdctxt_t *ctx) +{ + char *val; + val = Cmd_Argv(ctx, 1); + + if (*val) + { + if (!Cmd_IsLocal(ctx)) + { + Cmd_Printf(ctx, "Sorry, but I don't trust this code that well!\n"); + return; + } + while (*val > 0 &&*val <= ' ') + val++; + + if (strchr(val, '.') || strchr(val, ':') || *val == '/') + Cmd_Printf(ctx, "Rejecting path\n"); + else + { + qstrncpyz(ctx->cluster->demodir, val, sizeof(ctx->cluster->demodir)); + Cmd_Printf(ctx, "Changed demo dir to \"%s\"\n", ctx->cluster->demodir); + } + } + else + { + Cmd_Printf(ctx, "Current demo directory is \"%s\"\n", ctx->cluster->demodir); + } +} + +void Cmd_MuteStream(cmdctxt_t *ctx) +{ + char *val; + val = Cmd_Argv(ctx, 1); + ctx->qtv->silentstream = atoi(val); +#warning Implement me +} + #ifdef VIEWER -char *Cmd_Watch(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_Watch(cmdctxt_t *ctx) { if (!localcommand) - return "watch is not permitted remotly\n"; + { + Cmd_Printf(ctx, "watch is not permitted remotly\n"); + } if (cluster->viewserver == qtv) { cluster->viewserver = NULL; - return "Stopped watching\n"; + Cmd_Printf(ctx, "Stopped watching\n"); + } + else + { + cluster->viewserver = qtv; + Cmd_Printf(ctx, "Watching\n"); } - - cluster->viewserver = qtv; - - return "Watching\n"; } #endif -char *Cmd_Commands(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) + +void Cmd_Commands(cmdctxt_t *ctx) { - return "fixme\n"; + Cmd_Printf(ctx, "fixme\n"); } typedef struct rconcommands_s { char *name; qboolean serverspecific; //works within a qtv context qboolean clusterspecific; //works without a qtv context (ignores context) - dispatchrconcommand_t func; + consolecommand_t func; } rconcommands_t; const rconcommands_t rconcommands[] = @@ -904,26 +1036,32 @@ const rconcommands_t rconcommands[] = {"connect", 0, 1, Cmd_QTVConnect}, {"qw", 0, 1, Cmd_QWConnect}, {"observe", 0, 1, Cmd_QWConnect}, + {"demos", 0, 1, Cmd_DemoList}, {"demo", 0, 1, Cmd_MVDConnect}, {"playdemo", 0, 1, Cmd_MVDConnect}, {"choke", 0, 1, Cmd_Choke}, {"late", 0, 1, Cmd_Late}, {"talking", 0, 1, Cmd_Talking}, {"nobsp", 0, 1, Cmd_NoBSP}, + {"userconnects", 0, 1, Cmd_UserConnects}, {"maxviewers", 0, 1, Cmd_MaxViewers}, {"maxproxies", 0, 1, Cmd_MaxProxies}, + {"demodir", 0, 1, Cmd_DemoDir}, {"ping", 0, 1, Cmd_Ping}, {"reconnect", 0, 1, Cmd_Reconnect}, {"echo", 0, 1, Cmd_Echo}, {"quit", 0, 1, Cmd_Quit}, + {"exit", 0, 1, Cmd_Quit}, {"streams", 0, 1, Cmd_Streams}, {"allownq", 0, 1, Cmd_AllowNQ}, + {"mutestream", 1, 0, Cmd_MuteStream}, {"disconnect", 1, 0, Cmd_Disconnect}, {"record", 1, 0, Cmd_Record}, {"stop", 1, 0, Cmd_Stop}, + {"demospeed", 1, 0, Cmd_DemoSpeed}, {"tcpport", 0, 1, Cmd_MVDPort}, {"mvdport", 0, 1, Cmd_MVDPort}, @@ -934,16 +1072,16 @@ const rconcommands_t rconcommands[] = {NULL} }; -char *Rcon_Command(cluster_t *cluster, sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qboolean localcommand) +void Cmd_ExecuteNow(cmdctxt_t *ctx, char *command) { #define TOKENIZE_PUNCTUATION "" int i; char arg[MAX_ARGS][ARG_LEN]; - char *argptrs[MAX_ARGS]; char *sid; + char *cmdname; - for (sid = command; *sid; sid = sid++) + for (sid = command; *sid; sid++) { if (*sid == ':') break; @@ -955,51 +1093,89 @@ char *Rcon_Command(cluster_t *cluster, sv_t *qtv, char *command, char *buffer, i i = atoi(command); command = sid+1; - for (qtv = cluster->servers; qtv; qtv = qtv->next) - if (qtv->streamid == i) + for (ctx->qtv = ctx->cluster->servers; ctx->qtv; ctx->qtv = ctx->qtv->next) + if (ctx->qtv->streamid == i) break; } + ctx->argc = 0; for (i = 0; i < MAX_ARGS; i++) { command = COM_ParseToken(command, arg[i], ARG_LEN, TOKENIZE_PUNCTUATION); - argptrs[i] = arg[i]; + ctx->arg[i] = arg[i]; + if (command) + ctx->argc++; } - if (!qtv && cluster->numservers==1) - qtv = cluster->servers; + cmdname = Cmd_Argv(ctx, 0); - buffer[0] = 0; + if (!ctx->qtv && ctx->cluster->numservers==1) + ctx->qtv = ctx->cluster->servers; - if (qtv) + if (ctx->qtv) { //if there is a specific connection targetted for (i = 0; rconcommands[i].name; i++) { if (rconcommands[i].serverspecific) - if (!strcmp(rconcommands[i].name, argptrs[0])) - return rconcommands[i].func(cluster, qtv, argptrs, buffer, sizeofbuffer, localcommand); + if (!strcmp(rconcommands[i].name, cmdname)) + { + rconcommands[i].func(ctx); + return; + } } } for (i = 0; rconcommands[i].name; i++) { - if (!strcmp(rconcommands[i].name, argptrs[0])) + if (!strcmp(rconcommands[i].name, cmdname)) { if (rconcommands[i].clusterspecific) - return rconcommands[i].func(cluster, NULL, argptrs, buffer, sizeofbuffer, localcommand); + { + rconcommands[i].func(ctx); + return; + } else if (rconcommands[i].serverspecific) { - snprintf(buffer, sizeofbuffer, "Command \"%s\" requires a targeted server.\n", arg[0]); - return buffer; + Cmd_Printf(ctx, "Command \"%s\" requires a targeted server.\n", cmdname); + return; } } } - snprintf(buffer, sizeofbuffer, "Command \"%s\" not recognised.\n", arg[0]); - return buffer; + Cmd_Printf(ctx, "Command \"%s\" not recognised.\n", cmdname); } +void Rcon_PrintToBuffer(cmdctxt_t *ctx, char *msg) +{ + if (ctx->printcookiesize < 1) + return; + while (ctx->printcookiesize>2 && *msg) + { + ctx->printcookiesize--; + *(char*)ctx->printcookie = *msg++; + ctx->printcookie = ((char*)ctx->printcookie)+1; + } + + ctx->printcookiesize--; + *(char*)ctx->printcookie = 0; +} + +char *Rcon_Command(cluster_t *cluster, sv_t *source, char *command, char *resultbuffer, int resultbuffersize, int islocalcommand) +{ + cmdctxt_t ctx; + ctx.cluster = cluster; + ctx.qtv = source; + ctx.argc = 0; + ctx.printfunc = Rcon_PrintToBuffer; + ctx.printcookie = resultbuffer; + ctx.printcookiesize = resultbuffersize; + ctx.localcommand = islocalcommand; + *(char*)ctx.printcookie = 0; + Cmd_ExecuteNow(&ctx, command); + + return resultbuffer; +} diff --git a/fteqtv/source.c b/fteqtv/source.c index a0a3a1e01..ef3c1c5a9 100644 --- a/fteqtv/source.c +++ b/fteqtv/source.c @@ -59,6 +59,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #define RECONNECT_TIME (1000*30) +#define RECONNECT_TIME_DEMO (1000*5) #define UDPRECONNECT_TIME (1000) #define PINGSINTERVAL_TIME (1000*5) #define UDPTIMEOUT_LENGTH (1000*20) @@ -98,9 +99,9 @@ qboolean NET_StringToAddr (char *s, netadr_t *sadr, int defaultport) } else #endif -#ifdef IPPROTO_IPV6 +#if 0//def IPPROTO_IPV6 if (getaddrinfo) - { + {//ipv6 method (can return ipv4 addresses too) struct addrinfo *addrinfo, *pos; struct addrinfo udp6hint; int error; @@ -163,7 +164,7 @@ dblbreak: } else #endif - { + { //old fashioned method ((struct sockaddr_in *)sadr)->sin_family = AF_INET; ((struct sockaddr_in *)sadr)->sin_port = htons(defaultport); @@ -511,12 +512,7 @@ qboolean DemoFilenameIsOkay(char *fname) qboolean Net_ConnectToServer(sv_t *qtv) { char *at; - enum { - SRC_BAD, - SRC_DEMO, - SRC_UDP, - SRC_TCP - } type = SRC_BAD; + sourcetype_t type = SRC_BAD; char *ip = qtv->server; if (!strncmp(ip, "udp:", 4)) @@ -548,31 +544,58 @@ qboolean Net_ConnectToServer(sv_t *qtv) ip = at+1; } - qtv->usequkeworldprotocols = false; + qtv->usequakeworldprotocols = false; - qtv->nextconnectattempt = qtv->curtime + RECONNECT_TIME; //wait half a minuite before trying to reconnect + if (qtv->sourcetype == SRC_DEMO) + qtv->nextconnectattempt = qtv->curtime + RECONNECT_TIME_DEMO; //wait half a minuite before trying to reconnect + else + qtv->nextconnectattempt = qtv->curtime + RECONNECT_TIME; //wait half a minuite before trying to reconnect + + qtv->sourcetype = type; switch(type) { case SRC_DEMO: qtv->sourcesock = INVALID_SOCKET; if (DemoFilenameIsOkay(ip)) - qtv->sourcefile = fopen(ip, "rb"); + { + char fullname[512]; + snprintf(fullname, sizeof(fullname), "%s%s", qtv->cluster->demodir, ip); + qtv->sourcefile = fopen(fullname, "rb"); + } else qtv->sourcefile = NULL; if (qtv->sourcefile) { + char smallbuffer[17]; fseek(qtv->sourcefile, 0, SEEK_END); qtv->filelength = ftell(qtv->sourcefile); + + //attempt to detect the end of the file + fseek(qtv->sourcefile, -sizeof(smallbuffer), SEEK_CUR); + fread(smallbuffer, 1, 17, qtv->sourcefile); + //0 is the time + if (smallbuffer[1] == dem_all || smallbuffer[1] == dem_read) //mvdsv changed it to read... + { + //2,3,4,5 are the length + if (smallbuffer[6] == svc_disconnect) + { + if (!strcmp(smallbuffer+7, "EndOfDemo")) + { + qtv->filelength -= 17; + } + } + } + fseek(qtv->sourcefile, 0, SEEK_SET); return true; } - Sys_Printf(qtv->cluster, "Unable to open file %s\n", ip+5); + Sys_Printf(qtv->cluster, "Unable to open file %s\n", ip); return false; case SRC_UDP: - qtv->usequkeworldprotocols = true; + qtv->usequakeworldprotocols = true; return Net_ConnectToUDPServer(qtv, ip); case SRC_TCP: @@ -586,7 +609,7 @@ qboolean Net_ConnectToServer(sv_t *qtv) void Net_QueueUpstream(sv_t *qtv, int size, char *buffer) { - if (qtv->usequkeworldprotocols) + if (qtv->usequakeworldprotocols) return; if (qtv->upstreambuffersize + size > sizeof(qtv->upstreambuffer)) @@ -599,7 +622,7 @@ void Net_QueueUpstream(sv_t *qtv, int size, char *buffer) qtv->upstreambuffersize += size; } -qboolean Net_WriteUpStream(sv_t *qtv) +qboolean Net_WriteUpstream(sv_t *qtv) { int len; @@ -629,7 +652,48 @@ qboolean Net_WriteUpStream(sv_t *qtv) return true; } -int SV_ConsistantMVDData(unsigned char *buffer, int remaining) +void SV_SendUpstream(sv_t *qtv, netmsg_t *nm) +{ + char size[2]; + + size[0] = (nm->cursize&0x00ff)>>0; + size[1] = (nm->cursize&0xff00)>>8; + Net_QueueUpstream(qtv, 2, size); + Net_QueueUpstream(qtv, nm->cursize, nm->data); + Net_WriteUpstream(qtv); //try and flush it +} + +int SV_SayToUpstream(sv_t *qtv, char *message) +{ + char buffer[1024]; + netmsg_t nm; + if (!qtv->upstreamacceptschat) + { + Sys_Printf(qtv->cluster, "not forwarding say\n"); + return 0; + } + + InitNetMsg(&nm, buffer, sizeof(buffer)); + + WriteByte(&nm, qtv_clc_stringcmd); + WriteString2(&nm, "say "); + WriteString(&nm, message); + SV_SendUpstream(qtv, &nm); + + return 1; +} + +void SV_SayToViewers(sv_t *qtv, char *message) +{ + viewer_t *v; + Fwd_SayToDownstream(qtv, message); + #warning Send to viewers here too +} + +//This function 1: parses the 'don't delay' packets in the stream +// 2: returns the length of continuous data (that is, whole-packet bytes that have not been truncated by the networking layer) +// this means we know that the client proxies have valid data, at least from our side. +int SV_EarlyParse(sv_t *qtv, unsigned char *buffer, int remaining) { int lengthofs; int length; @@ -644,8 +708,8 @@ int SV_ConsistantMVDData(unsigned char *buffer, int remaining) switch (buffer[1]&dem_mask) { case dem_set: - length = 10; - goto gottotallength; + lengthofs = 0; //to silence gcc, nothing more + break; case dem_multiple: lengthofs = 6; break; @@ -654,17 +718,27 @@ int SV_ConsistantMVDData(unsigned char *buffer, int remaining) break; } - if (lengthofs+4 > remaining) - return available; + if (lengthofs > 0) + { + if (lengthofs+4 > remaining) + return available; - length = (buffer[lengthofs]<<0) + (buffer[lengthofs+1]<<8) + (buffer[lengthofs+2]<<16) + (buffer[lengthofs+3]<<24); + length = (buffer[lengthofs]<<0) + (buffer[lengthofs+1]<<8) + (buffer[lengthofs+2]<<16) + (buffer[lengthofs+3]<<24); + + length += lengthofs+4; + if (length > 1500) + printf("Probably corrupt mvd (length %i)\n", length); + } + else + length = 10; - length += lengthofs+4; - if (length > 1400) - printf("Corrupt mvd\n"); -gottotallength: if (remaining < length) return available; + + if ((buffer[1]&dem_mask) == dem_all && (buffer[1] & ~dem_mask) && qtv->sourcetype != SRC_DEMO) //dem_qtvdata + { + ParseMessage(qtv, buffer+lengthofs+4, length - (lengthofs+4), buffer[1], 0xffffffff); + } remaining -= length; available += length; @@ -676,7 +750,7 @@ qboolean Net_ReadStream(sv_t *qtv) { int maxreadable; int read; - char *buffer; + void *buffer; int err; maxreadable = MAX_PROXY_BUFFER - qtv->buffersize; @@ -690,13 +764,21 @@ qboolean Net_ReadStream(sv_t *qtv) maxreadable = PREFERED_PROXY_BUFFER-qtv->buffersize; if (maxreadable<=0) return true; + + //reuse read a little... + read = ftell(qtv->sourcefile); + if (read+maxreadable > qtv->filelength) + maxreadable = qtv->filelength-read; //clamp to the end of the file + //even if that 'end' is before the svc_disconnect + read = fread(buffer, 1, maxreadable, qtv->sourcefile); } else { - read = sizeof(err); + unsigned int errsize; + errsize = sizeof(err); err = 0; - getsockopt(qtv->sourcesock, SOL_SOCKET, SO_ERROR, (char*)&err, &read); + getsockopt(qtv->sourcesock, SOL_SOCKET, SO_ERROR, (char*)&err, &errsize); if (err == ECONNREFUSED) { Sys_Printf(qtv->cluster, "Error: server %s refused connection\n", qtv->server); @@ -714,7 +796,7 @@ qboolean Net_ReadStream(sv_t *qtv) if (!qtv->cluster->lateforward && !qtv->parsingqtvheader) //qtv header being the auth part of the connection rather than the stream { int forwardable; - forwardable = SV_ConsistantMVDData(qtv->buffer+qtv->forwardpoint, qtv->buffersize - qtv->forwardpoint); + forwardable = SV_EarlyParse(qtv, qtv->buffer+qtv->forwardpoint, qtv->buffersize - qtv->forwardpoint); if (forwardable > 0) { SV_ForwardStream(qtv, qtv->buffer+qtv->forwardpoint, forwardable); @@ -752,7 +834,7 @@ qboolean Net_ReadStream(sv_t *qtv) return true; } -#define BUFFERTIME 10 //secords for arificial delay, so we can buffer things properly. +#define BUFFERTIME 10 //secords for artificial delay, so we can buffer things properly. unsigned int Sys_Milliseconds(void) { @@ -777,10 +859,12 @@ unsigned int Sys_Milliseconds(void) return timeGetTime(); #else //assume every other system follows standards. + unsigned int t; struct timeval tv; gettimeofday(&tv, NULL); - return ((unsigned)tv.tv_sec)*1000 + (((unsigned)tv.tv_usec)/1000); + t = ((unsigned int)tv.tv_sec)*1000 + (((unsigned int)tv.tv_usec)/1000); + return t; #endif } /* @@ -921,7 +1005,7 @@ qboolean QTV_Connect(sv_t *qtv, char *serverurl) memcpy(qtv->server, serverurl, sizeof(qtv->server)-1); - if (qtv->disconnectwhennooneiswatching) + if (qtv->disconnectwhennooneiswatching == 2) { //added because of paranoia rather than need. Should never occur. printf("bug: autoclose==2\n"); return false; @@ -998,7 +1082,7 @@ void QTV_Shutdown(sv_t *qtv) { QW_SetViewersServer(qtv->cluster, v, NULL); QW_SetMenu(v, MENU_NONE); - QTV_Say(cluster, v->server, v, ".menu", false); + QTV_SayCommand(cluster, v->server, v, "menu"); QW_PrintfToViewer(v, "Stream %s is closing\n", qtv->server); } } @@ -1076,7 +1160,7 @@ void ChooseFavoriteTrack(sv_t *tv) SendClientCommand (tv, "ptrack %i\n", best); tv->trackplayer = best; - if (tv->usequkeworldprotocols) + if (tv->usequakeworldprotocols) QW_StreamStuffcmd(tv->cluster, tv, "track %i\n", best); } } @@ -1122,7 +1206,7 @@ static const unsigned char chktbl[1024] = { }; -unsigned char COM_BlockSequenceCRCByte (unsigned char *base, int length, int sequence) +unsigned char COM_BlockSequenceCRCByte (void *base, int length, int sequence) { unsigned short crc; const unsigned char *p; @@ -1165,7 +1249,7 @@ void QTV_ParseQWStream(sv_t *qtv) { char buffer[1500]; netadr_t from; - int fromlen; + unsigned int fromlen; int readlen; netmsg_t msg; fromlen = sizeof(from); //bug: this won't work on (free)bsd @@ -1386,7 +1470,7 @@ void QTV_Run(sv_t *qtv) - if (qtv->usequkeworldprotocols) + if (qtv->sourcetype == SRC_UDP) { qtv->simtime += qtv->curtime - oldcurtime; @@ -1560,12 +1644,13 @@ void QTV_Run(sv_t *qtv) //obviously, we need to keep reading the stream to keep things smooth } - Net_WriteUpStream(qtv); + Net_WriteUpstream(qtv); } if (qtv->parsingqtvheader) { + float svversion; int length; char *start; char *nl; @@ -1584,16 +1669,17 @@ void QTV_Run(sv_t *qtv) length = qtv->buffersize; if (length > 6) length = 6; - if (strncmp(qtv->buffer, "QTVSV ", length)) + if (ustrncmp(qtv->buffer, "QTVSV ", length)) { Sys_Printf(qtv->cluster, "Server is not a QTV server (or is incompatable)\n"); +printf("%i, %s\n", qtv->buffersize, qtv->buffer); qtv->drop = true; return; } if (length < 6) return; //not ready yet - end = qtv->buffer + qtv->buffersize - 1; - for (nl = qtv->buffer; nl < end; nl++) + end = (char*)qtv->buffer + qtv->buffersize - 1; + for (nl = (char*)qtv->buffer; nl < end; nl++) { if (nl[0] == '\n' && nl[1] == '\n') break; @@ -1603,17 +1689,22 @@ void QTV_Run(sv_t *qtv) //we now have a complete packet. - if (atoi(qtv->buffer + 6) != 1) + svversion = atof((char*)qtv->buffer + 6); + if ((int)svversion != 1) { - Sys_Printf(qtv->cluster, "QTV server doesn't support a compatable protocol version (returned %i)\n", atoi(qtv->buffer + 6)); + Sys_Printf(qtv->cluster, "QTV server doesn't support a compatable protocol version (returned %i)\n", atoi((char*)qtv->buffer + 6)); qtv->drop = true; return; } + + qtv->upstreamacceptschat = svversion>=1.1; + qtv->upstreamacceptsdownload = svversion>=1.1; + length = (nl - (char*)qtv->buffer) + 2; end = nl; nl[1] = '\0'; - start = strchr(qtv->buffer, '\n')+1; + start = strchr((char*)qtv->buffer, '\n')+1; while((nl = strchr(start, '\n'))) { @@ -1710,7 +1801,7 @@ void QTV_Run(sv_t *qtv) if (qtv->serverquery) { - Sys_Printf(qtv->cluster, "End of list\n", colon); + Sys_Printf(qtv->cluster, "End of list\n"); qtv->drop = true; qtv->buffersize = 0; return; @@ -1722,14 +1813,14 @@ void QTV_Run(sv_t *qtv) } else if (qtv->parsingqtvheader) { - Sys_Printf(qtv->cluster, "QTV server sent no begin command - assuming incompatable\n\n", colon); + Sys_Printf(qtv->cluster, "QTV server sent no begin command - assuming incompatable\n\n"); qtv->drop = true; qtv->buffersize = 0; return; } qtv->parsetime = Sys_Milliseconds() + BUFFERTIME*1000; - if (!qtv->usequkeworldprotocols) + if (!qtv->usequakeworldprotocols) Sys_Printf(qtv->cluster, "Connection established, buffering for %i seconds\n", BUFFERTIME); SV_ForwardStream(qtv, qtv->buffer, qtv->forwardpoint); @@ -1737,7 +1828,6 @@ void QTV_Run(sv_t *qtv) QTV_CollectCommentry(qtv); - while (qtv->curtime >= qtv->parsetime) { if (qtv->buffersize < 2) @@ -1746,7 +1836,7 @@ void QTV_Run(sv_t *qtv) { qtv->parsetime = qtv->curtime + 2*1000; //add two seconds if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET) - Sys_Printf(qtv->cluster, "Not enough buffered\n"); + QTV_Printf(qtv, "Not enough buffered\n"); } break; } @@ -1761,7 +1851,7 @@ void QTV_Run(sv_t *qtv) { //not enough stuff to play. qtv->parsetime = qtv->curtime + 2*1000; //add two seconds if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET) - Sys_Printf(qtv->cluster, "Not enough buffered\n"); + QTV_Printf(qtv, "Not enough buffered\n"); continue; } qtv->parsetime += buffer[0]; //well this was pointless @@ -1787,17 +1877,17 @@ void QTV_Run(sv_t *qtv) if (qtv->buffersize < lengthofs+4) { //the size parameter doesn't fit. if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET) - Sys_Printf(qtv->cluster, "Not enough buffered\n"); + QTV_Printf(qtv, "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! + if (length > 1500) + { //THIS SHOULDN'T HAPPEN! //Blame the upstream proxy! - Sys_Printf(qtv->cluster, "Warning: corrupt input packet (%i) too big! Flushing and reconnecting!\n", length); + QTV_Printf(qtv, "Warning: corrupt input packet (%i bytes) too big! Flushing and reconnecting!\n", length); if (qtv->sourcefile) { fclose(qtv->sourcefile); @@ -1815,12 +1905,21 @@ void QTV_Run(sv_t *qtv) if (length+lengthofs+4 > qtv->buffersize) { if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET) - Sys_Printf(qtv->cluster, "Not enough buffered\n"); + QTV_Printf(qtv, "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->sourcesock != INVALID_SOCKET) +// { +// QTV_Printf(qtv, "Forcing demo speed to play at 100% speed\n"); +// qtv->parsespeed = 1000; //no speeding up/slowing down routed demos +// } + + packettime = buffer[0]; + if (qtv->parsespeed>0) + packettime = ((1000*packettime) / qtv->parsespeed); + qtv->nextpackettime = qtv->parsetime + packettime; if (qtv->nextpackettime < qtv->curtime) { @@ -1833,9 +1932,13 @@ void QTV_Run(sv_t *qtv) 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); + if (qtv->buffer[1] & ~dem_mask) //dem_qtvdata + if (qtv->sourcetype != SRC_DEMO) + break; + //fallthrough + case dem_read: + ParseMessage(qtv, buffer+lengthofs+4, length, qtv->buffer[1], 0xffffffff); break; default: Sys_Printf(qtv->cluster, "Message type %i\n", qtv->buffer[1]&dem_mask); @@ -1846,7 +1949,6 @@ void QTV_Run(sv_t *qtv) qtv->oldpackettime = qtv->curtime; - packettime = buffer[0]; if (qtv->buffersize) { //svc_disconnect can flush our input buffer (to prevent the EndOfDemo part from interfering) @@ -1906,6 +2008,7 @@ sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, char *password, qtv->parsingconnectiondata = true; qtv->serverquery = query; qtv->silentstream = true; + qtv->parsespeed = 1000; qtv->streamid = ++cluster->nextstreamid;