diff --git a/fteqtv/cmd.h b/fteqtv/cmd.h index 22c1c8c35..edc8dc5ac 100755 --- a/fteqtv/cmd.h +++ b/fteqtv/cmd.h @@ -4,7 +4,8 @@ typedef struct cmdctxt_s cmdctxt_t; struct cmdctxt_s { cluster_t *cluster; - sv_t *qtv; + sv_t *qtv; + int streamid; //streamid, which is valid even if qtv is not, for specifying the streamid to use on connects char *arg[MAX_ARGS]; int argc; void (*printfunc)(cmdctxt_t *ctx, char *str); diff --git a/fteqtv/control.c b/fteqtv/control.c index 80d49856d..0112e4af3 100644 --- a/fteqtv/control.c +++ b/fteqtv/control.c @@ -8,7 +8,9 @@ Contains the control routines that handle both incoming and outgoing stuff #ifndef _WIN32 #include -#include +#include +#else +#include #endif // char *date = "Oct 24 1996"; @@ -608,3 +610,297 @@ void Sys_Printf(cluster_t *cluster, char *fmt, ...) printf("%s", string); } + + + + + + + + + + + + + + + + + + + + + + +//FIXME: move this to an appropriate place +#ifdef _WIN32 +void Sys_mkdir(char *name) +{ + _mkdir(name); +} +#elif defined(__linux__) +void Sys_mkdir(char *name) +{ + mkdir(name, 0777); +} +#else +#warning no Sys_mkdir function defined, hope the default works for you +void Sys_mkdir(char *name) +{ + mkdir(name, 0777); +} +#endif + +void QTV_mkdir(char *path) +{ + char *ofs; + + for (ofs = path+1 ; *ofs ; ofs++) + { + if (*ofs == '/') + { // create the directory + *ofs = 0; + Sys_mkdir (path); + *ofs = '/'; + } + } +} + + + +/* + + +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; +} + +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; + } + } + } +} + + + + + +*/ \ No newline at end of file diff --git a/fteqtv/forward.c b/fteqtv/forward.c index e99c9079e..5d5fff320 100644 --- a/fteqtv/forward.c +++ b/fteqtv/forward.c @@ -334,19 +334,19 @@ void Prox_SendPlayerStats(sv_t *qtv, oproxy_t *prox) { for (snum = 0; snum < MAX_STATS; snum++) { - if (qtv->players[player].stats[snum]) + if (qtv->map.players[player].stats[snum]) { - if ((unsigned)qtv->players[player].stats[snum] > 255) + if ((unsigned)qtv->map.players[player].stats[snum] > 255) { WriteByte(&msg, svc_updatestatlong); WriteByte(&msg, snum); - WriteLong(&msg, qtv->players[player].stats[snum]); + WriteLong(&msg, qtv->map.players[player].stats[snum]); } else { WriteByte(&msg, svc_updatestat); WriteByte(&msg, snum); - WriteByte(&msg, qtv->players[player].stats[snum]); + WriteByte(&msg, qtv->map.players[player].stats[snum]); } } } @@ -366,46 +366,46 @@ void Prox_SendInitialPlayers(sv_t *qtv, oproxy_t *prox, netmsg_t *msg) for (i = 0; i < MAX_CLIENTS; i++) { - if (!qtv->players[i].active) // interesting, is this set to false if player disconnect from server? + if (!qtv->map.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) + | (qtv->map.players[i].dead ? DF_DEAD : 0) + | (qtv->map.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)))) + if (*qtv->map.players[i].userinfo && atoi(Info_ValueForKey(qtv->map.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 + WriteByte (msg, qtv->map.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]); + WriteShort (msg, qtv->map.players[i].current.origin[j]); for (j = 0 ; j < 3 ; j++) if (flags & (DF_ANGLES << j)) - WriteShort (msg, qtv->players[i].current.angles[j]); + WriteShort (msg, qtv->map.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); + WriteByte (msg, qtv->map.players[i].current.modelindex); if (flags & DF_SKINNUM) - WriteByte (msg, qtv->players[i].current.skinnum); + WriteByte (msg, qtv->map.players[i].current.skinnum); if (flags & DF_EFFECTS) - WriteByte (msg, qtv->players[i].current.effects); + WriteByte (msg, qtv->map.players[i].current.effects); if (flags & DF_WEAPONFRAME) - WriteByte (msg, qtv->players[i].current.weaponframe); + WriteByte (msg, qtv->map.players[i].current.weaponframe); } } @@ -430,7 +430,9 @@ void Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox) netmsg_t msg; int prespawn; - if (!*qtv->mapname) + //only send connection data if there's actual data to be sent + //if not, the other end will get the data when we receive it anyway. + if (!*qtv->map.mapname) return; InitNetMsg(&msg, buffer, sizeof(buffer)); @@ -443,14 +445,14 @@ void Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox) for (prespawn = 0;prespawn >= 0;) { - prespawn = SendList(qtv, prespawn, qtv->soundlist, svc_soundlist, &msg); + prespawn = SendList(qtv, prespawn, qtv->map.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); + prespawn = SendList(qtv, prespawn, qtv->map.modellist, svc_modellist, &msg); Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1); msg.cursize = 0; } @@ -764,12 +766,12 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend) int i; for (i = 0; i < MAX_CLIENTS; i++) { - if (*qtv->players[i].userinfo) + if (*qtv->map.players[i].userinfo) plyrs++; } sprintf(tempbuf, "SRCSRV: %s\n", qtv->server); Net_ProxySendString(cluster, pend, tempbuf); - sprintf(tempbuf, "SRCHOST: %s\n", qtv->hostname); + sprintf(tempbuf, "SRCHOST: %s\n", qtv->map.hostname); Net_ProxySendString(cluster, pend, tempbuf); sprintf(tempbuf, "SRCPLYRS: %i\n", plyrs); Net_ProxySendString(cluster, pend, tempbuf); @@ -780,7 +782,7 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend) } else { - sprintf(tempbuf, "ASOURCE: %i: %15s: %15s\n", qtv->streamid, qtv->server, qtv->hostname); + sprintf(tempbuf, "ASOURCE: %i: %15s: %15s\n", qtv->streamid, qtv->server, qtv->map.hostname); Net_ProxySendString(cluster, pend, tempbuf); } } @@ -899,7 +901,7 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend) if (*t < '0' || *t > '9') break; if (*t) - qtv = QTV_NewServerConnection(cluster, colon, "", false, true, true, false); + qtv = QTV_NewServerConnection(cluster, 0, colon, "", false, true, true, false); else { //numerical source, use a stream id. @@ -913,7 +915,7 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend) char buf[256]; snprintf(buf, sizeof(buf), "demo:%s", colon); - qtv = QTV_NewServerConnection(cluster, buf, "", false, true, true, false); + qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, true, true, false); if (!qtv) { Net_ProxySendString(cluster, pend, QTVSVHEADER diff --git a/fteqtv/httpsv.c b/fteqtv/httpsv.c index 5edadd3b7..06f51acf2 100644 --- a/fteqtv/httpsv.c +++ b/fteqtv/httpsv.c @@ -188,7 +188,7 @@ static void HTTPSV_GenerateNowPlaying(cluster_t *cluster, oproxy_t *dest) for (streams = cluster->servers; streams; streams = streams->next) { HTMLPRINT("
"); - HTMLprintf(buffer, sizeof(buffer), "%s (%s: %s)", streams->server, streams->gamedir, streams->mapname); + HTMLprintf(buffer, sizeof(buffer), "%s (%s: %s)", streams->server, streams->map.gamedir, streams->map.mapname); Net_ProxySend(cluster, dest, buffer, strlen(buffer)); snprintf(buffer, sizeof(buffer), " [ Watch Now ]", streams->streamid); Net_ProxySend(cluster, dest, buffer, strlen(buffer)); @@ -196,11 +196,11 @@ static void HTTPSV_GenerateNowPlaying(cluster_t *cluster, oproxy_t *dest) for (player = 0; player < MAX_CLIENTS; player++) { - if (*streams->players[player].userinfo) + if (*streams->map.players[player].userinfo) { - Info_ValueForKey(streams->players[player].userinfo, "name", plname, sizeof(plname)); + Info_ValueForKey(streams->map.players[player].userinfo, "name", plname, sizeof(plname)); - if (streams->players[player].frags < -90) + if (streams->map.players[player].frags < -90) { HTMLPRINT("
  • "); } diff --git a/fteqtv/menu.c b/fteqtv/menu.c new file mode 100644 index 000000000..18e63fa0e --- /dev/null +++ b/fteqtv/menu.c @@ -0,0 +1,561 @@ +#include "qtv.h" + +#define CENTERTIME 1.5 + +void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum) +{ + //build a possible message, even though it'll probably not be sent + + sv_t *sv; + int i, min; + + switch(viewer->menunum) + { + default: + break; + + case MENU_MAIN: + if (buttonnum < 0) + viewer->menuop -= (MENU_MAIN_ITEMCOUNT + 1)/2; + else if (buttonnum > 0) + viewer->menuop += (MENU_MAIN_ITEMCOUNT + 1)/2; + else if (buttonnum == 0) + { + switch(viewer->menuop) + { + case MENU_MAIN_STREAMS: //Streams + QW_SetMenu(viewer, MENU_SERVERS); + break; + case MENU_MAIN_CLIENTLIST://Client List + QW_SetMenu(viewer, MENU_CLIENTS); + break; + + case MENU_MAIN_NEWSTREAM://New Stream + QW_PrintfToViewer(viewer, "Not implemented yet\n"); + break; + case MENU_MAIN_DEMOS://Demos + Cluster_BuildAvailableDemoList(cluster); + QW_SetMenu(viewer, MENU_DEMOS); + break; + + case MENU_MAIN_SERVERBROWSER://Server Browser + QW_PrintfToViewer(viewer, "Not implemented yet\n"); + break; + case MENU_MAIN_ADMIN://Admin + QW_SetMenu(viewer, MENU_ADMIN); + break; + + case MENU_MAIN_PREVPROX://Previous Proxy + if (viewer->isproxy) + { + QW_SetMenu(viewer, MENU_NONE); + QW_StuffcmdToViewer(viewer, "say proxy:menu\n"); + } + else + QW_PrintfToViewer(viewer, "No client proxy detected\n"); + break; + case MENU_MAIN_NEXTPROX://Next Proxy + if (viewer->server && viewer->server->serverisproxy && viewer->server->controller == viewer) + { + viewer->server->proxyisselected = false; + QW_SetMenu(viewer, MENU_NONE); + SendClientCommand(viewer->server, "say .menu"); + } + else + QW_PrintfToViewer(viewer, "No server proxy detected\n"); + break; + + case MENU_MAIN_HELP://Help Menu + QW_PrintfToViewer(viewer, "Not implemented yet\n"); + break; + } + } + break; + + case MENU_CLIENTS: + { + } + break; + + case MENU_DEMOS: + if (buttonnum >= 0) + QW_StuffcmdToViewer(viewer, "say .demo %s\n", cluster->availdemos[viewer->menuop].name); + else + QW_SetMenu(viewer, MENU_MAIN); + break; + + case MENU_ADMINSERVER: + if (viewer->server) + { + i = 0; + sv = viewer->server; + if (i++ == viewer->menuop) + { //auto disconnect + sv->disconnectwhennooneiswatching ^= 1; + } + if (i++ == viewer->menuop) + { //disconnect + QTV_Shutdown(viewer->server); + } + if (i++ == viewer->menuop) + { + if (sv->controller == viewer) + sv->controller = NULL; + else + { + sv->controller = viewer; + sv->controllersquencebias = viewer->netchan.outgoing_sequence - sv->netchan.outgoing_sequence; + } + } + if (i++ == viewer->menuop) + { //back + QW_SetMenu(viewer, MENU_ADMIN); + } + break; + } + //fallthrough + case MENU_SERVERS: + if (!cluster->servers) + { + QW_StuffcmdToViewer(viewer, "echo Please enter a server ip\nmessagemode\n"); + strcpy(viewer->expectcommand, "insecadddemo"); + } + else + { + if (viewer->menuop < 0) + viewer->menuop = 0; + i = 0; + min = viewer->menuop - 10; + if (min < 0) + min = 0; + for (sv = cluster->servers; sv && inext, i++) + {//skip over the early connections. + } + min+=20; + for (; sv && i < min; sv = sv->next, i++) + { + if (i == viewer->menuop) + { + /*if (sv->parsingconnectiondata || !sv->modellist[1].name[0]) + { + QW_PrintfToViewer(viewer, "But that stream isn't connected\n"); + } + else*/ + { + QW_SetViewersServer(cluster, viewer, sv); + QW_SetMenu(viewer, MENU_NONE); + viewer->thinksitsconnected = false; + } + break; + } + } + } + break; + case MENU_ADMIN: + i = 0; + if (i++ == viewer->menuop) + { //connection stuff + QW_SetMenu(viewer, MENU_ADMINSERVER); + } + if (i++ == viewer->menuop) + { //qw port + QW_StuffcmdToViewer(viewer, "echo You will need to reconnect\n"); + cluster->qwlistenportnum += (buttonnum<0)?-1:1; + } + if (i++ == viewer->menuop) + { //hostname + strcpy(viewer->expectcommand, "hostname"); + QW_StuffcmdToViewer(viewer, "echo Please enter the new hostname\nmessagemode\n"); + } + if (i++ == viewer->menuop) + { //master + strcpy(viewer->expectcommand, "master"); + QW_StuffcmdToViewer(viewer, "echo Please enter the master dns or ip\necho Enter '.' for masterless mode\nmessagemode\n"); + } + if (i++ == viewer->menuop) + { //password + strcpy(viewer->expectcommand, "password"); + QW_StuffcmdToViewer(viewer, "echo Please enter the new rcon password\nmessagemode\n"); + } + if (i++ == viewer->menuop) + { //add server + strcpy(viewer->expectcommand, "messagemode"); + QW_StuffcmdToViewer(viewer, "echo Please enter the new qtv server dns or ip\naddserver\n"); + } + if (i++ == viewer->menuop) + { //add demo + strcpy(viewer->expectcommand, "adddemo"); + QW_StuffcmdToViewer(viewer, "echo Please enter the name of the demo to play\nmessagemode\n"); + } + if (i++ == viewer->menuop) + { //choke + cluster->chokeonnotupdated ^= 1; + } + if (i++ == viewer->menuop) + { //late forwarding + cluster->lateforward ^= 1; + } + if (i++ == viewer->menuop) + { //no talking + cluster->notalking ^= 1; + } + if (i++ == viewer->menuop) + { //nobsp + cluster->nobsp ^= 1; + } + if (i++ == viewer->menuop) + { //back + QW_SetMenu(viewer, MENU_NONE); + } + + break; + } +} + +void WriteStringSelection(netmsg_t *b, qboolean selected, const char *str) +{ + if (selected) + { + WriteByte(b, 13); + while(*str) + WriteByte(b, 128|*str++); + } + else + { + WriteByte(b, ' '); + while(*str) + WriteByte(b, *str++); + } +} + +void Menu_Draw(cluster_t *cluster, viewer_t *viewer) +{ + char buffer[2048]; + char str[64]; + sv_t *sv; + int i, min; + unsigned char *s; + + netmsg_t m; + + if (viewer->backbuffered) + return; + + if (viewer->menunum == MENU_FORWARDING) + return; + + if (viewer->menuspamtime > cluster->curtime && viewer->menuspamtime < cluster->curtime + CENTERTIME*2000) + return; + viewer->menuspamtime = cluster->curtime + CENTERTIME*1000; + + InitNetMsg(&m, buffer, sizeof(buffer)); + + WriteByte(&m, svc_centerprint); + + sprintf(str, "FTEQTV build %i\n", cluster->buildnumber); + WriteString2(&m, str); + WriteString2(&m, "www.FTEQW.com\n"); + WriteString2(&m, "-------------\n"); + + if (strcmp(cluster->hostname, DEFAULT_HOSTNAME)) + WriteString2(&m, cluster->hostname); + + switch(viewer->menunum) + { + default: + WriteString2(&m, "bad menu"); + break; + + case MENU_MAIN: + { + 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; + while (viewer->menuop >= MENU_MAIN_ITEMCOUNT) + viewer->menuop -= MENU_MAIN_ITEMCOUNT; + i = viewer->menuop; + + WriteStringSelection(&m, i==MENU_MAIN_STREAMS, "Streams "); + WriteStringSelection(&m, i==MENU_MAIN_CLIENTLIST, "Client List "); + WriteByte(&m, '\n'); + WriteStringSelection(&m, i==MENU_MAIN_NEWSTREAM, "New Stream "); + WriteStringSelection(&m, i==MENU_MAIN_DEMOS, "Demos "); + WriteByte(&m, '\n'); + WriteStringSelection(&m, i==MENU_MAIN_SERVERBROWSER,"Server Browser "); + WriteStringSelection(&m, i==MENU_MAIN_ADMIN, "Admin "); + WriteByte(&m, '\n'); + WriteStringSelection(&m, i==MENU_MAIN_PREVPROX, "Previous Proxy "); + WriteStringSelection(&m, i==MENU_MAIN_NEXTPROX, "Next Proxy "); + WriteByte(&m, '\n'); + WriteStringSelection(&m, i==MENU_MAIN_HELP, "Help "); + WriteString2(&m, " "); + + 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"); + } + break; + + case MENU_CLIENTS: + { + int start; + viewer_t *v; + char *srv; + int c; + v = cluster->viewers; + + WriteString2(&m, "\nActive Clients\n\n"); + + start = viewer->menuop & ~7; + for (i = 0; i < start && v; i++) + v = v->next; + for (i = start; i < start+8 && v; i++, v = v->next) + { + for (c = strlen(v->name); c < 14; c++) + WriteByte(&m, ' '); + WriteStringSelection(&m, viewer->menuop == i, v->name); + WriteString2(&m, ": "); + if (v->server) + { + if (!v->server->sourcefile && !v->server->parsingconnectiondata) + srv = v->server->map.hostname; + else + srv = v->server->server; + } + else + srv = "None"; + for (c = 0; c < 20; c++) + { + if (*srv) + WriteByte(&m, *srv++); + else + WriteByte(&m, ' '); + } + + WriteByte(&m, '\n'); + } + for (; i < start+8; i++) + WriteByte(&m, '\n'); + } + break; + + + case MENU_DEMOS: + { + int start; + + WriteString2(&m, "\nAvailable Demos\n\n"); + + if (cluster->availdemoscount == 0) + { + WriteString2(&m, "No demos are available"); + break; + } + + if (viewer->menuop < 0) + viewer->menuop = 0; + if (viewer->menuop > cluster->availdemoscount-1) + viewer->menuop = cluster->availdemoscount-1; + + start = viewer->menuop & ~7; + for (i = start; i < start+8; i++) + { + if (i == viewer->menuop) + { + WriteByte(&m, '['); + WriteString2(&m, cluster->availdemos[i].name); + WriteByte(&m, ']'); + } + else + { + WriteString2(&m, cluster->availdemos[i].name); + } + WriteByte(&m, '\n'); + } + } + break; + + case MENU_ADMINSERVER: //per-connection options + if (viewer->server) + { + sv = viewer->server; + WriteString2(&m, "\n\nConnection Admin\n"); + WriteString2(&m, sv->map.hostname); + if (sv->sourcefile) + WriteString2(&m, " (demo)"); + WriteString2(&m, "\n\n"); + + if (viewer->menuop < 0) + viewer->menuop = 0; + + i = 0; + + WriteString2(&m, " auto disconnect"); + WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); + if (viewer->server->disconnectwhennooneiswatching == 2) + sprintf(str, "%-20s", "when server disconnects"); + else if (viewer->server->disconnectwhennooneiswatching) + sprintf(str, "%-20s", "when inactive"); + else + sprintf(str, "%-20s", "never"); + WriteString2(&m, str); + WriteString2(&m, "\n"); + + WriteString2(&m, "force disconnect"); + WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); + sprintf(str, "%-20s", "..."); + WriteString2(&m, str); + WriteString2(&m, "\n"); + + WriteString2(&m, " take control"); + WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); + sprintf(str, "%-20s", "..."); + WriteString2(&m, str); + WriteString2(&m, "\n"); + + WriteString2(&m, " back"); + WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); + sprintf(str, "%-20s", "..."); + WriteString2(&m, str); + WriteString2(&m, "\n"); + + if (viewer->menuop >= i) + viewer->menuop = i - 1; + + WriteString2(&m, "\n"); + + WriteString2(&m, " status"); + WriteString2(&m, " : "); + sprintf(str, "%-20s", viewer->server->status); + WriteString2(&m, str); + WriteString2(&m, "\n"); + + break; + } + //fallthrough + case MENU_SERVERS: //connections list + + WriteString2(&m, "\n\nServers\n\n"); + + if (!cluster->servers) + { + WriteString2(&m, "No active connections"); + } + else + { + if (viewer->menuop < 0) + viewer->menuop = 0; + i = 0; + min = viewer->menuop - 10; + if (min < 0) + min = 0; + for (sv = cluster->servers; sv && inext, i++) + {//skip over the early connections. + } + min+=20; + for (; sv && i < min; sv = sv->next, i++) + { + //Info_ValueForKey(sv->serverinfo, "hostname", str, sizeof(str)); + //if (sv->parsingconnectiondata || !sv->modellist[1].name[0]) + // snprintf(str, sizeof(str), "%s", sv->server); + snprintf(str, sizeof(str), "%s", *sv->map.hostname?sv->map.hostname:sv->server); + + if (i == viewer->menuop) + for (s = (unsigned char *)str; *s; s++) + { + if ((unsigned)*s >= ' ') + *s = 128 | (*s&~128); + } + WriteString2(&m, str); + WriteString2(&m, "\n"); + } + } + break; + + case MENU_ADMIN: //admin menu + + WriteString2(&m, "\n\nCluster Admin\n\n"); + + if (viewer->menuop < 0) + viewer->menuop = 0; + i = 0; + + WriteString2(&m, " this connection"); + WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); + sprintf(str, "%-20s", "..."); + WriteString2(&m, str); + WriteString2(&m, "\n"); + + WriteString2(&m, " port"); + WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); + sprintf(str, "%-20i", cluster->qwlistenportnum); + WriteString2(&m, str); + WriteString2(&m, "\n"); + + WriteString2(&m, " hostname"); + WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); + sprintf(str, "%-20s", cluster->hostname); + WriteString2(&m, str); + WriteString2(&m, "\n"); + + WriteString2(&m, " master"); + WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); + sprintf(str, "%-20s", cluster->master); + WriteString2(&m, str); + WriteString2(&m, "\n"); + + WriteString2(&m, " password"); + WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); + sprintf(str, "%-20s", "..."); + WriteString2(&m, str); + WriteString2(&m, "\n"); + + WriteString2(&m, " add server"); + WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); + sprintf(str, "%-20s", "..."); + WriteString2(&m, str); + WriteString2(&m, "\n"); + + WriteString2(&m, " add demo"); + WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); + sprintf(str, "%-20s", "..."); + WriteString2(&m, str); + WriteString2(&m, "\n"); + + WriteString2(&m, " choke"); + WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); + sprintf(str, "%-20s", cluster->chokeonnotupdated?"yes":"no"); + WriteString2(&m, str); + WriteString2(&m, "\n"); + + WriteString2(&m, "delay forwarding"); + WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); + sprintf(str, "%-20s", cluster->lateforward?"yes":"no"); + WriteString2(&m, str); + WriteString2(&m, "\n"); + + WriteString2(&m, " talking"); + WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); + sprintf(str, "%-20s", cluster->notalking?"no":"yes"); + WriteString2(&m, str); + WriteString2(&m, "\n"); + + WriteString2(&m, " nobsp"); + WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); + sprintf(str, "%-20s", cluster->nobsp?"yes":"no"); + WriteString2(&m, str); + WriteString2(&m, "\n"); + + WriteString2(&m, " back"); + WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); + sprintf(str, "%-20s", "..."); + WriteString2(&m, str); + WriteString2(&m, "\n"); + + if (viewer->menuop >= i) + viewer->menuop = i - 1; + break; + } + + + WriteByte(&m, 0); + SendBufferToViewer(viewer, m.data, m.cursize, true); +} \ No newline at end of file diff --git a/fteqtv/parse.c b/fteqtv/parse.c index 79dfb2b4c..a712271ad 100644 --- a/fteqtv/parse.c +++ b/fteqtv/parse.c @@ -114,10 +114,12 @@ void ConnectionData(sv_t *tv, void *buffer, int length, int to, unsigned int pla static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playermask) { - int i; int protocol; viewer_t *v; + //free the old map state + QTV_CleanupMap(tv); + protocol = ReadLong(m); if (protocol != PROTOCOL_VERSION) { @@ -129,37 +131,37 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma tv->clservercount = ReadLong(m); //we don't care about server's servercount, it's all reliable data anyway. - tv->trackplayer = -1; + tv->map.trackplayer = -1; - ReadString(m, tv->gamedir, sizeof(tv->gamedir)); + ReadString(m, tv->map.gamedir, sizeof(tv->map.gamedir)); if (tv->usequakeworldprotocols) - tv->thisplayer = ReadByte(m)&~128; + tv->map.thisplayer = ReadByte(m)&~128; else { - tv->thisplayer = MAX_CLIENTS-1; + tv->map.thisplayer = MAX_CLIENTS-1; /*tv->servertime =*/ ReadFloat(m); } if (tv->controller) - tv->controller->thisplayer = tv->thisplayer; - ReadString(m, tv->mapname, sizeof(tv->mapname)); + tv->controller->thisplayer = tv->map.thisplayer; + ReadString(m, tv->map.mapname, sizeof(tv->map.mapname)); - QTV_Printf(tv, "Gamedir: %s\n", tv->gamedir); + QTV_Printf(tv, "Gamedir: %s\n", tv->map.gamedir); QTV_Printf(tv, "---------------------\n"); - Sys_Printf(tv->cluster, "Stream %i: %s\n", tv->streamid, tv->mapname); + Sys_Printf(tv->cluster, "Stream %i: %s\n", tv->streamid, tv->map.mapname); QTV_Printf(tv, "---------------------\n"); // get the movevars - tv->movevars.gravity = ReadFloat(m); - tv->movevars.stopspeed = ReadFloat(m); - tv->movevars.maxspeed = ReadFloat(m); - tv->movevars.spectatormaxspeed = ReadFloat(m); - tv->movevars.accelerate = ReadFloat(m); - tv->movevars.airaccelerate = ReadFloat(m); - tv->movevars.wateraccelerate = ReadFloat(m); - tv->movevars.friction = ReadFloat(m); - tv->movevars.waterfriction = ReadFloat(m); - tv->movevars.entgrav = ReadFloat(m); + tv->map.movevars.gravity = ReadFloat(m); + tv->map.movevars.stopspeed = ReadFloat(m); + tv->map.movevars.maxspeed = ReadFloat(m); + tv->map.movevars.spectatormaxspeed = ReadFloat(m); + tv->map.movevars.accelerate = ReadFloat(m); + tv->map.movevars.airaccelerate = ReadFloat(m); + tv->map.movevars.wateraccelerate = ReadFloat(m); + tv->map.movevars.friction = ReadFloat(m); + tv->map.movevars.waterfriction = ReadFloat(m); + tv->map.movevars.entgrav = ReadFloat(m); for (v = tv->cluster->viewers; v; v = v->next) { @@ -167,23 +169,6 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma v->thinksitsconnected = false; } -// tv->maxents = 0; //clear these - tv->spawnstatic_count = 0; - memset(tv->modellist, 0, sizeof(tv->modellist)); - memset(tv->soundlist, 0, sizeof(tv->soundlist)); - memset(tv->lightstyle, 0, sizeof(tv->lightstyle)); - tv->staticsound_count = 0; - memset(tv->staticsound, 0, sizeof(tv->staticsound)); - - memset(tv->players, 0, sizeof(tv->players)); - memset(tv->entity, 0, sizeof(tv->entity)); //for the baselines - - - for (i = 0; i < MAX_ENTITY_FRAMES; i++) - { - tv->frame[i].numents = 0; - } - if (!tv->controller && tv->usequakeworldprotocols) { tv->netchan.message.cursize = 0; //mvdsv sucks @@ -201,13 +186,13 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma static void ParseCDTrack(sv_t *tv, netmsg_t *m, int to, unsigned int mask) { char nqversion[3]; - tv->cdtrack = ReadByte(m); + tv->map.cdtrack = ReadByte(m); ConnectionData(tv, (void*)((char*)m->data+m->startpos), m->readpos - m->startpos, to, mask, QW); nqversion[0] = svc_cdtrack; - nqversion[1] = tv->cdtrack; - nqversion[2] = tv->cdtrack; + nqversion[1] = tv->map.cdtrack; + nqversion[2] = tv->map.cdtrack; ConnectionData(tv, nqversion, 3, to, mask, NQ); } static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) @@ -269,9 +254,9 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) text[strlen(text)-1] = '\0'; //copy over the server's serverinfo - strlcpy(tv->serverinfo, text+16, sizeof(tv->serverinfo)); + strlcpy(tv->map.serverinfo, text+16, sizeof(tv->map.serverinfo)); - Info_ValueForKey(tv->serverinfo, "*qtv", value, sizeof(value)); + Info_ValueForKey(tv->map.serverinfo, "*qtv", value, sizeof(value)); if (*value) { fromproxy = true; @@ -281,13 +266,13 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) fromproxy = false; //add on our extra infos - Info_SetValueForStarKey(tv->serverinfo, "*qtv", VERSION, sizeof(tv->serverinfo)); - Info_SetValueForStarKey(tv->serverinfo, "*z_ext", Z_EXT_STRING, sizeof(tv->serverinfo)); + Info_SetValueForStarKey(tv->map.serverinfo, "*qtv", VERSION, sizeof(tv->map.serverinfo)); + Info_SetValueForStarKey(tv->map.serverinfo, "*z_ext", Z_EXT_STRING, sizeof(tv->map.serverinfo)); - Info_ValueForKey(tv->serverinfo, "hostname", tv->hostname, sizeof(tv->hostname)); + Info_ValueForKey(tv->map.serverinfo, "hostname", tv->map.hostname, sizeof(tv->map.hostname)); //change the hostname (the qtv's hostname with the server's hostname in brackets) - Info_ValueForKey(tv->serverinfo, "hostname", value, sizeof(value)); + Info_ValueForKey(tv->map.serverinfo, "hostname", value, sizeof(value)); if (fromproxy && strchr(value, '(') && value[strlen(value)-1] == ')') //already has brackets { //the fromproxy check is because it's fairly common to find a qw server with brackets after it's name. char *s; @@ -301,7 +286,7 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) else snprintf(text, sizeof(text), "%s (live: %s)", tv->cluster->hostname, value); } - Info_SetValueForStarKey(tv->serverinfo, "hostname", text, sizeof(tv->serverinfo)); + Info_SetValueForStarKey(tv->map.serverinfo, "hostname", text, sizeof(tv->map.serverinfo)); if (tv->controller && (tv->controller->netchan.isnqprotocol == false)) SendBufferToViewer(tv->controller, (char*)m->data+m->startpos, m->readpos - m->startpos, true); @@ -372,7 +357,7 @@ static void ParseSetInfo(sv_t *tv, netmsg_t *m) ReadString(m, value, sizeof(value)); if (pnum < MAX_CLIENTS) - Info_SetValueForStarKey(tv->players[pnum].userinfo, key, value, sizeof(tv->players[pnum].userinfo)); + Info_SetValueForStarKey(tv->map.players[pnum].userinfo, key, value, sizeof(tv->map.players[pnum].userinfo)); ConnectionData(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, dem_all, (unsigned)-1, QW); } @@ -385,7 +370,7 @@ static void ParseServerinfo(sv_t *tv, netmsg_t *m) ReadString(m, value, sizeof(value)); 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)); + Info_SetValueForStarKey(tv->map.serverinfo, key, value, sizeof(tv->map.serverinfo)); ConnectionData(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, dem_all, (unsigned)-1, QW); } @@ -500,27 +485,27 @@ static void ParseBaseline(sv_t *tv, netmsg_t *m, int to, unsigned int mask) ParseError(m); return; } - ParseEntityState(&tv->entity[entnum].baseline, m); + ParseEntityState(&tv->map.entity[entnum].baseline, m); ConnectionData(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1); } static void ParseStaticSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask) { - if (tv->staticsound_count == MAX_STATICSOUNDS) + if (tv->map.staticsound_count == MAX_STATICSOUNDS) { - tv->staticsound_count--; // don't be fatal. + tv->map.staticsound_count--; // don't be fatal. Sys_Printf(tv->cluster, "Too many static sounds\n"); } - tv->staticsound[tv->staticsound_count].origin[0] = ReadShort(m); - tv->staticsound[tv->staticsound_count].origin[1] = ReadShort(m); - tv->staticsound[tv->staticsound_count].origin[2] = ReadShort(m); - tv->staticsound[tv->staticsound_count].soundindex = ReadByte(m); - tv->staticsound[tv->staticsound_count].volume = ReadByte(m); - tv->staticsound[tv->staticsound_count].attenuation = ReadByte(m); + tv->map.staticsound[tv->map.staticsound_count].origin[0] = ReadShort(m); + tv->map.staticsound[tv->map.staticsound_count].origin[1] = ReadShort(m); + tv->map.staticsound[tv->map.staticsound_count].origin[2] = ReadShort(m); + tv->map.staticsound[tv->map.staticsound_count].soundindex = ReadByte(m); + tv->map.staticsound[tv->map.staticsound_count].volume = ReadByte(m); + tv->map.staticsound[tv->map.staticsound_count].attenuation = ReadByte(m); - tv->staticsound_count++; + tv->map.staticsound_count++; ConnectionData(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1); } @@ -539,15 +524,15 @@ static void ParseIntermission(sv_t *tv, netmsg_t *m, int to, unsigned int mask) void ParseSpawnStatic(sv_t *tv, netmsg_t *m, int to, unsigned int mask) { - if (tv->spawnstatic_count == MAX_STATICENTITIES) + if (tv->map.spawnstatic_count == MAX_STATICENTITIES) { - tv->spawnstatic_count--; // don't be fatal. + tv->map.spawnstatic_count--; // don't be fatal. Sys_Printf(tv->cluster, "Too many static entities\n"); } - ParseEntityState(&tv->spawnstatic[tv->spawnstatic_count], m); + ParseEntityState(&tv->map.spawnstatic[tv->map.spawnstatic_count], m); - tv->spawnstatic_count++; + tv->map.spawnstatic_count++; ConnectionData(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1); } @@ -565,7 +550,7 @@ static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers) for (i = 0; i < MAX_CLIENTS; i++) { //hide players //they'll be sent after this packet. - tv->players[i].active = false; + tv->map.players[i].active = false; } } @@ -575,18 +560,18 @@ static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers) num = 0; // don't be fatal. Sys_Printf(tv->cluster, "Too many svc_playerinfos, wrapping\n"); } - tv->players[num].old = tv->players[num].current; + tv->map.players[num].old = tv->map.players[num].current; if (tv->usequakeworldprotocols) { - tv->players[num].old = tv->players[num].current; + tv->map.players[num].old = tv->map.players[num].current; flags = (unsigned short)ReadShort (m); - tv->players[num].current.origin[0] = ReadShort (m); - tv->players[num].current.origin[1] = ReadShort (m); - tv->players[num].current.origin[2] = ReadShort (m); + tv->map.players[num].current.origin[0] = ReadShort (m); + tv->map.players[num].current.origin[1] = ReadShort (m); + tv->map.players[num].current.origin[2] = ReadShort (m); - tv->players[num].current.frame = ReadByte(m); + tv->map.players[num].current.frame = ReadByte(m); if (flags & PF_MSEC) ReadByte (m); @@ -594,100 +579,100 @@ static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers) if (flags & PF_COMMAND) { ReadDeltaUsercmd(m, &nullcmd, &nonnullcmd); - tv->players[num].current.angles[0] = nonnullcmd.angles[0]; - tv->players[num].current.angles[1] = nonnullcmd.angles[1]; - tv->players[num].current.angles[2] = nonnullcmd.angles[2]; + tv->map.players[num].current.angles[0] = nonnullcmd.angles[0]; + tv->map.players[num].current.angles[1] = nonnullcmd.angles[1]; + tv->map.players[num].current.angles[2] = nonnullcmd.angles[2]; } else { //the only reason we'd not get a command is if it's us. if (tv->controller) { - tv->players[num].current.angles[0] = tv->controller->ucmds[2].angles[0]; - tv->players[num].current.angles[1] = tv->controller->ucmds[2].angles[1]; - tv->players[num].current.angles[2] = tv->controller->ucmds[2].angles[2]; + tv->map.players[num].current.angles[0] = tv->controller->ucmds[2].angles[0]; + tv->map.players[num].current.angles[1] = tv->controller->ucmds[2].angles[1]; + tv->map.players[num].current.angles[2] = tv->controller->ucmds[2].angles[2]; } else { - tv->players[num].current.angles[0] = tv->proxyplayerangles[0]/360*65535; - tv->players[num].current.angles[1] = tv->proxyplayerangles[1]/360*65535; - tv->players[num].current.angles[2] = tv->proxyplayerangles[2]/360*65535; + tv->map.players[num].current.angles[0] = tv->proxyplayerangles[0]/360*65535; + tv->map.players[num].current.angles[1] = tv->proxyplayerangles[1]/360*65535; + tv->map.players[num].current.angles[2] = tv->proxyplayerangles[2]/360*65535; } } for (i=0 ; i<3 ; i++) { if (flags & (PF_VELOCITY1<players[num].current.velocity[i] = ReadShort(m); + tv->map.players[num].current.velocity[i] = ReadShort(m); else - tv->players[num].current.velocity[i] = 0; + tv->map.players[num].current.velocity[i] = 0; } - tv->players[num].gibbed = !!(flags & PF_GIB); - tv->players[num].dead = !!(flags & PF_DEAD); + tv->map.players[num].gibbed = !!(flags & PF_GIB); + tv->map.players[num].dead = !!(flags & PF_DEAD); if (flags & PF_MODEL) - tv->players[num].current.modelindex = ReadByte (m); + tv->map.players[num].current.modelindex = ReadByte (m); else - tv->players[num].current.modelindex = tv->modelindex_player; + tv->map.players[num].current.modelindex = tv->map.modelindex_player; if (flags & PF_SKINNUM) - tv->players[num].current.skinnum = ReadByte (m); + tv->map.players[num].current.skinnum = ReadByte (m); else - tv->players[num].current.skinnum = 0; + tv->map.players[num].current.skinnum = 0; if (flags & PF_EFFECTS) - tv->players[num].current.effects = ReadByte (m); + tv->map.players[num].current.effects = ReadByte (m); else - tv->players[num].current.effects = 0; + tv->map.players[num].current.effects = 0; if (flags & PF_WEAPONFRAME) - tv->players[num].current.weaponframe = ReadByte (m); + tv->map.players[num].current.weaponframe = ReadByte (m); else - tv->players[num].current.weaponframe = 0; + tv->map.players[num].current.weaponframe = 0; - tv->players[num].active = true; + tv->map.players[num].active = true; } else { flags = ReadShort(m); - tv->players[num].gibbed = !!(flags & DF_GIB); - tv->players[num].dead = !!(flags & DF_DEAD); - tv->players[num].current.frame = ReadByte(m); + tv->map.players[num].gibbed = !!(flags & DF_GIB); + tv->map.players[num].dead = !!(flags & DF_DEAD); + tv->map.players[num].current.frame = ReadByte(m); for (i = 0; i < 3; i++) { if (flags & (DF_ORIGIN << i)) - tv->players[num].current.origin[i] = ReadShort (m); + tv->map.players[num].current.origin[i] = ReadShort (m); } for (i = 0; i < 3; i++) { if (flags & (DF_ANGLES << i)) { - tv->players[num].current.angles[i] = ReadShort(m); + tv->map.players[num].current.angles[i] = ReadShort(m); } } if (flags & DF_MODEL) - tv->players[num].current.modelindex = ReadByte (m); + tv->map.players[num].current.modelindex = ReadByte (m); if (flags & DF_SKINNUM) - tv->players[num].current.skinnum = ReadByte (m); + tv->map.players[num].current.skinnum = ReadByte (m); if (flags & DF_EFFECTS) - tv->players[num].current.effects = ReadByte (m); + tv->map.players[num].current.effects = ReadByte (m); if (flags & DF_WEAPONFRAME) - tv->players[num].current.weaponframe = ReadByte (m); + tv->map.players[num].current.weaponframe = ReadByte (m); - tv->players[num].active = true; + tv->map.players[num].active = true; } - tv->players[num].leafcount = BSP_SphereLeafNums(tv->bsp, MAX_ENTITY_LEAFS, tv->players[num].leafs, - tv->players[num].current.origin[0]/8.0f, - tv->players[num].current.origin[1]/8.0f, - tv->players[num].current.origin[2]/8.0f, 32); + tv->map.players[num].leafcount = BSP_SphereLeafNums(tv->map.bsp, MAX_ENTITY_LEAFS, tv->map.players[num].leafs, + tv->map.players[num].current.origin[0]/8.0f, + tv->map.players[num].current.origin[1]/8.0f, + tv->map.players[num].current.origin[2]/8.0f, 32); } static int readentitynum(netmsg_t *m, unsigned int *retflags) @@ -756,7 +741,7 @@ static void ParseEntityDelta(sv_t *tv, netmsg_t *m, entity_state_t *old, entity_ if (forcerelink || (flags & (U_ORIGIN1|U_ORIGIN2|U_ORIGIN3|U_MODEL))) { ent->leafcount = - BSP_SphereLeafNums(tv->bsp, MAX_ENTITY_LEAFS, ent->leafs, + BSP_SphereLeafNums(tv->map.bsp, MAX_ENTITY_LEAFS, ent->leafs, new->origin[0]/8.0f, new->origin[1]/8.0f, new->origin[2]/8.0f, 32); @@ -808,7 +793,7 @@ static void ParsePacketEntities(sv_t *tv, netmsg_t *m, int deltaframe) viewer_t *v; - tv->nailcount = 0; + tv->map.nailcount = 0; tv->physicstime = tv->parsetime; @@ -830,7 +815,7 @@ static void ParsePacketEntities(sv_t *tv, netmsg_t *m, int deltaframe) if (tv->usequakeworldprotocols) { - newframe = &tv->frame[tv->netchan.incoming_sequence & (ENTITY_FRAMES-1)]; + newframe = &tv->map.frame[tv->netchan.incoming_sequence & (ENTITY_FRAMES-1)]; if (tv->netchan.outgoing_sequence - tv->netchan.incoming_sequence >= ENTITY_FRAMES - 1) { @@ -844,11 +829,11 @@ static void ParsePacketEntities(sv_t *tv, netmsg_t *m, int deltaframe) { deltaframe = tv->netchan.incoming_sequence & (ENTITY_FRAMES-1); tv->netchan.incoming_sequence++; - newframe = &tv->frame[tv->netchan.incoming_sequence & (ENTITY_FRAMES-1)]; + newframe = &tv->map.frame[tv->netchan.incoming_sequence & (ENTITY_FRAMES-1)]; } if (deltaframe != -1) { - oldframe = &tv->frame[deltaframe]; + oldframe = &tv->map.frame[deltaframe]; oldcount = oldframe->numents; } else @@ -915,7 +900,7 @@ static void ParsePacketEntities(sv_t *tv, netmsg_t *m, int deltaframe) if (!ExpandFrame(newindex, newframe)) break; - ParseEntityDelta(tv, m, &tv->entity[newnum].baseline, &newframe->ents[newindex], flags, &tv->entity[newnum], true); + ParseEntityDelta(tv, m, &tv->map.entity[newnum].baseline, &newframe->ents[newindex], flags, &tv->map.entity[newnum], true); newframe->entnums[newindex] = newnum; newindex++; } @@ -930,7 +915,7 @@ static void ParsePacketEntities(sv_t *tv, netmsg_t *m, int deltaframe) //printf("Propogate (changed)\n"); if (!ExpandFrame(newindex, newframe)) break; - ParseEntityDelta(tv, m, &oldframe->ents[oldindex], &newframe->ents[newindex], flags, &tv->entity[newnum], false); + ParseEntityDelta(tv, m, &oldframe->ents[oldindex], &newframe->ents[newindex], flags, &tv->map.entity[newnum], false); newframe->entnums[newindex] = newnum; newindex++; oldindex++; @@ -1016,7 +1001,7 @@ static void ParseUpdatePing(sv_t *tv, netmsg_t *m, int to, unsigned int mask) ping = ReadShort(m); if (pnum < MAX_CLIENTS) - tv->players[pnum].ping = ping; + tv->map.players[pnum].ping = ping; else Sys_Printf(tv->cluster, "svc_updateping: invalid player number\n"); @@ -1031,7 +1016,7 @@ static void ParseUpdateFrags(sv_t *tv, netmsg_t *m, int to, unsigned int mask) frags = (signed short)ReadShort(m); if (pnum < MAX_CLIENTS) - tv->players[pnum].frags = frags; + tv->map.players[pnum].frags = frags; else Sys_Printf(tv->cluster, "svc_updatefrags: invalid player number\n"); @@ -1052,7 +1037,7 @@ static void ParseUpdateStat(sv_t *tv, netmsg_t *m, int to, unsigned int mask) for (pnum = 0; pnum < MAX_CLIENTS; pnum++) { if (mask & (1<players[pnum].stats[statnum] = value; + tv->map.players[pnum].stats[statnum] = value; } } else @@ -1074,7 +1059,7 @@ static void ParseUpdateStatLong(sv_t *tv, netmsg_t *m, int to, unsigned int mask for (pnum = 0; pnum < MAX_CLIENTS; pnum++) { if (mask & (1<players[pnum].stats[statnum] = value; + tv->map.players[pnum].stats[statnum] = value; } } else @@ -1089,7 +1074,7 @@ static void ParseUpdateUserinfo(sv_t *tv, netmsg_t *m, int to, unsigned int mask pnum = ReadByte(m); ReadLong(m); if (pnum < MAX_CLIENTS) - ReadString(m, tv->players[pnum].userinfo, sizeof(tv->players[pnum].userinfo)); + ReadString(m, tv->map.players[pnum].userinfo, sizeof(tv->map.players[pnum].userinfo)); else { Sys_Printf(tv->cluster, "svc_updateuserinfo: invalid player number\n"); @@ -1110,7 +1095,7 @@ static void ParsePacketloss(sv_t *tv, netmsg_t *m, int to, unsigned int mask) value = ReadByte(m); if (pnum < MAX_CLIENTS) - tv->players[pnum].packetloss = value; + tv->map.players[pnum].packetloss = value; else Sys_Printf(tv->cluster, "svc_updatepl: invalid player number\n"); @@ -1126,7 +1111,7 @@ static void ParseUpdateEnterTime(sv_t *tv, netmsg_t *m, int to, unsigned int mas value = ReadFloat(m); if (pnum < MAX_CLIENTS) - tv->players[pnum].entertime = value; + tv->map.players[pnum].entertime = value; else Sys_Printf(tv->cluster, "svc_updateentertime: invalid player number\n"); @@ -1342,7 +1327,7 @@ void ParseLightstyle(sv_t *tv, netmsg_t *m) int style; style = ReadByte(m); if (style < MAX_LIGHTSTYLES) - ReadString(m, tv->lightstyle[style].name, sizeof(tv->lightstyle[style].name)); + ReadString(m, tv->map.lightstyle[style].name, sizeof(tv->map.lightstyle[style].name)); else { Sys_Printf(tv->cluster, "svc_lightstyle: invalid lightstyle index (%i)\n", style); @@ -1359,7 +1344,7 @@ void ParseNails(sv_t *tv, netmsg_t *m, qboolean nails2) int count; int i; count = (unsigned char)ReadByte(m); - while(count > sizeof(tv->nails) / sizeof(tv->nails[0])) + while(count > sizeof(tv->map.nails) / sizeof(tv->map.nails[0])) {//they sent too many, suck it out. count--; if (nails2) @@ -1368,15 +1353,15 @@ void ParseNails(sv_t *tv, netmsg_t *m, qboolean nails2) ReadByte(m); } - tv->nailcount = count; + tv->map.nailcount = count; while(count-- > 0) { if (nails2) - tv->nails[count].number = ReadByte(m); + tv->map.nails[count].number = ReadByte(m); else - tv->nails[count].number = count; + tv->map.nails[count].number = count; for (i = 0; i < 6; i++) - tv->nails[count].bits[i] = ReadByte(m); + tv->map.nails[count].bits[i] = ReadByte(m); } } @@ -1417,20 +1402,20 @@ void ParseDownload(sv_t *tv, netmsg_t *m) fclose(tv->downloadfile); tv->downloadfile = NULL; - snprintf(buffer, sizeof(buffer), "%s/%s", (tv->gamedir&&*tv->gamedir)?tv->gamedir:"id1", tv->modellist[1].name); + snprintf(buffer, sizeof(buffer), "%s/%s", (tv->map.gamedir&&*tv->map.gamedir)?tv->map.gamedir:"id1", tv->map.modellist[1].name); rename(tv->downloadname, buffer); Sys_Printf(tv->cluster, "Download complete\n"); - tv->bsp = BSP_LoadModel(tv->cluster, tv->gamedir, tv->modellist[1].name); - if (!tv->bsp) + tv->map.bsp = BSP_LoadModel(tv->cluster, tv->map.gamedir, tv->map.modellist[1].name); + if (!tv->map.bsp) { Sys_Printf(tv->cluster, "Failed to read BSP\n"); tv->errored = ERR_PERMANENT; } else { - SendClientCommand(tv, "prespawn %i 0 %i\n", tv->clservercount, LittleLong(BSP_Checksum(tv->bsp))); + SendClientCommand(tv, "prespawn %i 0 %i\n", tv->clservercount, LittleLong(BSP_Checksum(tv->map.bsp))); strcpy(tv->status, "Prespawning\n"); } } @@ -1441,7 +1426,7 @@ void ParseDownload(sv_t *tv, netmsg_t *m) } } -void ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask) +void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) { int i; netmsg_t buf; @@ -1587,7 +1572,7 @@ void ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask) break; case svc_setpause: // [qbyte] on / off - tv->ispaused = ReadByte(&buf); + tv->map.ispaused = ReadByte(&buf); Multicast(tv, (char*)buf.data+buf.startpos, buf.readpos - buf.startpos, dem_read, (unsigned)-1, Q1); break; @@ -1664,36 +1649,36 @@ void ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask) break; case svc_modellist: - i = ParseList(tv, &buf, tv->modellist, to, mask); + i = ParseList(tv, &buf, tv->map.modellist, to, mask); if (!i) { int j; - if (tv->bsp) - BSP_Free(tv->bsp); + if (tv->map.bsp) + BSP_Free(tv->map.bsp); if (tv->cluster->nobsp)// || !tv->usequkeworldprotocols) - tv->bsp = NULL; + tv->map.bsp = NULL; else - tv->bsp = BSP_LoadModel(tv->cluster, tv->gamedir, tv->modellist[1].name); + tv->map.bsp = BSP_LoadModel(tv->cluster, tv->map.gamedir, tv->map.modellist[1].name); - tv->numinlines = 0; + tv->map.numinlines = 0; for (j = 2; j < 256; j++) { - if (*tv->modellist[j].name != '*') + if (*tv->map.modellist[j].name != '*') break; - tv->numinlines = j; + tv->map.numinlines = j; } - tv->modelindex_player = 0; - tv->modelindex_spike = 0; + tv->map.modelindex_player = 0; + tv->map.modelindex_spike = 0; for (j = 2; j < 256; j++) { - if (!*tv->modellist[j].name) + if (!*tv->map.modellist[j].name) break; - if (!strcmp(tv->modellist[j].name, "progs/player.mdl")) - tv->modelindex_player = j; - if (!strcmp(tv->modellist[j].name, "progs/spike.mdl")) - tv->modelindex_spike = j; + if (!strcmp(tv->map.modellist[j].name, "progs/player.mdl")) + tv->map.modelindex_player = j; + if (!strcmp(tv->map.modellist[j].name, "progs/spike.mdl")) + tv->map.modelindex_spike = j; } strcpy(tv->status, "Prespawning\n"); } @@ -1702,7 +1687,7 @@ void ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask) { if (i) SendClientCommand(tv, "modellist %i %i\n", tv->clservercount, i); - else if (!tv->bsp && !tv->cluster->nobsp) + else if (!tv->map.bsp && !tv->cluster->nobsp) { if (tv->downloadfile) { @@ -1711,29 +1696,32 @@ void ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask) Sys_Printf(tv->cluster, "Was already downloading %s\nOld download canceled\n", tv->downloadname); tv->downloadfile = NULL; } - snprintf(tv->downloadname, sizeof(tv->downloadname), "%s/%s.tmp", (tv->gamedir&&*tv->gamedir)?tv->gamedir:"id1", tv->modellist[1].name); + snprintf(tv->downloadname, sizeof(tv->downloadname), "%s/%s.tmp", (tv->map.gamedir&&*tv->map.gamedir)?tv->map.gamedir:"id1", tv->map.modellist[1].name); + QTV_mkdir(tv->downloadname); tv->downloadfile = fopen(tv->downloadname, "wb"); if (!tv->downloadfile) { Sys_Printf(tv->cluster, "Couldn't open temporary file %s\n", tv->downloadname); + + SendClientCommand(tv, "prespawn %i 0 %i\n", tv->clservercount, LittleLong(BSP_Checksum(tv->map.bsp))); } else { strcpy(tv->status, "Downloading map\n"); Sys_Printf(tv->cluster, "Attempting download of %s\n", tv->downloadname); - SendClientCommand(tv, "download %s\n", tv->modellist[1].name); + SendClientCommand(tv, "download %s\n", tv->map.modellist[1].name); - QW_StreamPrint(tv->cluster, tv, NULL, "[QTV] Attempting map download\n"); + QW_StreamPrint(tv->cluster, tv, NULL, "[QTV] Attempting map download (%s)\n", tv->map.modellist[1].name); } } else { - SendClientCommand(tv, "prespawn %i 0 %i\n", tv->clservercount, LittleLong(BSP_Checksum(tv->bsp))); + SendClientCommand(tv, "prespawn %i 0 %i\n", tv->clservercount, LittleLong(BSP_Checksum(tv->map.bsp))); } } break; case svc_soundlist: - i = ParseList(tv, &buf, tv->soundlist, to, mask); + i = ParseList(tv, &buf, tv->map.soundlist, to, mask); if (!i) strcpy(tv->status, "Receiving modellist\n"); ConnectionData(tv, (void*)((char*)buf.data+buf.startpos), buf.readpos - buf.startpos, to, mask, QW); diff --git a/fteqtv/qtv.h b/fteqtv/qtv.h index def64c2fe..038658956 100644 --- a/fteqtv/qtv.h +++ b/fteqtv/qtv.h @@ -468,6 +468,7 @@ typedef enum { typedef enum { ERR_NONE, //stream is fine + ERR_PAUSED, ERR_RECONNECT, //stream needs to reconnect ERR_PERMANENT, //permanent error, transitioning to disabled next frame ERR_DISABLED, //stream is disabled, can be set to reconnect by admin @@ -500,36 +501,11 @@ struct sv_s { //details about a server connection (also known as stream) unsigned int parsetime; unsigned int parsespeed; - char gamedir[MAX_QPATH]; - char mapname[256]; - movevars_t movevars; - int cdtrack; - entity_t entity[MAX_ENTITIES]; - frame_t frame[MAX_ENTITY_FRAMES]; -// int maxents; - staticsound_t staticsound[MAX_STATICSOUNDS]; - int staticsound_count; - entity_state_t spawnstatic[MAX_STATICENTITIES]; - int spawnstatic_count; - filename_t lightstyle[MAX_LIGHTSTYLES]; - - char serverinfo[MAX_SERVERINFO_STRING]; - char hostname[MAX_QPATH]; - playerinfo_t players[MAX_CLIENTS]; - - filename_t modellist[MAX_MODELS]; - filename_t soundlist[MAX_SOUNDS]; - int modelindex_spike; // qw is wierd. - int modelindex_player; // qw is wierd. - FILE *downloadfile; char downloadname[256]; char status[64]; - nail_t nails[32]; - int nailcount; - qboolean silentstream; qboolean usequakeworldprotocols; @@ -538,10 +514,8 @@ struct sv_s { //details about a server connection (also known as stream) int isconnected; int clservercount; unsigned int nextsendpings; - int trackplayer; - int thisplayer; + unsigned int timeout; - qboolean ispaused; unsigned int packetratelimiter; viewer_t *controller; @@ -583,9 +557,6 @@ struct sv_s { //details about a server connection (also known as stream) cluster_t *cluster; sv_t *next; //next proxy->server connection - bsp_t *bsp; - int numinlines; - #ifdef COMMENTARY //audio stuff soundcapt_t *comentrycapture; @@ -594,6 +565,43 @@ struct sv_s { //details about a server connection (also known as stream) //options: char server[MAX_QPATH]; int streamid; + + struct mapstate_s + { + //this structure is freed+memset in QTV_CleanupMap + + bsp_t *bsp; + int numinlines; + + nail_t nails[32]; + int nailcount; + + char gamedir[MAX_QPATH]; + char mapname[256]; + movevars_t movevars; + int cdtrack; + entity_t entity[MAX_ENTITIES]; + frame_t frame[MAX_ENTITY_FRAMES]; + // int maxents; + staticsound_t staticsound[MAX_STATICSOUNDS]; + int staticsound_count; + entity_state_t spawnstatic[MAX_STATICENTITIES]; + int spawnstatic_count; + filename_t lightstyle[MAX_LIGHTSTYLES]; + + char serverinfo[MAX_SERVERINFO_STRING]; + char hostname[MAX_QPATH]; + playerinfo_t players[MAX_CLIENTS]; + + filename_t modellist[MAX_MODELS]; + filename_t soundlist[MAX_SOUNDS]; + int modelindex_spike; // qw is wierd. + int modelindex_player; // qw is wierd. + + int trackplayer; + int thisplayer; + qboolean ispaused; + } map; }; typedef struct { @@ -789,7 +797,7 @@ void Sys_Printf(cluster_t *cluster, char *fmt, ...) PRINTFWARNING(2); 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); +sv_t *QTV_NewServerConnection(cluster_t *cluster, int streamid, char *server, char *password, qboolean force, qboolean autoclose, qboolean noduplicates, qboolean query); SOCKET Net_MVDListen(int port); qboolean Net_StopFileProxy(sv_t *qtv); diff --git a/fteqtv/qtvprox.dsp b/fteqtv/qtvprox.dsp index 88ea64425..32ec6eba0 100644 --- a/fteqtv/qtvprox.dsp +++ b/fteqtv/qtvprox.dsp @@ -437,6 +437,21 @@ SOURCE=.\forward.c # End Source File # Begin Source File +SOURCE=.\libqtvc\glibc_sucks.c + +!IF "$(CFG)" == "qtvprox - Win32 Release" + +!ELSEIF "$(CFG)" == "qtvprox - Win32 Debug" + +# SUBTRACT CPP /YX /Yc /Yu + +!ELSEIF "$(CFG)" == "qtvprox - Win32 Viewer Debug" + +!ENDIF + +# End Source File +# Begin Source File + SOURCE=.\httpsv.c # End Source File # Begin Source File @@ -445,6 +460,29 @@ SOURCE=.\mdfour.c # End Source File # Begin Source File +SOURCE=.\menu.c +# End Source File +# Begin Source File + +SOURCE=.\msg.c +# End Source File +# Begin Source File + +SOURCE=.\libqtvc\msvc_sucks.c + +!IF "$(CFG)" == "qtvprox - Win32 Release" + +!ELSEIF "$(CFG)" == "qtvprox - Win32 Debug" + +# SUBTRACT CPP /YX /Yc /Yu + +!ELSEIF "$(CFG)" == "qtvprox - Win32 Viewer Debug" + +!ENDIF + +# End Source File +# Begin Source File + SOURCE=.\netchan.c !IF "$(CFG)" == "qtvprox - Win32 Release" diff --git a/fteqtv/qw.c b/fteqtv/qw.c index 7c8d55c6f..3a5e6b76f 100644 --- a/fteqtv/qw.c +++ b/fteqtv/qw.c @@ -22,8 +22,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "bsd_string.h" -#define CENTERTIME 1.5 - static const filename_t ConnectionlessModelList[] = {{""}, {"maps/start.bsp"}, {"progs/player.mdl"}, {""}}; static const filename_t ConnectionlessSoundList[] = {{""}, {""}}; @@ -48,30 +46,6 @@ void QTV_DefaultMovevars(movevars_t *vars) void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum); -#if defined(_WIN32) && !defined(__MINGW32_VERSION) -int snprintf(char *buffer, int buffersize, char *format, ...) -{ - va_list argptr; - int ret; - va_start (argptr, format); - ret = _vsnprintf (buffer, buffersize, format, argptr); - buffer[buffersize - 1] = '\0'; - va_end (argptr); - - return ret; -} -#endif -#if (defined(_WIN32) && !defined(_VC80_UPGRADE) && !defined(__MINGW32_VERSION)) -int vsnprintf(char *buffer, int buffersize, char *format, va_list argptr) -{ - int ret; - ret = _vsnprintf (buffer, buffersize, format, argptr); - buffer[buffersize - 1] = '\0'; - - return ret; -} -#endif - const usercmd_t nullcmd = {0}; #define CM_ANGLE1 (1<<0) @@ -255,7 +229,7 @@ void BuildServerData(sv_t *tv, netmsg_t *msg, int servercount, viewer_t *viewer) } else { - WriteString(msg, tv->gamedir); + WriteString(msg, tv->map.gamedir); if (!viewer) WriteFloat(msg, 0); @@ -266,26 +240,26 @@ void BuildServerData(sv_t *tv, netmsg_t *msg, int servercount, viewer_t *viewer) else WriteByte(msg, viewer->thisplayer | 128); } - WriteString(msg, tv->mapname); + WriteString(msg, tv->map.mapname); // get the movevars - WriteFloat(msg, tv->movevars.gravity); - WriteFloat(msg, tv->movevars.stopspeed); - WriteFloat(msg, tv->movevars.maxspeed); - WriteFloat(msg, tv->movevars.spectatormaxspeed); - WriteFloat(msg, tv->movevars.accelerate); - WriteFloat(msg, tv->movevars.airaccelerate); - WriteFloat(msg, tv->movevars.wateraccelerate); - WriteFloat(msg, tv->movevars.friction); - WriteFloat(msg, tv->movevars.waterfriction); - WriteFloat(msg, tv->movevars.entgrav); + WriteFloat(msg, tv->map.movevars.gravity); + WriteFloat(msg, tv->map.movevars.stopspeed); + WriteFloat(msg, tv->map.movevars.maxspeed); + WriteFloat(msg, tv->map.movevars.spectatormaxspeed); + WriteFloat(msg, tv->map.movevars.accelerate); + WriteFloat(msg, tv->map.movevars.airaccelerate); + WriteFloat(msg, tv->map.movevars.wateraccelerate); + WriteFloat(msg, tv->map.movevars.friction); + WriteFloat(msg, tv->map.movevars.waterfriction); + WriteFloat(msg, tv->map.movevars.entgrav); WriteByte(msg, svc_stufftext); WriteString2(msg, "fullserverinfo \""); - WriteString2(msg, tv->serverinfo); + WriteString2(msg, tv->map.serverinfo); WriteString(msg, "\"\n"); } } @@ -335,22 +309,22 @@ void BuildNQServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int playernum) //modellist - for (i = 1; *tv->modellist[i].name; i++) + for (i = 1; *tv->map.modellist[i].name; i++) { - WriteString(msg, tv->modellist[i].name); + WriteString(msg, tv->map.modellist[i].name); } WriteString(msg, ""); //soundlist - for (i = 1; *tv->soundlist[i].name; i++) + for (i = 1; *tv->map.soundlist[i].name; i++) { - WriteString(msg, tv->soundlist[i].name); + WriteString(msg, tv->map.soundlist[i].name); } WriteString(msg, ""); WriteByte(msg, svc_cdtrack); - WriteByte(msg, tv->cdtrack); //two of them, yeah... weird, eh? - WriteByte(msg, tv->cdtrack); + WriteByte(msg, tv->map.cdtrack); //two of them, yeah... weird, eh? + WriteByte(msg, tv->map.cdtrack); WriteByte(msg, svc_nqsetview); WriteShort(msg, 15); @@ -374,7 +348,7 @@ void SendServerData(sv_t *tv, viewer_t *viewer) InitNetMsg(&msg, buffer, viewer->netchan.maxreliablelen); if (tv && (tv->controller == viewer || !tv->controller)) - viewer->thisplayer = tv->thisplayer; + viewer->thisplayer = tv->map.thisplayer; else viewer->thisplayer = viewer->netchan.isnqprotocol?15:MAX_CLIENTS-1; if (viewer->netchan.isnqprotocol) @@ -406,16 +380,16 @@ void SendNQSpawnInfoToViewer(cluster_t *cluster, viewer_t *viewer, netmsg_t *msg { WriteByte (msg, svc_nqupdatename); WriteByte (msg, i); - Info_ValueForKey(tv->players[i].userinfo, "name", buffer, sizeof(buffer)); + Info_ValueForKey(tv->map.players[i].userinfo, "name", buffer, sizeof(buffer)); WriteString (msg, buffer); //fixme WriteByte (msg, svc_updatefrags); WriteByte (msg, i); - WriteShort (msg, tv->players[i].frags); + WriteShort (msg, tv->map.players[i].frags); - Info_ValueForKey(tv->players[i].userinfo, "bottomcolor", buffer, sizeof(buffer)); + Info_ValueForKey(tv->map.players[i].userinfo, "bottomcolor", buffer, sizeof(buffer)); colours = atoi(buffer); - Info_ValueForKey(tv->players[i].userinfo, "topcolor", buffer, sizeof(buffer)); + Info_ValueForKey(tv->map.players[i].userinfo, "topcolor", buffer, sizeof(buffer)); colours |= atoi(buffer)*16; WriteByte (msg, svc_nqupdatecolors); WriteByte (msg, i); @@ -461,10 +435,10 @@ int SendCurrentUserinfos(sv_t *tv, int cursize, netmsg_t *msg, int i, int thispl WriteString2(msg, "\\*spectator\\1\\name\\"); // Print the number of people on QTV along with the hostname - if (tv && tv->hostname[0]) + if (tv && tv->map.hostname[0]) { - if (tv->hostname[0]) - snprintf(name, sizeof(name), "[%d] %s", tv->numviewers, tv->hostname); + if (tv->map.hostname[0]) + snprintf(name, sizeof(name), "[%d] %s", tv->numviewers, tv->map.hostname); else snprintf(name, sizeof(name), "[%d] FTEQTV", tv->numviewers); } @@ -498,26 +472,26 @@ int SendCurrentUserinfos(sv_t *tv, int cursize, netmsg_t *msg, int i, int thispl } if (!tv) continue; - if (msg->cursize+cursize+strlen(tv->players[i].userinfo) > 768) + if (msg->cursize+cursize+strlen(tv->map.players[i].userinfo) > 768) { return i; } WriteByte(msg, svc_updateuserinfo); WriteByte(msg, i); WriteLong(msg, i); - WriteString(msg, tv->players[i].userinfo); + WriteString(msg, tv->map.players[i].userinfo); WriteByte(msg, svc_updatefrags); WriteByte(msg, i); - WriteShort(msg, tv->players[i].frags); + WriteShort(msg, tv->map.players[i].frags); WriteByte(msg, svc_updateping); WriteByte(msg, i); - WriteShort(msg, tv->players[i].ping); + WriteShort(msg, tv->map.players[i].ping); WriteByte(msg, svc_updatepl); WriteByte(msg, i); - WriteByte(msg, tv->players[i].packetloss); + WriteByte(msg, tv->map.players[i].packetloss); } i++; @@ -550,11 +524,11 @@ int SendCurrentBaselines(sv_t *tv, int cursize, netmsg_t *msg, int maxbuffersize return i; } - if (tv->entity[i].baseline.modelindex) + if (tv->map.entity[i].baseline.modelindex) { WriteByte(msg, svc_spawnbaseline); WriteShort(msg, i); - WriteEntityState(msg, &tv->entity[i].baseline); + WriteEntityState(msg, &tv->map.entity[i].baseline); } } @@ -567,13 +541,13 @@ int SendCurrentLightmaps(sv_t *tv, int cursize, netmsg_t *msg, int maxbuffersize for (; i < MAX_LIGHTSTYLES; i++) { - if (msg->cursize+cursize+strlen(tv->lightstyle[i].name) > maxbuffersize) + if (msg->cursize+cursize+strlen(tv->map.lightstyle[i].name) > maxbuffersize) { return i; } WriteByte(msg, svc_lightstyle); WriteByte(msg, i); - WriteString(msg, tv->lightstyle[i].name); + WriteString(msg, tv->map.lightstyle[i].name); } return i; } @@ -588,16 +562,16 @@ int SendStaticSounds(sv_t *tv, int cursize, netmsg_t *msg, int maxbuffersize, in { return i; } - if (!tv->staticsound[i].soundindex) + if (!tv->map.staticsound[i].soundindex) continue; WriteByte(msg, svc_spawnstaticsound); - WriteShort(msg, tv->staticsound[i].origin[0]); - WriteShort(msg, tv->staticsound[i].origin[1]); - WriteShort(msg, tv->staticsound[i].origin[2]); - WriteByte(msg, tv->staticsound[i].soundindex); - WriteByte(msg, tv->staticsound[i].volume); - WriteByte(msg, tv->staticsound[i].attenuation); + WriteShort(msg, tv->map.staticsound[i].origin[0]); + WriteShort(msg, tv->map.staticsound[i].origin[1]); + WriteShort(msg, tv->map.staticsound[i].origin[2]); + WriteByte(msg, tv->map.staticsound[i].soundindex); + WriteByte(msg, tv->map.staticsound[i].volume); + WriteByte(msg, tv->map.staticsound[i].attenuation); } return i; @@ -613,11 +587,11 @@ int SendStaticEntities(sv_t *tv, int cursize, netmsg_t *msg, int maxbuffersize, { return i; } - if (!tv->spawnstatic[i].modelindex) + if (!tv->map.spawnstatic[i].modelindex) continue; WriteByte(msg, svc_spawnstatic); - WriteEntityState(msg, &tv->spawnstatic[i]); + WriteEntityState(msg, &tv->map.spawnstatic[i]); } return i; @@ -720,7 +694,7 @@ void QW_SetViewersServer(cluster_t *cluster, viewer_t *viewer, sv_t *sv) { if (sv) { - snprintf(buffer, sizeof(buffer), "%cQTV%c%s leaves to watch %s (%i)\n", 91+128, 93+128, viewer->name, *sv->hostname?sv->hostname:sv->server, sv->streamid); + snprintf(buffer, sizeof(buffer), "%cQTV%c%s leaves to watch %s (%i)\n", 91+128, 93+128, viewer->name, *sv->map.hostname?sv->map.hostname:sv->server, sv->streamid); QW_StreamPrint(cluster, oldserver, viewer, buffer); } snprintf(buffer, sizeof(buffer), "%cQTV%c%s joins the stream\n", 91+128, 93+128, viewer->name); @@ -900,7 +874,7 @@ void NewNQClient(cluster_t *cluster, netadr_t *addr) if (cluster->numservers == 1) { initialserver = cluster->servers; - if (!initialserver->modellist[1].name[0]) + if (!initialserver->map.modellist[1].name[0]) initialserver = NULL; //damn, that server isn't ready } @@ -968,7 +942,7 @@ void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage) if (cluster->nouserconnects && cluster->numservers == 1) { initialserver = cluster->servers; - if (!initialserver->modellist[1].name[0]) + if (!initialserver->map.modellist[1].name[0]) initialserver = NULL; //damn, that server isn't ready } @@ -1099,14 +1073,14 @@ void QTV_Status(cluster_t *cluster, netadr_t *from) { //show this server's info sv = cluster->servers; - WriteString2(&msg, sv->serverinfo); + WriteString2(&msg, sv->map.serverinfo); WriteString2(&msg, "\n"); for (i = 0;i < MAX_CLIENTS; i++) { - if (i == sv->thisplayer) + if (i == sv->map.thisplayer) continue; - if (!sv->players[i].active) + if (!sv->map.players[i].active) continue; //userid sprintf(elem, "%i", i); @@ -1114,7 +1088,7 @@ void QTV_Status(cluster_t *cluster, netadr_t *from) WriteString2(&msg, " "); //frags - sprintf(elem, "%i", sv->players[i].frags); + sprintf(elem, "%i", sv->map.players[i].frags); WriteString2(&msg, elem); WriteString2(&msg, " "); @@ -1124,30 +1098,30 @@ void QTV_Status(cluster_t *cluster, netadr_t *from) WriteString2(&msg, " "); //ping - sprintf(elem, "%i", sv->players[i].ping); + sprintf(elem, "%i", sv->map.players[i].ping); WriteString2(&msg, elem); WriteString2(&msg, " "); //name - Info_ValueForKey(sv->players[i].userinfo, "name", elem, sizeof(elem)); + Info_ValueForKey(sv->map.players[i].userinfo, "name", elem, sizeof(elem)); WriteString2(&msg, "\""); WriteString2(&msg, elem); WriteString2(&msg, "\" "); //skin - Info_ValueForKey(sv->players[i].userinfo, "skin", elem, sizeof(elem)); + Info_ValueForKey(sv->map.players[i].userinfo, "skin", elem, sizeof(elem)); WriteString2(&msg, "\""); WriteString2(&msg, elem); WriteString2(&msg, "\" "); WriteString2(&msg, " "); //tc - Info_ValueForKey(sv->players[i].userinfo, "topcolor", elem, sizeof(elem)); + Info_ValueForKey(sv->map.players[i].userinfo, "topcolor", elem, sizeof(elem)); WriteString2(&msg, elem); WriteString2(&msg, " "); //bc - Info_ValueForKey(sv->players[i].userinfo, "bottomcolor", elem, sizeof(elem)); + Info_ValueForKey(sv->map.players[i].userinfo, "bottomcolor", elem, sizeof(elem)); WriteString2(&msg, elem); WriteString2(&msg, " "); @@ -1346,7 +1320,7 @@ void SV_EmitPacketEntities (const sv_t *qtv, const viewer_t *v, const packet_ent if (newnum < oldnum) { // this is a new entity, send it from the baseline - baseline = &qtv->entity[newnum].baseline; + baseline = &qtv->map.entity[newnum].baseline; //Con_Printf ("baseline %i\n", newnum); SV_WriteDelta (newnum, baseline, &to->ents[newindex], msg, true); @@ -1371,11 +1345,11 @@ void Prox_SendInitialEnts(sv_t *qtv, oproxy_t *prox, netmsg_t *msg) frame_t *frame; int i, entnum; WriteByte(msg, svc_packetentities); - frame = &qtv->frame[qtv->netchan.incoming_sequence & (ENTITY_FRAMES-1)]; + frame = &qtv->map.frame[qtv->netchan.incoming_sequence & (ENTITY_FRAMES-1)]; for (i = 0; i < frame->numents; i++) { entnum = frame->entnums[i]; - SV_WriteDelta(entnum, &qtv->entity[entnum].baseline, &frame->ents[i], msg, true); + SV_WriteDelta(entnum, &qtv->map.entity[entnum].baseline, &frame->ents[i], msg, true); } WriteShort(msg, 0); } @@ -1403,40 +1377,40 @@ void SendLocalPlayerState(sv_t *tv, viewer_t *v, int playernum, netmsg_t *msg) if (tv && tv->controller == v) { //we're the one that is actually playing. - v->trackplayer = tv->thisplayer; + v->trackplayer = tv->map.thisplayer; flags = 0; - if (tv->players[tv->thisplayer].current.weaponframe) + if (tv->map.players[tv->map.thisplayer].current.weaponframe) flags |= PF_WEAPONFRAME; - if (tv->players[tv->thisplayer].current.effects) + if (tv->map.players[tv->map.thisplayer].current.effects) flags |= PF_EFFECTS; - if (tv->players[tv->thisplayer].dead) + if (tv->map.players[tv->map.thisplayer].dead) flags |= PF_DEAD; - if (tv->players[tv->thisplayer].gibbed) + if (tv->map.players[tv->map.thisplayer].gibbed) flags |= PF_GIB; for (j=0 ; j<3 ; j++) - if (tv->players[tv->thisplayer].current.velocity[j]) + if (tv->map.players[tv->map.thisplayer].current.velocity[j]) flags |= (PF_VELOCITY1<players[tv->thisplayer].current.origin[0]); - WriteShort(msg, tv->players[tv->thisplayer].current.origin[1]); - WriteShort(msg, tv->players[tv->thisplayer].current.origin[2]); - WriteByte(msg, tv->players[tv->thisplayer].current.frame); + WriteShort(msg, tv->map.players[tv->map.thisplayer].current.origin[0]); + WriteShort(msg, tv->map.players[tv->map.thisplayer].current.origin[1]); + WriteShort(msg, tv->map.players[tv->map.thisplayer].current.origin[2]); + WriteByte(msg, tv->map.players[tv->map.thisplayer].current.frame); for (j=0 ; j<3 ; j++) if (flags & (PF_VELOCITY1<players[tv->thisplayer].current.velocity[j]); + WriteShort (msg, tv->map.players[tv->map.thisplayer].current.velocity[j]); if (flags & PF_MODEL) - WriteByte(msg, tv->players[tv->thisplayer].current.modelindex); + WriteByte(msg, tv->map.players[tv->map.thisplayer].current.modelindex); if (flags & PF_SKINNUM) - WriteByte(msg, tv->players[tv->thisplayer].current.skinnum); + WriteByte(msg, tv->map.players[tv->map.thisplayer].current.skinnum); if (flags & PF_EFFECTS) - WriteByte(msg, tv->players[tv->thisplayer].current.effects); + WriteByte(msg, tv->map.players[tv->map.thisplayer].current.effects); if (flags & PF_WEAPONFRAME) - WriteByte(msg, tv->players[tv->thisplayer].current.weaponframe); + WriteByte(msg, tv->map.players[tv->map.thisplayer].current.weaponframe); } else { @@ -1519,7 +1493,7 @@ void SendNQClientData(sv_t *tv, viewer_t *v, netmsg_t *msg) return; } else - pl = &tv->players[v->trackplayer]; + pl = &tv->map.players[v->trackplayer]; bits = 0; @@ -1611,7 +1585,7 @@ void SendNQPlayerStates(cluster_t *cluster, sv_t *tv, viewer_t *v, netmsg_t *msg WriteByte(msg, svc_nqtime); WriteFloat(msg, tv->physicstime/1000.0f); - BSP_SetupForPosition(tv->bsp, v->origin[0], v->origin[1], v->origin[2]); + BSP_SetupForPosition(tv->map.bsp, v->origin[0], v->origin[1], v->origin[2]); lerp = ((tv->simtime - tv->oldpackettime)/1000.0f) / ((tv->nextpackettime - tv->oldpackettime)/1000.0f); if (lerp < 0) @@ -1641,9 +1615,9 @@ void SendNQPlayerStates(cluster_t *cluster, sv_t *tv, viewer_t *v, netmsg_t *msg WriteShort(msg, v->trackplayer+1); WriteByte(msg, svc_setangle); - WriteByte(msg, (int)InterpolateAngle(tv->players[v->trackplayer].old.angles[0], tv->players[v->trackplayer].current.angles[0], lerp)>>8); - WriteByte(msg, (int)InterpolateAngle(tv->players[v->trackplayer].old.angles[1], tv->players[v->trackplayer].current.angles[1], lerp)>>8); - WriteByte(msg, (int)InterpolateAngle(tv->players[v->trackplayer].old.angles[2], tv->players[v->trackplayer].current.angles[2], lerp)>>8); + WriteByte(msg, (int)InterpolateAngle(tv->map.players[v->trackplayer].old.angles[0], tv->map.players[v->trackplayer].current.angles[0], lerp)>>8); + WriteByte(msg, (int)InterpolateAngle(tv->map.players[v->trackplayer].old.angles[1], tv->map.players[v->trackplayer].current.angles[1], lerp)>>8); + WriteByte(msg, (int)InterpolateAngle(tv->map.players[v->trackplayer].old.angles[2], tv->map.players[v->trackplayer].current.angles[2], lerp)>>8); } else { @@ -1654,8 +1628,8 @@ void SendNQPlayerStates(cluster_t *cluster, sv_t *tv, viewer_t *v, netmsg_t *msg for (e = 0; e < MAX_CLIENTS; e++) { - pl = &tv->players[e]; - ent = &tv->entity[e+1]; + pl = &tv->map.players[e]; + ent = &tv->map.entity[e+1]; if (e == v->thisplayer && v->trackplayer < 0) { @@ -1703,7 +1677,7 @@ void SendNQPlayerStates(cluster_t *cluster, sv_t *tv, viewer_t *v, netmsg_t *msg if (!pl->active) continue; - if (pl->current.modelindex >= tv->numinlines && !BSP_Visible(tv->bsp, pl->leafcount, pl->leafs)) + if (pl->current.modelindex >= tv->map.numinlines && !BSP_Visible(tv->map.bsp, pl->leafcount, pl->leafs)) continue; pl->current.modelindex = 8; @@ -1798,7 +1772,7 @@ void SendNQPlayerStates(cluster_t *cluster, sv_t *tv, viewer_t *v, netmsg_t *msg snapdist = snapdist*8; snapdist = snapdist*snapdist; - topacket = &tv->frame[tv->netchan.incoming_sequence&(ENTITY_FRAMES-1)]; + topacket = &tv->map.frame[tv->netchan.incoming_sequence&(ENTITY_FRAMES-1)]; for (newindex = 0; newindex < topacket->numents; newindex++) { @@ -1806,7 +1780,7 @@ void SendNQPlayerStates(cluster_t *cluster, sv_t *tv, viewer_t *v, netmsg_t *msg //pvs cull everything else newstate = &topacket->ents[newindex]; newnum = topacket->entnums[newindex]; - if (newstate->modelindex >= tv->numinlines && !BSP_Visible(tv->bsp, tv->entity[newnum].leafcount, tv->entity[newnum].leafs)) + if (newstate->modelindex >= tv->map.numinlines && !BSP_Visible(tv->map.bsp, tv->map.entity[newnum].leafcount, tv->map.entity[newnum].leafs)) continue; if (msg->cursize + 128 > msg->maxsize) @@ -1927,7 +1901,7 @@ viewer_t *GetCommentator(viewer_t *v) void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) { - bsp_t *bsp = (!tv || tv->controller == v ? NULL : tv->bsp); + bsp_t *bsp = (!tv || tv->controller == v ? NULL : tv->map.bsp); viewer_t *cv; packet_entities_t *e; int i; @@ -1967,8 +1941,8 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) if (tv->controller == v) { lerp = 1; - track = tv->thisplayer; - v->trackplayer = tv->thisplayer; + track = tv->map.thisplayer; + v->trackplayer = tv->map.thisplayer; } else { @@ -1985,10 +1959,10 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) if (!v->commentator && track >= 0 && !v->backbuffered) { - if (v->trackplayer != tv->trackplayer && tv->usequakeworldprotocols) - if (!tv->players[v->trackplayer].active && tv->players[tv->trackplayer].active) + if (v->trackplayer != tv->map.trackplayer && tv->usequakeworldprotocols) + if (!tv->map.players[v->trackplayer].active && tv->map.players[tv->map.trackplayer].active) { - QW_StuffcmdToViewer (v, "track %i\n", tv->trackplayer); + QW_StuffcmdToViewer (v, "track %i\n", tv->map.trackplayer); } } } @@ -2001,57 +1975,57 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) continue; } - if (!tv->players[i].active && track != i) + if (!tv->map.players[i].active && track != i) continue; //bsp cull. currently tracked player is always visible - if (track != i && !BSP_Visible(bsp, tv->players[i].leafcount, tv->players[i].leafs)) + if (track != i && !BSP_Visible(bsp, tv->map.players[i].leafcount, tv->map.players[i].leafs)) continue; flags = PF_COMMAND; - if (track == i && tv->players[i].current.weaponframe) + if (track == i && tv->map.players[i].current.weaponframe) flags |= PF_WEAPONFRAME; - if (tv->players[i].current.modelindex != tv->modelindex_player) + if (tv->map.players[i].current.modelindex != tv->map.modelindex_player) flags |= PF_MODEL; - if (tv->players[i].dead || !tv->players[i].active) + if (tv->map.players[i].dead || !tv->map.players[i].active) flags |= PF_DEAD; - if (tv->players[i].gibbed || !tv->players[i].active) + if (tv->map.players[i].gibbed || !tv->map.players[i].active) flags |= PF_GIB; - if (tv->players[i].current.effects != 0) + if (tv->map.players[i].current.effects != 0) flags |= PF_EFFECTS; - if (tv->players[i].current.skinnum != 0) + if (tv->map.players[i].current.skinnum != 0) flags |= PF_SKINNUM; - if (tv->players[i].current.velocity[0]) + if (tv->map.players[i].current.velocity[0]) flags |= PF_VELOCITY1; - if (tv->players[i].current.velocity[1]) + if (tv->map.players[i].current.velocity[1]) flags |= PF_VELOCITY2; - if (tv->players[i].current.velocity[2]) + if (tv->map.players[i].current.velocity[2]) flags |= PF_VELOCITY3; WriteByte(msg, svc_playerinfo); WriteByte(msg, i); WriteShort(msg, flags); - if (!tv->players[i].active || - (tv->players[i].current.origin[0] - tv->players[i].old.origin[0])*(tv->players[i].current.origin[0] - tv->players[i].old.origin[0]) > snapdist || - (tv->players[i].current.origin[1] - tv->players[i].old.origin[1])*(tv->players[i].current.origin[1] - tv->players[i].old.origin[1]) > snapdist || - (tv->players[i].current.origin[2] - tv->players[i].old.origin[2])*(tv->players[i].current.origin[2] - tv->players[i].old.origin[2]) > snapdist) + if (!tv->map.players[i].active || + (tv->map.players[i].current.origin[0] - tv->map.players[i].old.origin[0])*(tv->map.players[i].current.origin[0] - tv->map.players[i].old.origin[0]) > snapdist || + (tv->map.players[i].current.origin[1] - tv->map.players[i].old.origin[1])*(tv->map.players[i].current.origin[1] - tv->map.players[i].old.origin[1]) > snapdist || + (tv->map.players[i].current.origin[2] - tv->map.players[i].old.origin[2])*(tv->map.players[i].current.origin[2] - tv->map.players[i].old.origin[2]) > snapdist) { //teleported (or respawned), so don't interpolate - WriteShort(msg, tv->players[i].current.origin[0]); - WriteShort(msg, tv->players[i].current.origin[1]); - WriteShort(msg, tv->players[i].current.origin[2]); + WriteShort(msg, tv->map.players[i].current.origin[0]); + WriteShort(msg, tv->map.players[i].current.origin[1]); + WriteShort(msg, tv->map.players[i].current.origin[2]); } else { //send interpolated angles - interp = (lerp)*tv->players[i].current.origin[0] + (1-lerp)*tv->players[i].old.origin[0]; + interp = (lerp)*tv->map.players[i].current.origin[0] + (1-lerp)*tv->map.players[i].old.origin[0]; WriteShort(msg, interp); - interp = (lerp)*tv->players[i].current.origin[1] + (1-lerp)*tv->players[i].old.origin[1]; + interp = (lerp)*tv->map.players[i].current.origin[1] + (1-lerp)*tv->map.players[i].old.origin[1]; WriteShort(msg, interp); - interp = (lerp)*tv->players[i].current.origin[2] + (1-lerp)*tv->players[i].old.origin[2]; + interp = (lerp)*tv->map.players[i].current.origin[2] + (1-lerp)*tv->map.players[i].old.origin[2]; WriteShort(msg, interp); } - WriteByte(msg, tv->players[i].current.frame); + WriteByte(msg, tv->map.players[i].current.frame); if (flags & PF_MSEC) @@ -2060,39 +2034,39 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) } if (flags & PF_COMMAND) { - if (!tv->players[i].active) + if (!tv->map.players[i].active) { - to.angles[0] = tv->players[i].current.angles[0]; - to.angles[1] = tv->players[i].current.angles[1]; - to.angles[2] = tv->players[i].current.angles[2]; + to.angles[0] = tv->map.players[i].current.angles[0]; + to.angles[1] = tv->map.players[i].current.angles[1]; + to.angles[2] = tv->map.players[i].current.angles[2]; } else { - to.angles[0] = InterpolateAngle(tv->players[i].old.angles[0], tv->players[i].current.angles[0], lerp); - to.angles[1] = InterpolateAngle(tv->players[i].old.angles[1], tv->players[i].current.angles[1], lerp); - to.angles[2] = InterpolateAngle(tv->players[i].old.angles[2], tv->players[i].current.angles[2], lerp); + to.angles[0] = InterpolateAngle(tv->map.players[i].old.angles[0], tv->map.players[i].current.angles[0], lerp); + to.angles[1] = InterpolateAngle(tv->map.players[i].old.angles[1], tv->map.players[i].current.angles[1], lerp); + to.angles[2] = InterpolateAngle(tv->map.players[i].old.angles[2], tv->map.players[i].current.angles[2], lerp); } WriteDeltaUsercmd(msg, &nullcmd, &to); } //vel if (flags & PF_VELOCITY1) - WriteShort(msg, tv->players[i].current.velocity[0]); + WriteShort(msg, tv->map.players[i].current.velocity[0]); if (flags & PF_VELOCITY2) - WriteShort(msg, tv->players[i].current.velocity[1]); + WriteShort(msg, tv->map.players[i].current.velocity[1]); if (flags & PF_VELOCITY3) - WriteShort(msg, tv->players[i].current.velocity[2]); + WriteShort(msg, tv->map.players[i].current.velocity[2]); //model if (flags & PF_MODEL) - WriteByte(msg, tv->players[i].current.modelindex); + WriteByte(msg, tv->map.players[i].current.modelindex); //skin if (flags & PF_SKINNUM) - WriteByte (msg, tv->players[i].current.skinnum); + WriteByte (msg, tv->map.players[i].current.skinnum); //effects if (flags & PF_EFFECTS) - WriteByte (msg, tv->players[i].current.effects); + WriteByte (msg, tv->map.players[i].current.effects); //weaponframe if (flags & PF_WEAPONFRAME) - WriteByte(msg, tv->players[i].current.weaponframe); + WriteByte(msg, tv->map.players[i].current.weaponframe); } } else @@ -2111,14 +2085,14 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) entity_state_t *newstate; int newnum, oldnum; frame_t *frompacket, *topacket; - topacket = &tv->frame[tv->netchan.incoming_sequence&(ENTITY_FRAMES-1)]; + topacket = &tv->map.frame[tv->netchan.incoming_sequence&(ENTITY_FRAMES-1)]; if (tv->usequakeworldprotocols) { - frompacket = &tv->frame[(topacket->oldframe)&(ENTITY_FRAMES-1)]; + frompacket = &tv->map.frame[(topacket->oldframe)&(ENTITY_FRAMES-1)]; } else { - frompacket = &tv->frame[(tv->netchan.incoming_sequence-1)&(ENTITY_FRAMES-1)]; + frompacket = &tv->map.frame[(tv->netchan.incoming_sequence-1)&(ENTITY_FRAMES-1)]; } for (newindex = 0; newindex < topacket->numents; newindex++) @@ -2127,7 +2101,7 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) //pvs cull everything else newstate = &topacket->ents[newindex]; newnum = topacket->entnums[newindex]; - if (newstate->modelindex >= tv->numinlines && !BSP_Visible(bsp, tv->entity[newnum].leafcount, tv->entity[newnum].leafs)) + if (newstate->modelindex >= tv->map.numinlines && !BSP_Visible(bsp, tv->map.entity[newnum].leafcount, tv->map.entity[newnum].leafs)) continue; e->entnum[e->numents] = newnum; @@ -2189,18 +2163,18 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) SV_EmitPacketEntities(tv, v, e, msg); - if (tv && tv->nailcount) + if (tv && tv->map.nailcount) { WriteByte(msg, svc_nails); - WriteByte(msg, tv->nailcount); - for (i = 0; i < tv->nailcount; i++) + WriteByte(msg, tv->map.nailcount); + for (i = 0; i < tv->map.nailcount; i++) { - WriteByte(msg, tv->nails[i].bits[0]); - WriteByte(msg, tv->nails[i].bits[1]); - WriteByte(msg, tv->nails[i].bits[2]); - WriteByte(msg, tv->nails[i].bits[3]); - WriteByte(msg, tv->nails[i].bits[4]); - WriteByte(msg, tv->nails[i].bits[5]); + WriteByte(msg, tv->map.nails[i].bits[0]); + WriteByte(msg, tv->map.nails[i].bits[1]); + WriteByte(msg, tv->map.nails[i].bits[2]); + WriteByte(msg, tv->map.nails[i].bits[3]); + WriteByte(msg, tv->map.nails[i].bits[4]); + WriteByte(msg, tv->map.nails[i].bits[5]); } } } @@ -2223,11 +2197,11 @@ void UpdateStats(sv_t *qtv, viewer_t *v) cv = v; if (qtv && qtv->controller == cv) - stats = qtv->players[qtv->thisplayer].stats; + stats = qtv->map.players[qtv->map.thisplayer].stats; else if (cv->trackplayer == -1 || !qtv) stats = nullstats; else - stats = qtv->players[cv->trackplayer].stats; + stats = qtv->map.players[cv->trackplayer].stats; for (i = 0; i < MAX_STATS; i++) { @@ -2301,13 +2275,13 @@ void PMove(viewer_t *v, usercmd_t *cmd) pmove_t pmove; if (v->server && v->server->controller == v) { - v->origin[0] = v->server->players[v->server->thisplayer].current.origin[0]/8.0f; - v->origin[1] = v->server->players[v->server->thisplayer].current.origin[1]/8.0f; - v->origin[2] = v->server->players[v->server->thisplayer].current.origin[2]/8.0f; + v->origin[0] = v->server->map.players[v->server->map.thisplayer].current.origin[0]/8.0f; + v->origin[1] = v->server->map.players[v->server->map.thisplayer].current.origin[1]/8.0f; + v->origin[2] = v->server->map.players[v->server->map.thisplayer].current.origin[2]/8.0f; - v->velocity[0] = v->server->players[v->server->thisplayer].current.velocity[2]/8.0f; - v->velocity[1] = v->server->players[v->server->thisplayer].current.velocity[2]/8.0f; - v->velocity[2] = v->server->players[v->server->thisplayer].current.velocity[2]/8.0f; + v->velocity[0] = v->server->map.players[v->server->map.thisplayer].current.velocity[2]/8.0f; + v->velocity[1] = v->server->map.players[v->server->map.thisplayer].current.velocity[2]/8.0f; + v->velocity[2] = v->server->map.players[v->server->map.thisplayer].current.velocity[2]/8.0f; return; } pmove.origin[0] = v->origin[0]; @@ -2322,7 +2296,7 @@ void PMove(viewer_t *v, usercmd_t *cmd) qtv = v->server; if (qtv) { - pmove.movevars = qtv->movevars; + pmove.movevars = qtv->map.movevars; } else { @@ -2581,7 +2555,7 @@ guimenu: QW_StuffcmdToViewer(v, "menutext 72 %i \"Áãôéöå Çáíåóº\"\n", y); y+=8; } - QW_StuffcmdToViewer(v, "menutext 32 %i \"%30s\" \"stream %i\"\n", y, *sv->hostname?sv->hostname:sv->server, sv->streamid); + QW_StuffcmdToViewer(v, "menutext 32 %i \"%30s\" \"stream %i\"\n", y, *sv->map.hostname?sv->map.hostname:sv->server, sv->streamid); y+=8; } if (!shownheader) @@ -2763,7 +2737,7 @@ tuiadmin: isjoin = true; snprintf(buf, sizeof(buf), "udp:%s", args); - qtv = QTV_NewServerConnection(cluster, buf, "", false, true, !isjoin, false); + qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, true, !isjoin, false); if (qtv) { QW_SetMenu(v, MENU_NONE); @@ -2782,7 +2756,7 @@ tuiadmin: char buf[256]; snprintf(buf, sizeof(buf), "tcp:%s", args); - qtv = QTV_NewServerConnection(cluster, buf, "", false, true, true, false); + qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, true, true, false); if (qtv) { QW_SetMenu(v, MENU_NONE); @@ -2828,7 +2802,7 @@ tuiadmin: { char buf[256]; snprintf(buf, sizeof(buf), "file:%s", args); - qtv = QTV_NewServerConnection(cluster, buf, "", false, true, true, false); + qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, true, true, false); if (qtv) { QW_SetMenu(v, MENU_NONE); @@ -3010,7 +2984,7 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean else if (!strcmp(v->expectcommand, "addserver")) { snprintf(buf, sizeof(buf), "tcp:%s", message); - qtv = QTV_NewServerConnection(cluster, buf, "", false, false, false, false); + qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, false, false, false); if (qtv) { QW_SetViewersServer(cluster, v, qtv); @@ -3036,7 +3010,7 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean else if (!strcmp(v->expectcommand, "insecadddemo")) { snprintf(buf, sizeof(buf), "file:%s", message); - qtv = QTV_NewServerConnection(cluster, buf, "", false, false, false, false); + qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, false, false, false); if (!qtv) QW_PrintfToViewer(v, "Failed to play demo \"%s\"\n", message); else @@ -3049,7 +3023,7 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean else if (!strcmp(v->expectcommand, "adddemo")) { snprintf(buf, sizeof(buf), "file:%s", message); - qtv = QTV_NewServerConnection(cluster, buf, "", false, false, false, false); + qtv = QTV_NewServerConnection(cluster, 0, buf, "", false, false, false, false); if (!qtv) QW_PrintfToViewer(v, "Failed to play demo \"%s\"\n", message); else @@ -3264,7 +3238,7 @@ void QW_PositionAtIntermission(sv_t *qtv, viewer_t *v) if (qtv) - spot = BSP_IntermissionSpot(qtv->bsp); + spot = BSP_IntermissionSpot(qtv->map.bsp); else spot = &nullstreamspot; @@ -3394,6 +3368,9 @@ void ParseNQC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) QTV_Say(cluster, v->server, v, ".menu", false); } + // else if (!strcmp(buf, "pause")) + // qtv->errored = ERR_PAUSED; + else if (!strncmp(buf, "say \"", 5)) QTV_Say(cluster, qtv, v, buf+5, false); else if (!strncmp(buf, "say ", 4)) @@ -3444,7 +3421,7 @@ void ParseNQC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) for (t = v->trackplayer+1; t < MAX_CLIENTS; t++) { - if (v->server->players[t].active) + if (v->server->map.players[t].active) { break; } @@ -3466,7 +3443,7 @@ void ParseNQC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) else { v->trackplayer = t; - Info_ValueForKey(v->server->players[t].userinfo, "name", buf, sizeof(buf)); + Info_ValueForKey(v->server->map.players[t].userinfo, "name", buf, sizeof(buf)); QW_PrintfToViewer(v, "Now tracking: %s\n", buf); } } @@ -3483,7 +3460,7 @@ void ParseNQC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) { for (t = MAX_CLIENTS-1; t >= v->trackplayer; t--) { - if (v->server->players[t].active) + if (v->server->map.players[t].active) { break; } @@ -3493,7 +3470,7 @@ void ParseNQC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) { for (t = v->trackplayer-1; t >= 0; t--) { - if (v->server->players[t].active) + if (v->server->map.players[t].active) { break; } @@ -3508,7 +3485,7 @@ void ParseNQC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) else { v->trackplayer = t; - Info_ValueForKey(v->server->players[t].userinfo, "name", buf, sizeof(buf)); + Info_ValueForKey(v->server->map.players[t].userinfo, "name", buf, sizeof(buf)); QW_PrintfToViewer(v, "Now tracking: %s\n", buf); } } @@ -3516,9 +3493,9 @@ void ParseNQC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) if (v->trackplayer > -1 && v->server) { - v->origin[0] = v->server->players[v->trackplayer].current.origin[0]/8.0; - v->origin[1] = v->server->players[v->trackplayer].current.origin[1]/8.0; - v->origin[2] = v->server->players[v->trackplayer].current.origin[2]/8.0; + v->origin[0] = v->server->map.players[v->trackplayer].current.origin[0]/8.0; + v->origin[1] = v->server->map.players[v->trackplayer].current.origin[1]/8.0; + v->origin[2] = v->server->map.players[v->trackplayer].current.origin[2]/8.0; } break; @@ -3589,7 +3566,7 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) if (!qtv) SendList(qtv, first, ConnectionlessModelList, svc_modellist, &msg); else - SendList(qtv, first, qtv->modellist, svc_modellist, &msg); + SendList(qtv, first, qtv->map.modellist, svc_modellist, &msg); SendBufferToViewer(v, msg.data, msg.cursize, true); } else if (!iscont && !strncmp(buf, "soundlist ", 10)) @@ -3613,7 +3590,7 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) if (!qtv) SendList(qtv, first, ConnectionlessSoundList, svc_soundlist, &msg); else - SendList(qtv, first, qtv->soundlist, svc_soundlist, &msg); + SendList(qtv, first, qtv->map.soundlist, svc_soundlist, &msg); SendBufferToViewer(v, msg.data, msg.cursize, true); } else if (!iscont && !strncmp(buf, "prespawn", 8)) @@ -3646,15 +3623,15 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) if (qtv && qtv->controller == v) { - if (!qtv->bsp) + if (!qtv->map.bsp) { //warning do we still actually need to do this ourselves? Or can we just forward what the user stated? - QW_PrintfToViewer(v, "QTV doesn't have that map (%s), sorry.\n", qtv->modellist[1].name); + QW_PrintfToViewer(v, "QTV doesn't have that map (%s), sorry.\n", qtv->map.modellist[1].name); qtv->errored = ERR_DROP; } - else if (crc != BSP_Checksum(qtv->bsp)) + else if (crc != BSP_Checksum(qtv->map.bsp)) { - QW_PrintfToViewer(v, "QTV's map (%s) does not match the servers\n", qtv->modellist[1].name); + QW_PrintfToViewer(v, "QTV's map (%s) does not match the servers\n", qtv->map.modellist[1].name); qtv->errored = ERR_DROP; } } @@ -3706,7 +3683,7 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) else { v->thinksitsconnected = true; - if (qtv && qtv->ispaused) + if (qtv && qtv->map.ispaused) { char msgb[] = {svc_setpause, 1}; SendBufferToViewer(v, msgb, sizeof(msgb), true); @@ -3746,6 +3723,15 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) Sys_Printf(cluster, "QW viewer %s disconnects\n", v->name); v->drop = true; } + else if (!strcmp(buf, "pause")) + { + if (qtv->errored == ERR_PAUSED) + qtv->errored = ERR_NONE; + else if (qtv->sourcetype == SRC_DEMO && (1 || v->isadmin)) + qtv->errored = ERR_PAUSED; + else + QW_PrintfToViewer(v, "You may not pause this stream\n"); + } else if (!strncmp(buf, "ison", 4)) { viewer_t *other; @@ -3828,7 +3814,7 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) InitNetMsg(&m, buf, sizeof(buf)); WriteByte(&m, svc_print); WriteByte(&m, 2); - end = qtv->serverinfo; + end = qtv->map.serverinfo; for(;;) { if (!*end) @@ -3906,564 +3892,6 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) } } -void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum) -{ - //build a possible message, even though it'll probably not be sent - - sv_t *sv; - int i, min; - - switch(viewer->menunum) - { - default: - break; - - case MENU_MAIN: - if (buttonnum < 0) - viewer->menuop -= (MENU_MAIN_ITEMCOUNT + 1)/2; - else if (buttonnum > 0) - viewer->menuop += (MENU_MAIN_ITEMCOUNT + 1)/2; - else if (buttonnum == 0) - { - switch(viewer->menuop) - { - case MENU_MAIN_STREAMS: //Streams - QW_SetMenu(viewer, MENU_SERVERS); - break; - case MENU_MAIN_CLIENTLIST://Client List - QW_SetMenu(viewer, MENU_CLIENTS); - break; - - case MENU_MAIN_NEWSTREAM://New Stream - QW_PrintfToViewer(viewer, "Not implemented yet\n"); - break; - case MENU_MAIN_DEMOS://Demos - Cluster_BuildAvailableDemoList(cluster); - QW_SetMenu(viewer, MENU_DEMOS); - break; - - case MENU_MAIN_SERVERBROWSER://Server Browser - QW_PrintfToViewer(viewer, "Not implemented yet\n"); - break; - case MENU_MAIN_ADMIN://Admin - QW_SetMenu(viewer, MENU_ADMIN); - break; - - case MENU_MAIN_PREVPROX://Previous Proxy - if (viewer->isproxy) - { - QW_SetMenu(viewer, MENU_NONE); - QW_StuffcmdToViewer(viewer, "say proxy:menu\n"); - } - else - QW_PrintfToViewer(viewer, "No client proxy detected\n"); - break; - case MENU_MAIN_NEXTPROX://Next Proxy - if (viewer->server && viewer->server->serverisproxy && viewer->server->controller == viewer) - { - viewer->server->proxyisselected = false; - QW_SetMenu(viewer, MENU_NONE); - SendClientCommand(viewer->server, "say .menu"); - } - else - QW_PrintfToViewer(viewer, "No server proxy detected\n"); - break; - - case MENU_MAIN_HELP://Help Menu - QW_PrintfToViewer(viewer, "Not implemented yet\n"); - break; - } - } - break; - - case MENU_CLIENTS: - { - } - break; - - case MENU_DEMOS: - if (buttonnum >= 0) - QW_StuffcmdToViewer(viewer, "say .demo %s\n", cluster->availdemos[viewer->menuop].name); - else - QW_SetMenu(viewer, MENU_MAIN); - break; - - case MENU_ADMINSERVER: - if (viewer->server) - { - i = 0; - sv = viewer->server; - if (i++ == viewer->menuop) - { //auto disconnect - sv->disconnectwhennooneiswatching ^= 1; - } - if (i++ == viewer->menuop) - { //disconnect - QTV_Shutdown(viewer->server); - } - if (i++ == viewer->menuop) - { - if (sv->controller == viewer) - sv->controller = NULL; - else - { - sv->controller = viewer; - sv->controllersquencebias = viewer->netchan.outgoing_sequence - sv->netchan.outgoing_sequence; - } - } - if (i++ == viewer->menuop) - { //back - QW_SetMenu(viewer, MENU_ADMIN); - } - break; - } - //fallthrough - case MENU_SERVERS: - if (!cluster->servers) - { - QW_StuffcmdToViewer(viewer, "echo Please enter a server ip\nmessagemode\n"); - strcpy(viewer->expectcommand, "insecadddemo"); - } - else - { - if (viewer->menuop < 0) - viewer->menuop = 0; - i = 0; - min = viewer->menuop - 10; - if (min < 0) - min = 0; - for (sv = cluster->servers; sv && inext, i++) - {//skip over the early connections. - } - min+=20; - for (; sv && i < min; sv = sv->next, i++) - { - if (i == viewer->menuop) - { - /*if (sv->parsingconnectiondata || !sv->modellist[1].name[0]) - { - QW_PrintfToViewer(viewer, "But that stream isn't connected\n"); - } - else*/ - { - QW_SetViewersServer(cluster, viewer, sv); - QW_SetMenu(viewer, MENU_NONE); - viewer->thinksitsconnected = false; - } - break; - } - } - } - break; - case MENU_ADMIN: - i = 0; - if (i++ == viewer->menuop) - { //connection stuff - QW_SetMenu(viewer, MENU_ADMINSERVER); - } - if (i++ == viewer->menuop) - { //qw port - QW_StuffcmdToViewer(viewer, "echo You will need to reconnect\n"); - cluster->qwlistenportnum += (buttonnum<0)?-1:1; - } - if (i++ == viewer->menuop) - { //hostname - strcpy(viewer->expectcommand, "hostname"); - QW_StuffcmdToViewer(viewer, "echo Please enter the new hostname\nmessagemode\n"); - } - if (i++ == viewer->menuop) - { //master - strcpy(viewer->expectcommand, "master"); - QW_StuffcmdToViewer(viewer, "echo Please enter the master dns or ip\necho Enter '.' for masterless mode\nmessagemode\n"); - } - if (i++ == viewer->menuop) - { //password - strcpy(viewer->expectcommand, "password"); - QW_StuffcmdToViewer(viewer, "echo Please enter the new rcon password\nmessagemode\n"); - } - if (i++ == viewer->menuop) - { //add server - strcpy(viewer->expectcommand, "messagemode"); - QW_StuffcmdToViewer(viewer, "echo Please enter the new qtv server dns or ip\naddserver\n"); - } - if (i++ == viewer->menuop) - { //add demo - strcpy(viewer->expectcommand, "adddemo"); - QW_StuffcmdToViewer(viewer, "echo Please enter the name of the demo to play\nmessagemode\n"); - } - if (i++ == viewer->menuop) - { //choke - cluster->chokeonnotupdated ^= 1; - } - if (i++ == viewer->menuop) - { //late forwarding - cluster->lateforward ^= 1; - } - if (i++ == viewer->menuop) - { //no talking - cluster->notalking ^= 1; - } - if (i++ == viewer->menuop) - { //nobsp - cluster->nobsp ^= 1; - } - if (i++ == viewer->menuop) - { //back - QW_SetMenu(viewer, MENU_NONE); - } - - break; - } -} - -void WriteStringSelection(netmsg_t *b, qboolean selected, const char *str) -{ - if (selected) - { - WriteByte(b, 13); - while(*str) - WriteByte(b, 128|*str++); - } - else - { - WriteByte(b, ' '); - while(*str) - WriteByte(b, *str++); - } -} - -void Menu_Draw(cluster_t *cluster, viewer_t *viewer) -{ - char buffer[2048]; - char str[64]; - sv_t *sv; - int i, min; - unsigned char *s; - - netmsg_t m; - - if (viewer->backbuffered) - return; - - if (viewer->menunum == MENU_FORWARDING) - return; - - if (viewer->menuspamtime > cluster->curtime && viewer->menuspamtime < cluster->curtime + CENTERTIME*2000) - return; - viewer->menuspamtime = cluster->curtime + CENTERTIME*1000; - - InitNetMsg(&m, buffer, sizeof(buffer)); - - WriteByte(&m, svc_centerprint); - - sprintf(str, "FTEQTV build %i\n", cluster->buildnumber); - WriteString2(&m, str); - WriteString2(&m, "www.FTEQW.com\n"); - WriteString2(&m, "-------------\n"); - - if (strcmp(cluster->hostname, DEFAULT_HOSTNAME)) - WriteString2(&m, cluster->hostname); - - switch(viewer->menunum) - { - default: - WriteString2(&m, "bad menu"); - break; - - case MENU_MAIN: - { - 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; - while (viewer->menuop >= MENU_MAIN_ITEMCOUNT) - viewer->menuop -= MENU_MAIN_ITEMCOUNT; - i = viewer->menuop; - - WriteStringSelection(&m, i==MENU_MAIN_STREAMS, "Streams "); - WriteStringSelection(&m, i==MENU_MAIN_CLIENTLIST, "Client List "); - WriteByte(&m, '\n'); - WriteStringSelection(&m, i==MENU_MAIN_NEWSTREAM, "New Stream "); - WriteStringSelection(&m, i==MENU_MAIN_DEMOS, "Demos "); - WriteByte(&m, '\n'); - WriteStringSelection(&m, i==MENU_MAIN_SERVERBROWSER,"Server Browser "); - WriteStringSelection(&m, i==MENU_MAIN_ADMIN, "Admin "); - WriteByte(&m, '\n'); - WriteStringSelection(&m, i==MENU_MAIN_PREVPROX, "Previous Proxy "); - WriteStringSelection(&m, i==MENU_MAIN_NEXTPROX, "Next Proxy "); - WriteByte(&m, '\n'); - WriteStringSelection(&m, i==MENU_MAIN_HELP, "Help "); - WriteString2(&m, " "); - - 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"); - } - break; - - case MENU_CLIENTS: - { - int start; - viewer_t *v; - char *srv; - int c; - v = cluster->viewers; - - WriteString2(&m, "\nActive Clients\n\n"); - - start = viewer->menuop & ~7; - for (i = 0; i < start && v; i++) - v = v->next; - for (i = start; i < start+8 && v; i++, v = v->next) - { - for (c = strlen(v->name); c < 14; c++) - WriteByte(&m, ' '); - WriteStringSelection(&m, viewer->menuop == i, v->name); - WriteString2(&m, ": "); - if (v->server) - { - if (!v->server->sourcefile && !v->server->parsingconnectiondata) - srv = v->server->hostname; - else - srv = v->server->server; - } - else - srv = "None"; - for (c = 0; c < 20; c++) - { - if (*srv) - WriteByte(&m, *srv++); - else - WriteByte(&m, ' '); - } - - WriteByte(&m, '\n'); - } - for (; i < start+8; i++) - WriteByte(&m, '\n'); - } - break; - - - case MENU_DEMOS: - { - int start; - - WriteString2(&m, "\nAvailable Demos\n\n"); - - if (cluster->availdemoscount == 0) - { - WriteString2(&m, "No demos are available"); - break; - } - - if (viewer->menuop < 0) - viewer->menuop = 0; - if (viewer->menuop > cluster->availdemoscount-1) - viewer->menuop = cluster->availdemoscount-1; - - start = viewer->menuop & ~7; - for (i = start; i < start+8; i++) - { - if (i == viewer->menuop) - { - WriteByte(&m, '['); - WriteString2(&m, cluster->availdemos[i].name); - WriteByte(&m, ']'); - } - else - { - WriteString2(&m, cluster->availdemos[i].name); - } - WriteByte(&m, '\n'); - } - } - break; - - case MENU_ADMINSERVER: //per-connection options - if (viewer->server) - { - sv = viewer->server; - WriteString2(&m, "\n\nConnection Admin\n"); - WriteString2(&m, sv->hostname); - if (sv->sourcefile) - WriteString2(&m, " (demo)"); - WriteString2(&m, "\n\n"); - - if (viewer->menuop < 0) - viewer->menuop = 0; - - i = 0; - - WriteString2(&m, " auto disconnect"); - WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); - if (viewer->server->disconnectwhennooneiswatching == 2) - sprintf(str, "%-20s", "when server disconnects"); - else if (viewer->server->disconnectwhennooneiswatching) - sprintf(str, "%-20s", "when inactive"); - else - sprintf(str, "%-20s", "never"); - WriteString2(&m, str); - WriteString2(&m, "\n"); - - WriteString2(&m, "force disconnect"); - WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); - sprintf(str, "%-20s", "..."); - WriteString2(&m, str); - WriteString2(&m, "\n"); - - WriteString2(&m, " take control"); - WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); - sprintf(str, "%-20s", "..."); - WriteString2(&m, str); - WriteString2(&m, "\n"); - - WriteString2(&m, " back"); - WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); - sprintf(str, "%-20s", "..."); - WriteString2(&m, str); - WriteString2(&m, "\n"); - - if (viewer->menuop >= i) - viewer->menuop = i - 1; - - WriteString2(&m, "\n"); - - WriteString2(&m, " status"); - WriteString2(&m, " : "); - sprintf(str, "%-20s", viewer->server->status); - WriteString2(&m, str); - WriteString2(&m, "\n"); - - break; - } - //fallthrough - case MENU_SERVERS: //connections list - - WriteString2(&m, "\n\nServers\n\n"); - - if (!cluster->servers) - { - WriteString2(&m, "No active connections"); - } - else - { - if (viewer->menuop < 0) - viewer->menuop = 0; - i = 0; - min = viewer->menuop - 10; - if (min < 0) - min = 0; - for (sv = cluster->servers; sv && inext, i++) - {//skip over the early connections. - } - min+=20; - for (; sv && i < min; sv = sv->next, i++) - { - //Info_ValueForKey(sv->serverinfo, "hostname", str, sizeof(str)); - //if (sv->parsingconnectiondata || !sv->modellist[1].name[0]) - // snprintf(str, sizeof(str), "%s", sv->server); - snprintf(str, sizeof(str), "%s", *sv->hostname?sv->hostname:sv->server); - - if (i == viewer->menuop) - for (s = (unsigned char *)str; *s; s++) - { - if ((unsigned)*s >= ' ') - *s = 128 | (*s&~128); - } - WriteString2(&m, str); - WriteString2(&m, "\n"); - } - } - break; - - case MENU_ADMIN: //admin menu - - WriteString2(&m, "\n\nCluster Admin\n\n"); - - if (viewer->menuop < 0) - viewer->menuop = 0; - i = 0; - - WriteString2(&m, " this connection"); - WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); - sprintf(str, "%-20s", "..."); - WriteString2(&m, str); - WriteString2(&m, "\n"); - - WriteString2(&m, " port"); - WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); - sprintf(str, "%-20i", cluster->qwlistenportnum); - WriteString2(&m, str); - WriteString2(&m, "\n"); - - WriteString2(&m, " hostname"); - WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); - sprintf(str, "%-20s", cluster->hostname); - WriteString2(&m, str); - WriteString2(&m, "\n"); - - WriteString2(&m, " master"); - WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); - sprintf(str, "%-20s", cluster->master); - WriteString2(&m, str); - WriteString2(&m, "\n"); - - WriteString2(&m, " password"); - WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); - sprintf(str, "%-20s", "..."); - WriteString2(&m, str); - WriteString2(&m, "\n"); - - WriteString2(&m, " add server"); - WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); - sprintf(str, "%-20s", "..."); - WriteString2(&m, str); - WriteString2(&m, "\n"); - - WriteString2(&m, " add demo"); - WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); - sprintf(str, "%-20s", "..."); - WriteString2(&m, str); - WriteString2(&m, "\n"); - - WriteString2(&m, " choke"); - WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); - sprintf(str, "%-20s", cluster->chokeonnotupdated?"yes":"no"); - WriteString2(&m, str); - WriteString2(&m, "\n"); - - WriteString2(&m, "delay forwarding"); - WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); - sprintf(str, "%-20s", cluster->lateforward?"yes":"no"); - WriteString2(&m, str); - WriteString2(&m, "\n"); - - WriteString2(&m, " talking"); - WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); - sprintf(str, "%-20s", cluster->notalking?"no":"yes"); - WriteString2(&m, str); - WriteString2(&m, "\n"); - - WriteString2(&m, " nobsp"); - WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); - sprintf(str, "%-20s", cluster->nobsp?"yes":"no"); - WriteString2(&m, str); - WriteString2(&m, "\n"); - - WriteString2(&m, " back"); - WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); - sprintf(str, "%-20s", "..."); - WriteString2(&m, str); - WriteString2(&m, "\n"); - - if (viewer->menuop >= i) - viewer->menuop = i - 1; - break; - } - - - WriteByte(&m, 0); - SendBufferToViewer(viewer, m.data, m.cursize, true); -} - static const char dropcmd[] = {svc_stufftext, 'd', 'i', 's', 'c', 'o', 'n', 'n', 'e', 'c', 't', '\n', '\0'}; void QW_FreeViewer(cluster_t *cluster, viewer_t *viewer) diff --git a/fteqtv/rcon.c b/fteqtv/rcon.c index 821ef9fa7..b8949f952 100644 --- a/fteqtv/rcon.c +++ b/fteqtv/rcon.c @@ -448,7 +448,7 @@ void Cmd_GenericQuery(cmdctxt_t *ctx, int dataset) memmove(address+strlen(method), address, ARG_LEN-(1+strlen(method))); strncpy(address, method, strlen(method)); - if (!QTV_NewServerConnection(ctx->cluster, address, password, false, false, false, dataset)) + if (!QTV_NewServerConnection(ctx->cluster, ctx->streamid, 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); @@ -484,7 +484,7 @@ void Cmd_GenericConnect(cmdctxt_t *ctx, char *method) memmove(address+strlen(method), address, ARG_LEN-(1+strlen(method))); strncpy(address, method, strlen(method)); - sv = QTV_NewServerConnection(ctx->cluster, address, password, false, false, false, false); + sv = QTV_NewServerConnection(ctx->cluster, ctx->streamid?ctx->streamid:1, address, password, false, false, false, false); if (!sv) Cmd_Printf(ctx, "Failed to connect to \"%s\", connection aborted\n", address); else @@ -636,9 +636,9 @@ void Cmd_Status(cmdctxt_t *ctx) else if (ctx->qtv->sourcesock == INVALID_SOCKET && !ctx->qtv->sourcefile) Cmd_Printf(ctx, " Connection not established\n"); - if (*ctx->qtv->modellist[1].name) + if (*ctx->qtv->map.modellist[1].name) { - Cmd_Printf(ctx, " Map name %s\n", ctx->qtv->modellist[1].name); + Cmd_Printf(ctx, " Map name %s\n", ctx->qtv->map.modellist[1].name); } if (*ctx->qtv->connectpassword) Cmd_Printf(ctx, " Using a password\n"); @@ -655,9 +655,9 @@ void Cmd_Status(cmdctxt_t *ctx) } */ - if (ctx->qtv->bsp) + if (ctx->qtv->map.bsp) { - Cmd_Printf(ctx, " BSP (%s) is loaded\n", ctx->qtv->mapname); + Cmd_Printf(ctx, " BSP (%s) is loaded\n", ctx->qtv->map.mapname); } } @@ -842,6 +842,8 @@ void Cmd_Streams(cmdctxt_t *ctx) else status = ""; break; + case ERR_PAUSED: + status = " (paused)"; case ERR_DISABLED: status = " (disabled)"; break; @@ -895,13 +897,41 @@ void Cmd_Halt(cmdctxt_t *ctx) Cmd_Printf(ctx, "Stream will disconnect\n"); } } + +void Cmd_Pause(cmdctxt_t *ctx) +{ + if (ctx->qtv->errored == ERR_PAUSED) + { + ctx->qtv->errored = ERR_NONE; + Cmd_Printf(ctx, "Stream unpaused.\n"); + } + else if (ctx->qtv->errored == ERR_NONE) + { + if (ctx->qtv->sourcetype == SRC_DEMO) + { + ctx->qtv->errored = ERR_PAUSED; + Cmd_Printf(ctx, "Stream paused.\n"); + } + else + Cmd_Printf(ctx, "Sorry, only demos may be paused.\n"); + } +} + void Cmd_Resume(cmdctxt_t *ctx) { - if (ctx->qtv->errored == ERR_NONE) - Cmd_Printf(ctx, "Stream is already functional\n"); + if (ctx->qtv->errored == ERR_PAUSED) + { + ctx->qtv->errored = ERR_NONE; + Cmd_Printf(ctx, "Stream unpaused.\n"); + } + else + { + if (ctx->qtv->errored == ERR_NONE) + Cmd_Printf(ctx, "Stream is already functional\n"); - ctx->qtv->errored = ERR_RECONNECT; - Cmd_Printf(ctx, "Stream will attempt to reconnect\n"); + ctx->qtv->errored = ERR_RECONNECT; + Cmd_Printf(ctx, "Stream will attempt to reconnect\n"); + } } void Cmd_Record(cmdctxt_t *ctx) @@ -998,7 +1028,7 @@ void Cmd_BaseDir(cmdctxt_t *ctx) char *val; val = Cmd_Argv(ctx, 1); if (!Cmd_IsLocal(ctx)) - Cmd_Printf(ctx, "Sorry, you may not use this command remotly\n"); + Cmd_Printf(ctx, "Sorry, you may not use this command remotely\n"); if (*val) chdir(val); @@ -1147,8 +1177,9 @@ const rconcommands_t rconcommands[] = - {"halt", 1, 0, Cmd_Halt, "disables a stream, preventing it from reconnecting until someone tries watching it anew"}, + {"halt", 1, 0, Cmd_Halt, "disables a stream, preventing it from reconnecting until someone tries watching it anew. Boots current spectators"}, {"disable", 1, 0, Cmd_Halt}, + {"pause", 1, 0, Cmd_Pause, "Pauses a demo stream."}, {"resume", 1, 0, Cmd_Resume, "reactivates a stream, allowing it to reconnect"}, {"enable", 1, 0, Cmd_Resume}, {"mute", 1, 0, Cmd_MuteStream, "hides prints that come from the game server"}, @@ -1187,13 +1218,15 @@ void Cmd_ExecuteNow(cmdctxt_t *ctx, char *command) { i = atoi(command); command = sid+1; + + ctx->streamid = i; for (ctx->qtv = ctx->cluster->servers; ctx->qtv; ctx->qtv = ctx->qtv->next) if (ctx->qtv->streamid == i) break; } - - + else + ctx->streamid = 0; ctx->argc = 0; for (i = 0; i < MAX_ARGS; i++) diff --git a/fteqtv/sc_dsound.c b/fteqtv/sc_dsound.c new file mode 100644 index 000000000..396a90a1b --- /dev/null +++ b/fteqtv/sc_dsound.c @@ -0,0 +1,191 @@ +#include "qtv.h" + +#ifdef COMMENTARY +#include + +static HANDLE hInstDS; +static HRESULT (WINAPI *pDirectSoundCaptureCreate)(GUID FAR *lpGUID, LPDIRECTSOUNDCAPTURE FAR *lplpDS, IUnknown FAR *pUnkOuter); + +typedef struct { + soundcapt_t funcs; + + LPDIRECTSOUNDCAPTURE DSCapture; + LPDIRECTSOUNDCAPTUREBUFFER DSCaptureBuffer; + long lastreadpos; + + WAVEFORMATEX wfxFormat; + + long bufferbytes; +} dscapture_t; + + +void DSOUND_CloseCapture(soundcapt_t *ghnd) +{ + dscapture_t *hnd = (dscapture_t*)ghnd; + + if (hnd->DSCaptureBuffer) + { + IDirectSoundCaptureBuffer_Stop(hnd->DSCaptureBuffer); + IDirectSoundCaptureBuffer_Release(hnd->DSCaptureBuffer); + hnd->DSCaptureBuffer=NULL; + } + if (hnd->DSCapture) + { + IDirectSoundCapture_Release(hnd->DSCapture); + hnd->DSCapture=NULL; + } + + free(hnd); +} + +int DSOUND_UpdateCapture(soundcapt_t *ghnd, int samplechunks, char *buffer) +{ + dscapture_t *hnd = (dscapture_t*)ghnd; + HRESULT hr; + LPBYTE lpbuf1 = NULL; + LPBYTE lpbuf2 = NULL; + DWORD dwsize1 = 0; + DWORD dwsize2 = 0; + + DWORD capturePos; + DWORD readPos; + long filled; + + int inputbytes; + + inputbytes = hnd->wfxFormat.wBitsPerSample/8; + + +// Query to see how much data is in buffer. + hr = IDirectSoundCaptureBuffer_GetCurrentPosition(hnd->DSCaptureBuffer, &capturePos, &readPos ); + if( hr != DS_OK ) + { + return 0; + } + filled = readPos - hnd->lastreadpos; + if( filled < 0 ) + filled += hnd->bufferbytes; // unwrap offset + + if (filled > samplechunks) //figure out how much we need to empty it by, and if that's enough to be worthwhile. + filled = samplechunks; + else if (filled < samplechunks) + return 0; + + if ((filled/inputbytes) & 1) //force even numbers of samples + filled -= inputbytes; + + // Lock free space in the DS + hr = IDirectSoundCaptureBuffer_Lock ( hnd->DSCaptureBuffer, hnd->lastreadpos, filled, (void **) &lpbuf1, &dwsize1, + (void **) &lpbuf2, &dwsize2, 0); + if (hr == DS_OK) + { + // Copy from DS to the buffer + memcpy( buffer, lpbuf1, dwsize1); + if(lpbuf2 != NULL) + { + memcpy( buffer+dwsize1, lpbuf2, dwsize2); + } + // Update our buffer offset and unlock sound buffer + hnd->lastreadpos = (hnd->lastreadpos + dwsize1 + dwsize2) % (hnd->bufferbytes); + IDirectSoundCaptureBuffer_Unlock ( hnd->DSCaptureBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2); + } + else + { + return 0; + } + return filled/inputbytes; +} + + +soundcapt_t *SND_InitCapture (int speed, int bits) +{ + dscapture_t *hnd; + DSCBUFFERDESC bufdesc; + + if (!hInstDS) + { + hInstDS = LoadLibrary("dsound.dll"); + + if (hInstDS == NULL) + { + printf ("Couldn't load dsound.dll\n"); + return NULL; + } + } + if (!pDirectSoundCaptureCreate) + { + pDirectSoundCaptureCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCaptureCreate"); + + if (!pDirectSoundCaptureCreate) + { + printf ("Couldn't get DS proc addr (DirectSoundCaptureCreate)\n"); + return NULL; + } + + } + + + hnd = malloc(sizeof(dscapture_t)); + memset(hnd, 0, sizeof(*hnd)); + + hnd->wfxFormat.wFormatTag = WAVE_FORMAT_PCM; + hnd->wfxFormat.nChannels = 1; + hnd->wfxFormat.nSamplesPerSec = speed; + hnd->wfxFormat.wBitsPerSample = bits; + hnd->wfxFormat.nBlockAlign = hnd->wfxFormat.nChannels * (hnd->wfxFormat.wBitsPerSample / 8); + hnd->wfxFormat.nAvgBytesPerSec = hnd->wfxFormat.nSamplesPerSec * hnd->wfxFormat.nBlockAlign; + hnd->wfxFormat.cbSize = 0; + + bufdesc.dwSize = sizeof(bufdesc); + bufdesc.dwBufferBytes = hnd->bufferbytes = speed*bits/8; //1 sec + bufdesc.dwFlags = 0; + bufdesc.dwReserved = 0; + bufdesc.lpwfxFormat = &hnd->wfxFormat; + + + + + pDirectSoundCaptureCreate(NULL, &hnd->DSCapture, NULL); + + if (FAILED(IDirectSoundCapture_CreateCaptureBuffer(hnd->DSCapture, &bufdesc, &hnd->DSCaptureBuffer, NULL))) + { + printf ("Couldn't create a capture buffer\n"); + IDirectSoundCapture_Release(hnd->DSCapture); + free(hnd); + return NULL; + } + + IDirectSoundCaptureBuffer_Start(hnd->DSCaptureBuffer, DSBPLAY_LOOPING); + + hnd->lastreadpos = 0; + + hnd->funcs.update = DSOUND_UpdateCapture; + hnd->funcs.close = DSOUND_CloseCapture; + + return &hnd->funcs; +} + + + + +/* +void soundtestcallback (char *buffer, int samples, int bitspersample) +{ + FILE *f; + f = fopen("c:/test.raw", "at"); + fseek(f, 0, SEEK_END); + fwrite(buffer, samples, bitspersample/8, f); + fclose(f); +} + +void soundtest(void) +{ + soundcapt_t *capt; + + capt = SNDDMA_InitCapture(11025, 8); + while(1) + capt->update(capt, 1400, soundtestcallback); + capt->close(capt); +} +*/ +#endif diff --git a/fteqtv/source.c b/fteqtv/source.c index d34d114ee..eb30782c3 100644 --- a/fteqtv/source.c +++ b/fteqtv/source.c @@ -392,11 +392,15 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip) if (!NET_StringToAddr(ip, &qtv->serveraddress, 27500)) { Sys_Printf(qtv->cluster, "Stream %i: Unable to resolve %s\n", qtv->streamid, ip); + strcpy(qtv->status, "Unable to resolve server\n"); return false; } qtv->sourcesock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (qtv->sourcesock == INVALID_SOCKET) + { + strcpy(qtv->status, "Network error\n"); return false; + } memset(&from, 0, sizeof(from)); ((struct sockaddr*)&from)->sa_family = ((struct sockaddr*)&qtv->serveraddress)->sa_family; @@ -404,6 +408,7 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip) { closesocket(qtv->sourcesock); qtv->sourcesock = INVALID_SOCKET; + strcpy(qtv->status, "Network error\n"); return false; } @@ -411,6 +416,7 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip) { closesocket(qtv->sourcesock); qtv->sourcesock = INVALID_SOCKET; + strcpy(qtv->status, "Network error\n"); return false; } @@ -421,6 +427,7 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip) { closesocket(qtv->sourcesock); qtv->sourcesock = INVALID_SOCKET; + strcpy(qtv->status, "Connection failed\n"); return false; } } @@ -621,6 +628,7 @@ void Net_QueueUpstream(sv_t *qtv, int size, char *buffer) if (qtv->upstreambuffersize + size > sizeof(qtv->upstreambuffer)) { Sys_Printf(qtv->cluster, "Stream %i: Upstream queue overflowed for %s\n", qtv->streamid, qtv->server); + strcpy(qtv->status, "Upstream overflow"); qtv->errored = ERR_RECONNECT; return; } @@ -645,9 +653,15 @@ qboolean Net_WriteUpstream(sv_t *qtv) int err; err = qerrno; if (qerrno) + { Sys_Printf(qtv->cluster, "Stream %i: Error: source socket error %i\n", qtv->streamid, qerrno); + strcpy(qtv->status, "Network error\n"); + } else + { Sys_Printf(qtv->cluster, "Stream %i: Error: server %s disconnected\n", qtv->streamid, qtv->server); + strcpy(qtv->status, "Server disconnected"); + } qtv->errored = ERR_RECONNECT; //if the server is down, we'll detect it on reconnect } return false; @@ -1005,21 +1019,22 @@ qboolean QTV_Connect(sv_t *qtv, char *serverurl) qtv->sourcefile = NULL; } - *qtv->serverinfo = '\0'; - Info_SetValueForStarKey(qtv->serverinfo, "*version", "FTEQTV", sizeof(qtv->serverinfo)); - Info_SetValueForStarKey(qtv->serverinfo, "*qtv", VERSION, sizeof(qtv->serverinfo)); - Info_SetValueForStarKey(qtv->serverinfo, "hostname", qtv->cluster->hostname, sizeof(qtv->serverinfo)); - Info_SetValueForStarKey(qtv->serverinfo, "maxclients", "99", sizeof(qtv->serverinfo)); + *qtv->map.serverinfo = '\0'; + Info_SetValueForStarKey(qtv->map.serverinfo, "*version", "FTEQTV", sizeof(qtv->map.serverinfo)); + Info_SetValueForStarKey(qtv->map.serverinfo, "*qtv", VERSION, sizeof(qtv->map.serverinfo)); + Info_SetValueForStarKey(qtv->map.serverinfo, "hostname", qtv->cluster->hostname, sizeof(qtv->map.serverinfo)); + Info_SetValueForStarKey(qtv->map.serverinfo, "maxclients", "99", sizeof(qtv->map.serverinfo)); if (!strncmp(qtv->server, "file:", 5)) - Info_SetValueForStarKey(qtv->serverinfo, "server", "file", sizeof(qtv->serverinfo)); + Info_SetValueForStarKey(qtv->map.serverinfo, "server", "file", sizeof(qtv->map.serverinfo)); else - Info_SetValueForStarKey(qtv->serverinfo, "server", qtv->server, sizeof(qtv->serverinfo)); + Info_SetValueForStarKey(qtv->map.serverinfo, "server", qtv->server, sizeof(qtv->map.serverinfo)); memcpy(qtv->server, serverurl, sizeof(qtv->server)-1); if (qtv->disconnectwhennooneiswatching == 2) { //added because of paranoia rather than need. Should never occur. printf("bug: autoclose==2\n"); + strcpy(qtv->status, "Network error\n"); return false; } else if (!Net_ConnectToServer(qtv)) @@ -1040,6 +1055,62 @@ qboolean QTV_Connect(sv_t *qtv, char *serverurl) return true; } +void QTV_CleanupMap(sv_t *qtv) +{ + int i; + + //free the bsp + BSP_Free(qtv->map.bsp); + qtv->map.bsp = NULL; + + //clean up entity state + for (i = 0; i < ENTITY_FRAMES; i++) + { + if (qtv->map.frame[i].ents) + { + free(qtv->map.frame[i].ents); + qtv->map.frame[i].ents = NULL; + } + if (qtv->map.frame[i].entnums) + { + free(qtv->map.frame[i].entnums); + qtv->map.frame[i].entnums = NULL; + } + } + memset(&qtv->map, 0, sizeof(qtv->map)); +} + +void QTV_DisconnectFromSource(sv_t *qtv) +{ + // close the source handle + if (qtv->sourcesock != INVALID_SOCKET) + { + if (qtv->usequakeworldprotocols) + { + char dying[] = {clc_stringcmd, 'd', 'r', 'o', 'p', '\0'}; + Netchan_Transmit (qtv->cluster, &qtv->netchan, sizeof(dying), dying); + Netchan_Transmit (qtv->cluster, &qtv->netchan, sizeof(dying), dying); + Netchan_Transmit (qtv->cluster, &qtv->netchan, sizeof(dying), dying); + } + closesocket(qtv->sourcesock); + qtv->sourcesock = INVALID_SOCKET; + } + if (qtv->sourcefile) + { + fclose(qtv->sourcefile); + qtv->sourcefile = NULL; + } + + //cancel downloads + if (qtv->downloadfile) + { + fclose(qtv->downloadfile); + qtv->downloadfile = NULL; + unlink(qtv->downloadname); + *qtv->downloadname = '\0'; + } +} + void QTV_Cleanup(sv_t *qtv, qboolean leaveadmins) { //disconnects the stream viewer_t *v; @@ -1065,51 +1136,9 @@ void QTV_Cleanup(sv_t *qtv, qboolean leaveadmins) } } - // close the source handle - if (qtv->sourcesock != INVALID_SOCKET) - { - if (qtv->usequakeworldprotocols) - { - char dying[] = {clc_stringcmd, 'd', 'r', 'o', 'p', '\0'}; - Netchan_Transmit (qtv->cluster, &qtv->netchan, sizeof(dying), dying); - Netchan_Transmit (qtv->cluster, &qtv->netchan, sizeof(dying), dying); - Netchan_Transmit (qtv->cluster, &qtv->netchan, sizeof(dying), dying); - } - closesocket(qtv->sourcesock); - qtv->sourcesock = INVALID_SOCKET; - } - if (qtv->sourcefile) - { - fclose(qtv->sourcefile); - qtv->sourcefile = NULL; - } + QTV_DisconnectFromSource(qtv); - //cancel downloads - if (qtv->downloadfile) - { - fclose(qtv->downloadfile); - qtv->downloadfile = NULL; - unlink(qtv->downloadname); - *qtv->downloadname = '\0'; - } - //free the bsp - BSP_Free(qtv->bsp); - qtv->bsp = NULL; - - //clean up entity state - for (i = 0; i < ENTITY_FRAMES; i++) - { - if (qtv->frame[i].ents) - { - free(qtv->frame[i].ents); - qtv->frame[i].ents = NULL; - } - if (qtv->frame[i].entnums) - { - free(qtv->frame[i].entnums); - qtv->frame[i].entnums = NULL; - } - } + QTV_CleanupMap(qtv); //boot connected downstream proxies for (prox = qtv->proxies; prox; ) @@ -1187,27 +1216,27 @@ void ChooseFavoriteTrack(sv_t *tv) frags = -10000; best = -1; if (tv->controller || tv->proxyplayer) - best = tv->trackplayer; + best = tv->map.trackplayer; else { for (pnum = 0; pnum < MAX_CLIENTS; pnum++) { - if (*tv->players[pnum].userinfo && !atoi(Info_ValueForKey(tv->players[pnum].userinfo, "*spectator", buffer, sizeof(buffer)))) + if (*tv->map.players[pnum].userinfo && !atoi(Info_ValueForKey(tv->map.players[pnum].userinfo, "*spectator", buffer, sizeof(buffer)))) { - if (tv->thisplayer == pnum) + if (tv->map.thisplayer == pnum) continue; - if (frags < tv->players[pnum].frags) + if (frags < tv->map.players[pnum].frags) { best = pnum; - frags = tv->players[pnum].frags; + frags = tv->map.players[pnum].frags; } } } } - if (best != tv->trackplayer) + if (best != tv->map.trackplayer) { SendClientCommand (tv, "ptrack %i\n", best); - tv->trackplayer = best; + tv->map.trackplayer = best; if (tv->usequakeworldprotocols) QW_StreamStuffcmd(tv->cluster, tv, "track %i\n", best); @@ -1341,7 +1370,7 @@ void QTV_ParseQWStream(sv_t *qtv) strcpy(qtv->status, "Waiting for gamestate\n"); Netchan_Setup(qtv->sourcesock, &qtv->netchan, qtv->serveraddress, qtv->qport, true); - qtv->trackplayer = -1; + qtv->map.trackplayer = -1; qtv->isconnected = true; qtv->timeout = qtv->curtime + UDPTIMEOUT_LENGTH; @@ -1531,8 +1560,17 @@ void QTV_Run(sv_t *qtv) } + if (qtv->errored == ERR_PAUSED) + { + if (!qtv->parsingconnectiondata) + qtv->parsetime = qtv->curtime; + } + if (qtv->errored == ERR_RECONNECT) { + qtv->buffersize = 0; + qtv->forwardpoint = 0; + QTV_DisconnectFromSource(qtv); qtv->errored = ERR_NONE; qtv->nextconnectattempt = qtv->curtime; //make the reconnect happen _now_ } @@ -1557,7 +1595,9 @@ void QTV_Run(sv_t *qtv) if (qtv->sourcesock == INVALID_SOCKET && !qtv->sourcefile) { if (!QTV_Connect(qtv, qtv->server)) //reconnect it + { qtv->errored = ERR_PERMANENT; + } } if (qtv->errored == ERR_NONE) Netchan_OutOfBand(qtv->cluster, qtv->sourcesock, qtv->serveraddress, 13, "getchallenge\n"); @@ -1610,14 +1650,14 @@ void QTV_Run(sv_t *qtv) } ChooseFavoriteTrack(qtv); - if (qtv->trackplayer >= 0) + if (qtv->map.trackplayer >= 0) { qtv->packetratelimiter += UDPPACKETINTERVAL; WriteByte(&msg, clc_tmove); - WriteShort(&msg, qtv->players[qtv->trackplayer].current.origin[0]); - WriteShort(&msg, qtv->players[qtv->trackplayer].current.origin[1]); - WriteShort(&msg, qtv->players[qtv->trackplayer].current.origin[2]); + WriteShort(&msg, qtv->map.players[qtv->map.trackplayer].current.origin[0]); + WriteShort(&msg, qtv->map.players[qtv->map.trackplayer].current.origin[1]); + WriteShort(&msg, qtv->map.players[qtv->map.trackplayer].current.origin[2]); } else if (qtv->controller) { @@ -1642,7 +1682,7 @@ void QTV_Run(sv_t *qtv) SetMoveCRC(qtv, &msg); } - else if (qtv->proxyplayer || qtv->trackplayer < 0) + else if (qtv->proxyplayer || qtv->map.trackplayer < 0) { usercmd_t *cmd[3]; cmd[0] = &qtv->proxyplayerucmds[(qtv->proxyplayerucmdnum-2)%3]; @@ -1677,18 +1717,18 @@ void QTV_Run(sv_t *qtv) to = qtv->netchan.outgoing_sequence & (ENTITY_FRAMES-1); from = qtv->netchan.incoming_sequence & (ENTITY_FRAMES-1); - if (qtv->frame[from].numents) + if (qtv->map.frame[from].numents) { //remember which one we came from - qtv->frame[to].oldframe = from; + qtv->map.frame[to].oldframe = from; WriteByte(&msg, clc_delta); - WriteByte(&msg, qtv->frame[to].oldframe); //let the server know + WriteByte(&msg, qtv->map.frame[to].oldframe); //let the server know } else - qtv->frame[to].oldframe = -1; + qtv->map.frame[to].oldframe = -1; - qtv->frame[to].numents = 0; + qtv->map.frame[to].numents = 0; Netchan_Transmit(qtv->cluster, &qtv->netchan, msg.cursize, msg.data); } @@ -2067,7 +2107,7 @@ void QTV_Run(sv_t *qtv) } } -sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, char *password, qboolean force, qboolean autoclose, qboolean noduplicates, qboolean query) +sv_t *QTV_NewServerConnection(cluster_t *cluster, int newstreamid, char *server, char *password, qboolean force, qboolean autoclose, qboolean noduplicates, qboolean query) { sv_t *qtv; @@ -2087,6 +2127,20 @@ sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, char *password, } } } + if (!newstreamid) //no fixed id? generate a default id + newstreamid = 100; + //make sure it doesn't conflict + for(;;newstreamid++) + { + for (qtv = cluster->servers; qtv; qtv = qtv->next) + { + if (qtv->streamid == newstreamid) + break; + } + if (!qtv) + break; + } + if (autoclose) if (cluster->nouserconnects) return NULL; @@ -2110,7 +2164,7 @@ sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, char *password, qtv->silentstream = true; qtv->parsespeed = 1000; - qtv->streamid = ++cluster->nextstreamid; + qtv->streamid = newstreamid; qtv->cluster = cluster; qtv->next = cluster->servers; diff --git a/fteqtv/sp_dsound.c b/fteqtv/sp_dsound.c new file mode 100644 index 000000000..6b680d07f --- /dev/null +++ b/fteqtv/sp_dsound.c @@ -0,0 +1,251 @@ +#include "qtv.h" +#ifdef COMMENTARY +#include + +static HANDLE hInstDS; +static HRESULT (WINAPI *pDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN); + +typedef struct { + soundplay_t funcs; + + LPDIRECTSOUND ds; + LPDIRECTSOUNDBUFFER dsbuf; + + int buffersize; + + int writepos; + int readpos; + + int sampbytes; + +} dsplay_t; + +int DSOUND_UpdatePlayback(soundplay_t *sp, int samplechunks, char *buffer) +{ + int ret; + dsplay_t *dsp = (dsplay_t*)sp; + char *sbuf; + int sbufsize; + int writable; + int remaining = samplechunks; + + if (!samplechunks) + return 0; + + IDirectSoundBuffer_GetCurrentPosition(dsp->dsbuf, &dsp->readpos, NULL); + dsp->readpos /= dsp->sampbytes; + + while (ret = IDirectSoundBuffer_Lock(dsp->dsbuf, 0, dsp->buffersize*dsp->sampbytes, (void**)&sbuf, &sbufsize, NULL, NULL, 0)) + { + if (!FAILED(ret)) + break; + if (ret == DSERR_BUFFERLOST) + printf("Buffer lost\n"); + else + break; + +// if (FAILED(IDirectSoundBuffer_Resore(dsp->dsbuf))) +// return 0; + } + //memset(sbuf, 0, sbufsize); + writable = remaining; + if (writable > sbufsize/dsp->sampbytes - dsp->writepos) + writable = sbufsize/dsp->sampbytes - dsp->writepos; + memcpy(sbuf+dsp->writepos*dsp->sampbytes, buffer, writable*dsp->sampbytes); + remaining -= writable; + buffer += writable*dsp->sampbytes; + dsp->writepos += writable; + dsp->writepos %= dsp->buffersize; + if (samplechunks > 0) + { + writable = remaining; + if (writable > dsp->readpos) + writable = dsp->readpos; + memcpy(sbuf, buffer, writable*dsp->sampbytes); + remaining -= writable; + dsp->writepos += writable; + dsp->writepos %= dsp->buffersize; + } + IDirectSoundBuffer_Unlock(dsp->dsbuf, sbuf, sbufsize, NULL, 0); + + printf("%i %i\n", 100*dsp->readpos / dsp->buffersize, 100*dsp->writepos / dsp->buffersize); + + return samplechunks - remaining; +} + +void DSOUND_Shutdown(soundplay_t *dsp) +{ +} + +soundplay_t *SND_InitPlayback(int speed, int bits) +{ + int ret; + DSBCAPS caps; + DSBUFFERDESC bufdesc; + LPDIRECTSOUND ds; + dsplay_t *hnd; + WAVEFORMATEX format; + + if (!hInstDS) + { + hInstDS = LoadLibrary("dsound.dll"); + + if (hInstDS == NULL) + { + printf ("Couldn't load dsound.dll\n"); + return NULL; + } + + pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate"); + + if (!pDirectSoundCreate) + { + printf ("Couldn't get DS proc addr\n"); + return NULL; + } + +// pDirectSoundEnumerate = (void *)GetProcAddress(hInstDS,"DirectSoundEnumerateA"); + } + + ds = NULL; + pDirectSoundCreate(NULL, &ds, NULL); + + if (!ds) + return NULL; + hnd = malloc(sizeof(*hnd)); + memset(hnd, 0, sizeof(*hnd)); + + hnd->funcs.update = DSOUND_UpdatePlayback; + hnd->funcs.close = DSOUND_Shutdown; + + hnd->ds = ds; + hnd->sampbytes = bits/8; + + if (FAILED(IDirectSound_SetCooperativeLevel (hnd->ds, GetDesktopWindow(), DSSCL_EXCLUSIVE))) + printf("SetCooperativeLevel failed\n"); + + memset(&bufdesc, 0, sizeof(bufdesc)); + bufdesc.dwSize = sizeof(bufdesc); +// bufdesc.dwFlags |= DSBCAPS_GLOBALFOCUS; //so we hear it if quake is loaded + bufdesc.dwFlags |= DSBCAPS_PRIMARYBUFFER; //so we can set speed + bufdesc.dwFlags |= DSBCAPS_CTRLVOLUME; + bufdesc.lpwfxFormat = NULL; + bufdesc.dwBufferBytes = 0; + + format.wFormatTag = WAVE_FORMAT_PCM; + format.cbSize = 0; + + format.nChannels = 1; + format.wBitsPerSample = bits; + format.nSamplesPerSec = speed; + format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; + format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; + + ret = IDirectSound_CreateSoundBuffer(hnd->ds, &bufdesc, &hnd->dsbuf, NULL); + + if (!hnd->dsbuf) + { + printf("Couldn't create primary buffer\n"); + DSOUND_Shutdown(&hnd->funcs); + return NULL; + } + + if (FAILED(IDirectSoundBuffer_SetFormat(hnd->dsbuf, &format))) + printf("SetFormat failed\n"); + + //and now make a secondary buffer + bufdesc.dwFlags = 0; + bufdesc.dwFlags |= DSBCAPS_CTRLFREQUENCY; + bufdesc.dwFlags |= DSBCAPS_LOCSOFTWARE; + bufdesc.dwFlags |= DSBCAPS_GLOBALFOCUS; + bufdesc.dwBufferBytes = speed * format.nChannels * hnd->sampbytes; + bufdesc.lpwfxFormat = &format; + + ret = IDirectSound_CreateSoundBuffer(hnd->ds, &bufdesc, &hnd->dsbuf, NULL); + if (!hnd->dsbuf) + { + printf("Couldn't create secondary buffer\n"); + DSOUND_Shutdown(&hnd->funcs); + return NULL; + } + + + + + + + + + memset(&caps, 0, sizeof(caps)); + caps.dwSize = sizeof(caps); + IDirectSoundBuffer_GetCaps(hnd->dsbuf, &caps); + hnd->buffersize = caps.dwBufferBytes / hnd->sampbytes; + + //clear out the buffer + { + char *buffer; + int buffersize=0; + IDirectSoundBuffer_Play(hnd->dsbuf, 0, 0, DSBPLAY_LOOPING); + ret = IDirectSoundBuffer_Lock(hnd->dsbuf, 0, hnd->buffersize*hnd->sampbytes, (void**)&buffer, &buffersize, NULL, NULL, 0); + memset(buffer, 0, buffersize); + IDirectSoundBuffer_Unlock(hnd->dsbuf, buffer, buffersize, NULL, 0); + IDirectSoundBuffer_Stop(hnd->dsbuf); + } +//DSERR_INVALIDPARAM + IDirectSoundBuffer_Play(hnd->dsbuf, 0, 0, DSBPLAY_LOOPING); + + + IDirectSoundBuffer_GetCurrentPosition(hnd->dsbuf, &hnd->readpos, &hnd->writepos); + + hnd->writepos = hnd->readpos + speed / 2; //position our write position a quater of a second infront of the read position + + printf("%i %i\n", 100*hnd->readpos / hnd->buffersize, 100*hnd->writepos / hnd->buffersize); + + return &hnd->funcs; +} + + +/* + + +void soundtest(void) +{ + int speed = 22100*2; + int bits = 16; + + int i; + + int sampsavailable; + short buffer[1024]; + soundcapt_t *capt; + soundplay_t *play; + + capt = SND_InitCapture (speed, bits); + if (!capt) + { + printf("Failed to init capturer\n"); + exit(0); + } + + play = SND_InitPlayback (speed, bits); + if (!play) + { + printf("Failed to init playback\n"); + exit(0); + } + + + + + for(;;) + { + for (i = 0; i < sizeof(buffer)/sizeof(buffer[0]); i++) + buffer[i] = rand(); + sampsavailable = capt->update(capt, sizeof(buffer)/(bits/8), (char*)buffer); + play->update(play, sampsavailable, (char*)buffer); + + Sleep(1); + } +} +*/ +#endif