Broken more stuff.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@3246 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2009-07-07 23:40:06 +00:00
parent 6147a2dcd3
commit 0e3193a3ef
13 changed files with 1906 additions and 1055 deletions

View File

@ -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);

View File

@ -8,7 +8,9 @@ Contains the control routines that handle both incoming and outgoing stuff
#ifndef _WIN32
#include <sys/stat.h>
#include <dirent.h>
#include <dirent.h>
#else
#include <direct.h>
#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;
}
}
}
}
*/

View File

@ -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

View File

@ -188,7 +188,7 @@ static void HTTPSV_GenerateNowPlaying(cluster_t *cluster, oproxy_t *dest)
for (streams = cluster->servers; streams; streams = streams->next)
{
HTMLPRINT("<dt>");
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), "<span class=\"qtvfile\"> [ <a href=\"/watch.qtv?sid=%i\">Watch Now</a> ]</span>", 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("<li class=\"spectator\">");
}

561
fteqtv/menu.c Normal file
View File

@ -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 && i<min; sv = sv->next, i++)
{//skip over the early connections.
}
min+=20;
for (; sv && i < min; sv = sv->next, i++)
{
if (i == viewer->menuop)
{
/*if (sv->parsingconnectiondata || !sv->modellist[1].name[0])
{
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 && i<min; sv = sv->next, i++)
{//skip over the early connections.
}
min+=20;
for (; sv && i < min; sv = sv->next, i++)
{
//Info_ValueForKey(sv->serverinfo, "hostname", str, sizeof(str));
//if (sv->parsingconnectiondata || !sv->modellist[1].name[0])
// snprintf(str, sizeof(str), "%s", sv->server);
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);
}

View File

@ -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<<i) )
tv->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<<pnum))
tv->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<<pnum))
tv->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);

View File

@ -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);

View File

@ -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"

File diff suppressed because it is too large Load Diff

View File

@ -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++)

191
fteqtv/sc_dsound.c Normal file
View File

@ -0,0 +1,191 @@
#include "qtv.h"
#ifdef COMMENTARY
#include <dsound.h>
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

View File

@ -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;

251
fteqtv/sp_dsound.c Normal file
View File

@ -0,0 +1,251 @@
#include "qtv.h"
#ifdef COMMENTARY
#include <dsound.h>
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