From 6c74368870e349895e9cac3af6ccaf8195bf76be Mon Sep 17 00:00:00 2001 From: Circlemaster Date: Fri, 21 Nov 2014 18:10:29 +0000 Subject: [PATCH] Added 'dir' command to play a directory of demos. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4790 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- fteqtv/qtv.h | 2 + fteqtv/rcon.c | 8 ++ fteqtv/source.c | 208 +++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 179 insertions(+), 39 deletions(-) diff --git a/fteqtv/qtv.h b/fteqtv/qtv.h index bd803deb..7f86f040 100644 --- a/fteqtv/qtv.h +++ b/fteqtv/qtv.h @@ -558,6 +558,7 @@ typedef struct { typedef enum { SRC_BAD, SRC_DEMO, + SRC_DEMODIR, SRC_UDP, SRC_TCP } sourcetype_t; @@ -630,6 +631,7 @@ struct sv_s { //details about a server connection (also known as stream) FILE *sourcefile; unsigned int filelength; SOCKET sourcesock; + int last_random_number; // for demo directories randomizing stuff // SOCKET tcpsocket; //tcp + mvd protocol // int tcplistenportnum; diff --git a/fteqtv/rcon.c b/fteqtv/rcon.c index 97dfca00..471890f8 100644 --- a/fteqtv/rcon.c +++ b/fteqtv/rcon.c @@ -467,6 +467,8 @@ void Cmd_GenericConnect(cmdctxt_t *ctx, char *method, enum autodisconnect_e auto { if (!strncmp(method, "file", 4)) Cmd_Printf(ctx, "%s requires a demo name parameter\n", Cmd_Argv(ctx, 0)); + else if (!strncmp(method, "dir", 3)) + Cmd_Printf(ctx, "%s requires a demo directory parameter\n", Cmd_Argv(ctx, 0)); else Cmd_Printf(ctx, "%s requires an ip:port parameter\n", Cmd_Argv(ctx, 0)); return; @@ -498,6 +500,10 @@ void Cmd_MVDConnect(cmdctxt_t *ctx) { Cmd_GenericConnect(ctx, "file:", AD_NO); } +void Cmd_DirMVDConnect(cmdctxt_t *ctx) +{ + Cmd_GenericConnect(ctx, "dir:", AD_NO); +} void Cmd_Exec(cmdctxt_t *ctx) { @@ -1258,6 +1264,8 @@ const rconcommands_t rconcommands[] = {"demos", 0, 1, Cmd_DemoList, "shows the list of demos available on this proxy"}, {"demo", 0, 1, Cmd_MVDConnect, "adds a demo as a new stream"}, {"playdemo", 0, 1, Cmd_MVDConnect}, + {"dir", 0, 1, Cmd_DirMVDConnect, "adds a directory of demos as a new stream"}, + {"playdir", 0, 1, Cmd_DirMVDConnect}, {"choke", 0, 1, Cmd_Choke, "chokes packets to the data rate in the stream, disables proxy-side interpolation"}, {"late", 0, 1, Cmd_Late, "enforces a time delay on packets sent through this proxy"}, {"talking", 0, 1, Cmd_Talking, "permits viewers to talk to each other"}, diff --git a/fteqtv/source.c b/fteqtv/source.c index 4d021ad3..e13853ce 100644 --- a/fteqtv/source.c +++ b/fteqtv/source.c @@ -58,6 +58,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "bsd_string.h" #ifndef _WIN32 +#include #include #endif @@ -573,15 +574,30 @@ qboolean Net_ConnectToUDPServer(sv_t *qtv, char *ip) return true; } -qboolean DemoFilenameIsOkay(char *fname) +qboolean DemoFilenameIsOkay(sv_t* qtv, char *fname, char *dir) { int len; + + if (dir) + { + if (strchr(dir, '/')) + return false; //unix path seperator + if (strchr(dir, '\\')) + return false; //windows path seperator + if (strchr(dir, ':')) + return false; //mac path seperator + if (*(dir) == '.') + return false; + } + if (strchr(fname, '/')) return false; //unix path seperator if (strchr(fname, '\\')) return false; //windows path seperator if (strchr(fname, ':')) return false; //mac path seperator + if (*(fname) == '.') + return false; //now make certain that the last four characters are '.mvd' and not something like '.cfg' perhaps len = strlen(fname); @@ -619,6 +635,144 @@ qboolean DemoFilenameIsOkay(char *fname) */ } +qboolean Net_ConnectToDemoServer(sv_t* qtv, char* ip, char* dir) +{ + char fullname[512]; + qtv->sourcesock = INVALID_SOCKET; + if (DemoFilenameIsOkay(qtv, ip, dir)) + { + if (!dir) + snprintf(fullname, sizeof(fullname), "%s%s", qtv->cluster->demodir, ip); + else + snprintf(fullname, sizeof(fullname), "%s%s/%s", qtv->cluster->demodir, dir, 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, 0-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; + } + + if (!dir) + Sys_Printf(qtv->cluster, "Stream %i: Unable to open file %s\n", qtv->streamid, ip); + else + Sys_Printf(qtv->cluster, "Stream %i: Unable to open file %s in directory %s\n", qtv->streamid, ip, dir); + return false; +} + +qboolean Net_ConnectToDemoDirServer(sv_t* qtv, char *ip) +{ + char fullname[512]; + qtv->sourcesock = INVALID_SOCKET; + snprintf(fullname, sizeof(fullname), "%s%s", qtv->cluster->demodir, ip); + + #ifdef _WIN32 + // TODO: code for Windows directories goes here + // TODO: possible to do this without copy-pasting the entire GNU/Linux code below? + // TODO: also, what about MAC OS X? + Sys_Printf(qtv->cluster, "Windows support coming soon!\n"); + return false; + #else + DIR *dir; + struct dirent* ent; + + dir = opendir(fullname); + if (dir) + { + char demoname[512]; + int current_demo = 0; + int file_count = 0; + int random_number; + + // count the files, important for determining a random demo file + while ((ent = readdir(dir)) != NULL) + { + if (ent->d_type == DT_REG && *(ent->d_name) != '.') + file_count++; // only add non-hidden and regular files + } + + if (file_count == 0) + { + // empty directory + Sys_Printf(qtv->cluster, "Stream %i: Error: Directory is empty.\n", qtv->streamid); + closedir(dir); + return false; + } + + closedir(dir); + dir = opendir(fullname); + + // FIXME: not sure if srand should only be called once somewhere? + // FIXME: this is not really shuffling the demos, but does introduce some variety + srand(time(NULL)); + while ((random_number = rand()%file_count + 1) == qtv->last_random_number); + qtv->last_random_number = random_number; + + while (1) { + ent = readdir(dir); + if (!ent) + { + // reached the end of the directory, shouldn't happen + Sys_Printf(qtv->cluster, "Stream %i: Error: Reached end of directory (%s%s)\n", qtv->streamid, qtv->cluster->demodir, ip); + closedir(dir); + return false; + } + + if (ent->d_type != DT_REG || *(ent->d_name) == '.') + { + continue; // ignore hidden and non-regular files + } + + if (++current_demo != random_number) + continue; + + snprintf(demoname, sizeof(demoname), "%s/%s", ip, ent->d_name); + qtv->sourcefile = demoname; + closedir(dir); + if (Net_ConnectToDemoServer(qtv, ent->d_name, ip) == true) + { + return true; + } + else + { + return false; + } + } + closedir(dir); + } + else + { + Sys_Printf(qtv->cluster, "Stream %i: Unable to open directory %s\n", qtv->streamid, qtv->cluster->demodir); + return false; + } + #endif + + return false; +} + /*figures out the ip to connect to, and decides the protocol for it*/ char *Net_DiagnoseProtocol(sv_t *qtv) { @@ -646,11 +800,16 @@ char *Net_DiagnoseProtocol(sv_t *qtv) type = SRC_DEMO; ip += 5; } + else if (!strncmp(ip, "dir:", 4)) + { + type = SRC_DEMODIR; + ip += 4; + } at = strchrrev(ip, '@'); - if (at && (type == SRC_DEMO || type == SRC_TCP)) + if (at && (type == SRC_DEMO || type == SRC_DEMODIR || type == SRC_TCP)) { - if (type == SRC_DEMO) + if (type == SRC_DEMO || type == SRC_DEMODIR) type = SRC_TCP; ip = at+1; } @@ -666,50 +825,21 @@ qboolean Net_ConnectToServer(sv_t *qtv) qtv->usequakeworldprotocols = false; qtv->pext = 0; - if (qtv->sourcetype == SRC_DEMO) + if (qtv->sourcetype == SRC_DEMO || qtv->sourcetype == SRC_DEMODIR) + { 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 switch( qtv->sourcetype) { case SRC_DEMO: - qtv->sourcesock = INVALID_SOCKET; - if (DemoFilenameIsOkay(ip)) - { - 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); + return Net_ConnectToDemoServer(qtv, ip, NULL); - //attempt to detect the end of the file - fseek(qtv->sourcefile, 0-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, "Stream %i: Unable to open file %s\n", qtv->streamid, ip); - return false; + case SRC_DEMODIR: + return Net_ConnectToDemoDirServer(qtv, ip); case SRC_UDP: @@ -1885,7 +2015,7 @@ void QTV_Run(sv_t *qtv) if (qtv->errored == ERR_DISABLED) return; - if (qtv->curtime >= qtv->nextconnectattempt || qtv->curtime < qtv->nextconnectattempt - RECONNECT_TIME*2) + if (qtv->sourcetype == SRC_DEMODIR || qtv->curtime >= qtv->nextconnectattempt || qtv->curtime < qtv->nextconnectattempt - RECONNECT_TIME*2) { if (qtv->autodisconnect == AD_REVERSECONNECT) //2 means a reverse connection {