WARNING: Includes protocol changes.

Protocol changes will allow for passwords/commentator access.
Restructured some of the code to clean it up.
Added mini-http server for serving up qtv files for streams.
Mostly supports acting as an nq server, not totally finished yet.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@2380 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2006-09-17 01:27:32 +00:00
parent 42c43d960f
commit 1d4ada39d2
11 changed files with 3626 additions and 1201 deletions

View file

@ -4,15 +4,15 @@ STRIP=strip
STRIPFLAGS=--strip-unneeded --remove-section=.comment STRIPFLAGS=--strip-unneeded --remove-section=.comment
OBJS = netchan.o parse.o qw.o source.o bsp.o rcon.o mdfour.o crc.o OBJS = netchan.o parse.o qw.o source.o bsp.o rcon.o mdfour.o crc.o control.o forward.o
qtv: $(OBJS) qtv.h qtv: $(OBJS) qtv.h
$(CC) $(CFLAGS) $(OBJS) -o $@.db -lm $(LDFLAGS) $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $@.db -lm
$(STRIP) $(STRIPFLAGS) $@.db -o $@ $(STRIP) $(STRIPFLAGS) $@.db -o $@
qtv.exe: qtv.exe: *.c *.h
$(MAKE) qtv CFLAGS=-mno-cygwin LDFLAGS="-lwsock32 -lwinmm" $(MAKE) qtv CFLAGS=-mno-cygwin LDFLAGS="-lwsock32 -lwinmm"
mv qtv qtv.exe mv qtv qtv.exe
clean: clean:
rm -rf qtv qtv.db *.o rm -rf qtv qtv.exe qtv.db *.o

View file

@ -113,92 +113,6 @@ void DecompressVis(unsigned char *in, unsigned char *out, int bytecount)
} }
} }
typedef struct {
char name[56];
int offset;
int length;
} pakfile;
// PACK, offset, lengthofpakfiles
FILE *FindInPaks(char *gamedir, char *filename, int *size)
{
FILE *f;
char fname[1024];
int i, j;
int numfiles;
unsigned int header[3];
pakfile pf;
for (i = 0; ; i++)
{
sprintf(fname, "%s/pak%i.pak", gamedir, i);
f = fopen(fname, "rb");
if (!f)
return NULL; //ran out of possible pak files.
fread(header, 1, sizeof(header), f);
if (header[0] != *(unsigned int*)"PACK")
{ //err... hmm.
fclose(f);
continue;
}
numfiles = LittleLong(header[2])/sizeof(pakfile);
fseek(f, LittleLong(header[1]), SEEK_SET);
for (j = 0; j < numfiles; j++)
{
fread(&pf, 1, sizeof(pf), f);
if (!strcmp(pf.name, filename))
{
fseek(f, LittleLong(pf.offset), 0);
*size = LittleLong(pf.length);
return f;
}
}
fclose(f);
//not found
}
return NULL;
}
unsigned char *ReadFile_WINDOWSSUCKS(char *gamedir, char *filename, 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);
return data;
}
bsp_t *BSP_LoadModel(cluster_t *cluster, char *gamedir, char *bspname) bsp_t *BSP_LoadModel(cluster_t *cluster, char *gamedir, char *bspname)
{ {
unsigned char *data; unsigned char *data;
@ -215,18 +129,11 @@ bsp_t *BSP_LoadModel(cluster_t *cluster, char *gamedir, char *bspname)
bsp_t *bsp; bsp_t *bsp;
if (!gamedir || !*gamedir) data = FS_ReadFile(gamedir, bspname, &size);
gamedir = "qw";
data = ReadFile_WINDOWSSUCKS(gamedir, bspname, &size);
if (!data) if (!data)
{ {
data = ReadFile_WINDOWSSUCKS("id1", bspname, &size); Sys_Printf(cluster, "Couldn't open bsp file \"%s\" (gamedir \"%s\")\n", bspname, gamedir);
if (!data) return NULL;
{
Sys_Printf(cluster, "Couldn't open bsp file \"%s\" (gamedir \"%s\")\n", bspname, gamedir);
return NULL;
}
} }

441
fteqtv/control.c Normal file
View file

@ -0,0 +1,441 @@
/*
Contains the control routines that handle both incoming and outgoing stuff
*/
#include "qtv.h"
typedef struct {
char name[56];
int offset;
int length;
} pakfile;
// PACK, offset, lengthofpakfiles
FILE *FindInPaks(char *gamedir, char *filename, int *size)
{
FILE *f;
char fname[1024];
int i, j;
int numfiles;
unsigned int header[3];
pakfile pf;
for (i = 0; ; i++)
{
sprintf(fname, "%s/pak%i.pak", gamedir, i);
f = fopen(fname, "rb");
if (!f)
return NULL; //ran out of possible pak files.
fread(header, 1, sizeof(header), f);
if (header[0] != *(unsigned int*)"PACK")
{ //err... hmm.
fclose(f);
continue;
}
numfiles = LittleLong(header[2])/sizeof(pakfile);
fseek(f, LittleLong(header[1]), SEEK_SET);
for (j = 0; j < numfiles; j++)
{
fread(&pf, 1, sizeof(pf), f);
if (!strcmp(pf.name, filename))
{
fseek(f, LittleLong(pf.offset), 0);
if (size)
*size = LittleLong(pf.length);
return f;
}
}
fclose(f);
//not found
}
return NULL;
}
unsigned char *FS_ReadFile2(char *gamedir, char *filename, unsigned int *sizep)
{
int size;
unsigned char *data;
FILE *f;
char fname[1024];
if (!*filename)
return NULL;
//try and read it straight out of the file system
sprintf(fname, "%s/%s", gamedir, filename);
f = fopen(fname, "rb");
if (!f)
f = fopen(filename, "rb"); //see if we're being run from inside the gamedir
if (!f)
{
f = FindInPaks(gamedir, filename, &size);
if (!f)
f = FindInPaks("id1", filename, &size);
if (!f)
{
return NULL;
}
}
else
{
fseek(f, 0, SEEK_END);
size = ftell(f);
fseek(f, 0, SEEK_SET);
}
data = malloc(size);
if (data)
fread(data, 1, size, f);
fclose(f);
if (sizep)
*sizep = size;
return data;
}
unsigned char *FS_ReadFile(char *gamedir, char *filename, unsigned int *size)
{
char *data;
if (!gamedir || !*gamedir || !strcmp(gamedir, "qw"))
data = NULL;
else
data = FS_ReadFile2(gamedir, filename, size);
if (!data)
{
data = FS_ReadFile2("qw", filename, size);
if (!data)
{
data = FS_ReadFile2("id1", filename, size);
if (!data)
{
return NULL;
}
}
}
return data;
}
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
timeout.tv_sec = 100/1000;
timeout.tv_usec = (100%1000)*1000;
m = select(m, &socketset, NULL, NULL, &timeout);
#ifdef _WIN32
for (;;)
{
char buffer[8192];
char *result;
char c;
if (!_kbhit())
break;
c = _getch();
if (c == '\n' || c == '\r')
{
Sys_Printf(cluster, "\n");
if (cluster->inputlength)
{
cluster->commandinput[cluster->inputlength] = '\0';
result = Rcon_Command(cluster, NULL, cluster->commandinput, buffer, sizeof(buffer), true);
Sys_Printf(cluster, "%s", result);
cluster->inputlength = 0;
cluster->commandinput[0] = '\0';
}
}
else if (c == '\b')
{
if (cluster->inputlength > 0)
{
Sys_Printf(cluster, "%c", c);
Sys_Printf(cluster, " ", c);
Sys_Printf(cluster, "%c", c);
cluster->inputlength--;
cluster->commandinput[cluster->inputlength] = '\0';
}
}
else
{
Sys_Printf(cluster, "%c", c);
if (cluster->inputlength < sizeof(cluster->commandinput)-1)
{
cluster->commandinput[cluster->inputlength++] = c;
cluster->commandinput[cluster->inputlength] = '\0';
}
}
}
#else
if (FD_ISSET(STDIN, &socketset))
{
char buffer[8192];
char *result;
cluster->inputlength = read (0, cluster->commandinput, sizeof(cluster->commandinput));
if (cluster->inputlength >= 1)
{
cluster->commandinput[cluster->inputlength-1] = 0; // rip off the /n and terminate
if (cluster->inputlength)
{
cluster->commandinput[cluster->inputlength] = '\0';
result = Rcon_Command(cluster, NULL, cluster->commandinput, buffer, sizeof(buffer), true);
printf("%s", result);
cluster->inputlength = 0;
cluster->commandinput[0] = '\0';
}
}
}
#endif
}
cluster->curtime = Sys_Milliseconds();
for (sv = cluster->servers; sv; )
{
old = sv;
sv = sv->next;
QTV_Run(old);
}
SV_FindProxies(cluster->tcpsocket, cluster, NULL); //look for any other proxies wanting to muscle in on the action.
QW_UpdateUDPStuff(cluster);
while(cluster->pendingproxies)
{
pend2 = cluster->pendingproxies->next;
if (SV_ReadPendingProxy(cluster, cluster->pendingproxies))
cluster->pendingproxies = pend2;
else
break;
}
if (cluster->pendingproxies)
{
for(pend = cluster->pendingproxies; pend && pend->next; )
{
pend2 = pend->next;
pend3 = pend2->next;
if (SV_ReadPendingProxy(cluster, pend2))
{
pend->next = pend3;
pend = pend3;
}
else
{
pend = pend2;
}
}
}
}
void DoCommandLine(cluster_t *cluster, int argc, char **argv)
{
int i;
char commandline[8192];
char *start, *end, *result;
char buffer[8192];
commandline[0] = '\0';
//build a block of strings.
for (i = 1; i < argc; i++)
{
strcat(commandline, argv[i]);
strcat(commandline, " ");
}
strcat(commandline, "+");
start = commandline;
while(start)
{
end = strchr(start+1, '+');
if (end)
*end = '\0';
if (start[1])
{
result = Rcon_Command(cluster, NULL, start+1, buffer, sizeof(buffer), true);
Sys_Printf(cluster, "%s", result);
}
start = end;
}
}
int main(int argc, char **argv)
{
cluster_t cluster;
#ifndef _WIN32
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
#endif
#endif
#ifdef _WIN32
{
WSADATA discard;
WSAStartup(MAKEWORD(2,0), &discard);
}
#endif
memset(&cluster, 0, sizeof(cluster));
cluster.qwdsocket = INVALID_SOCKET;
cluster.tcpsocket = INVALID_SOCKET;
cluster.qwlistenportnum = 0;
strcpy(cluster.hostname, DEFAULT_HOSTNAME);
if (argc >= 2 && (!strcmp(argv[1], "-view") || !strcmp(argv[1], "-play")))
{
#ifdef VIEWER
sv_t *sv;
sv_t *svtest;
char sourcename[256];
char *s;
printf("Please enter a QTV source\n");
printf("eg: file:test.mvd\n");
printf("eg: udp:localhost:27500\n");
printf("eg: tcp:localhost:27599\n");
fgets(sourcename, sizeof(sourcename), stdin);
for (s = sourcename + strlen(sourcename)-1; s>=sourcename; s--)
{
if (*s == '\r' || *s == '\n')
*s = '\0';
else
break;
}
sv = QTV_NewServerConnection(&cluster, sourcename, "", false, false, true);
if (!sv)
{
printf("Unable to connect\n");
return 0;
}
if (!strcmp(argv[1], "-play"))
sv->proxyplayer = true;
DemoViewer_Init();
while (!cluster.wanttoexit)
{
Cluster_Run(&cluster, false);
for (svtest = cluster.servers; svtest; svtest = svtest->next)
{ //not the cleanest way to do this, of course
if (svtest == sv)
break;
}
if (svtest)
DemoViewer_Update(svtest);
else
cluster.wanttoexit = true;
}
DemoViewer_Shutdown();
while(cluster.viewers)
QW_FreeViewer(&cluster, cluster.viewers);
while(cluster.servers)
QTV_Shutdown(cluster.servers);
#else
Sys_Printf(&cluster, "Demo viewer is not enabled in this build. Sorry.\n");
#endif
return 0;
}
DoCommandLine(&cluster, argc, argv);
if (!cluster.numservers)
{ //probably running on a home user's computer
if (cluster.qwdsocket == INVALID_SOCKET && !cluster.qwlistenportnum)
{
cluster.qwdsocket = QW_InitUDPSocket(cluster.qwlistenportnum = 27599);
if (cluster.qwdsocket != INVALID_SOCKET)
Sys_Printf(&cluster, "opened udp port %i\n", cluster.qwlistenportnum);
}
if (cluster.tcpsocket == INVALID_SOCKET && !cluster.tcplistenportnum)
{
cluster.tcpsocket = Net_MVDListen(cluster.tcplistenportnum = 27599);
if (cluster.tcpsocket != INVALID_SOCKET)
Sys_Printf(&cluster, "opened tcp port %i\n", cluster.tcplistenportnum);
}
Sys_Printf(&cluster, "\n"
"Welcome to FTEQTV\n"
"Please type\n"
"qtv server:port\n"
" to connect to a tcp server.\n"
"qw server:port\n"
" to connect to a regular qw server.\n"
"demo qw/example.mvd\n"
" to play a demo from an mvd.\n"
"\n");
}
while (!cluster.wanttoexit)
Cluster_Run(&cluster, true);
return 0;
}
void Sys_Printf(cluster_t *cluster, char *fmt, ...)
{
va_list argptr;
char string[2024];
va_start (argptr, fmt);
vsnprintf (string, sizeof(string), fmt,argptr);
va_end (argptr);
printf("%s", string);
}

840
fteqtv/forward.c Normal file
View file

@ -0,0 +1,840 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the included (GNU.txt) GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
This is the file responsible for handling incoming tcp connections.
This includes mvd recording.
Password checks and stuff are implemented here. This is server side stuff.
*/
#include "qtv.h"
#undef IN
#define IN(x) buffer[(x)&(MAX_PROXY_BUFFER-1)]
void CheckMVDConsistancy(unsigned char *buffer, int pos, int size)
{
/*
int length;
int msec, type;
while(pos < size)
{
msec = IN(pos++);
type = IN(pos++);
if (type == dem_set)
{
pos+=8;
continue;
}
if (type == dem_multiple)
pos+=4;
length = (IN(pos+0)<<0) + (IN(pos+1)<<8) + (IN(pos+2)<<16) + (IN(pos+3)<<24);
pos+=4;
if (length > 1450)
printf("too big (%i)\n", length);
pos += length;
}
if (pos != size)
printf("pos != size\n");
*/
}
void SV_FindProxies(SOCKET sock, cluster_t *cluster, sv_t *defaultqtv)
{
oproxy_t *prox;
sock = accept(sock, NULL, NULL);
if (sock == INVALID_SOCKET)
return;
if (cluster->numproxies >= cluster->maxproxies && cluster->maxproxies)
{
const char buffer[] = {dem_all, 1, 'P','r','o','x','y',' ','i','s',' ','f','u','l','l','.'};
send(sock, buffer, strlen(buffer), 0);
closesocket(sock);
return;
}
prox = malloc(sizeof(*prox));
if (!prox)
{//out of mem?
closesocket(sock);
return;
}
memset(prox, 0, sizeof(*prox));
prox->sock = sock;
prox->file = NULL;
cluster->numproxies++;
#if 1
prox->defaultstream = defaultqtv;
prox->next = cluster->pendingproxies;
cluster->pendingproxies = prox;
#else
prox->next = qtv->pendingproxies;
qtv->pendingproxies = prox;
Net_SendConnectionMVD(qtv, prox);
#endif
}
void Net_TryFlushProxyBuffer(cluster_t *cluster, oproxy_t *prox)
{
char *buffer;
int length;
int bufpos;
if (prox->drop)
return;
while (prox->bufferpos >= MAX_PROXY_BUFFER)
{ //so we never get any issues with wrapping..
prox->bufferpos -= MAX_PROXY_BUFFER;
prox->buffersize -= MAX_PROXY_BUFFER;
}
bufpos = prox->bufferpos&(MAX_PROXY_BUFFER-1);
length = prox->buffersize - prox->bufferpos;
if (length > MAX_PROXY_BUFFER-bufpos) //cap the length correctly.
length = MAX_PROXY_BUFFER-bufpos;
if (!length)
return; //already flushed.
buffer = prox->buffer + bufpos;
// CheckMVDConsistancy(prox->buffer, prox->bufferpos, prox->buffersize);
if (bufpos+length > MAX_PROXY_BUFFER)
Sys_Printf(cluster, "oversize flush\n");
if (prox->file)
length = fwrite(buffer, 1, length, prox->file);
else
length = send(prox->sock, buffer, length, 0);
switch (length)
{
case 0: //eof / they disconnected
prox->drop = true;
break;
case -1:
if (qerrno != EWOULDBLOCK) //not a problem, so long as we can flush it later.
prox->drop = true; //drop them if we get any errors
break;
default:
prox->bufferpos += length;
}
}
void Net_ProxySend(cluster_t *cluster, oproxy_t *prox, char *buffer, int length)
{
int wrap;
if (prox->buffersize-prox->bufferpos + length > MAX_PROXY_BUFFER)
{
Net_TryFlushProxyBuffer(cluster, prox); //try flushing
if (prox->buffersize-prox->bufferpos + length > MAX_PROXY_BUFFER) //damn, still too big.
{ //they're too slow. hopefully it was just momentary lag
prox->flushing = true;
return;
}
}
#if 1
//just simple
prox->buffersize+=length;
for (wrap = prox->buffersize-length; wrap < prox->buffersize; wrap++)
prox->buffer[wrap&(MAX_PROXY_BUFFER-1)] = *buffer++;
#else
//we don't do multiple wrappings, the above check cannot succeed if it were required.
//find the wrap point
wrap = prox->buffersize-(prox->buffersize&(MAX_PROXY_BUFFER-1)) + MAX_PROXY_BUFFER;
wrap = wrap - (prox->buffersize&(MAX_PROXY_BUFFER-1)); //the ammount of data we can fit before wrapping.
if (wrap > length)
{ //we don't wrap afterall
memcpy(prox->buffer+(prox->buffersize)&(MAX_PROXY_BUFFER-1), buffer, length);
prox->buffersize+=length;
return;
}
memcpy(prox->buffer+prox->buffersize&(MAX_PROXY_BUFFER-1), buffer, wrap);
buffer += wrap;
length -= wrap;
memcpy(prox->buffer, buffer, length);
prox->buffersize+=length;
#endif
}
void Prox_SendMessage(cluster_t *cluster, oproxy_t *prox, char *buf, int length, int dem_type, unsigned int playermask)
{
netmsg_t msg;
char tbuf[16];
InitNetMsg(&msg, tbuf, sizeof(tbuf));
WriteByte(&msg, 0);
WriteByte(&msg, dem_type);
WriteLong(&msg, length);
if (dem_type == dem_multiple)
WriteLong(&msg, playermask);
if (prox->buffersize-prox->bufferpos + length + msg.cursize > MAX_PROXY_BUFFER)
{
Net_TryFlushProxyBuffer(cluster, prox); //try flushing
if (prox->buffersize-prox->bufferpos + length + msg.cursize > MAX_PROXY_BUFFER) //damn, still too big.
{ //they're too slow. hopefully it was just momentary lag
prox->flushing = true;
return;
}
}
Net_ProxySend(cluster, prox, msg.data, msg.cursize);
Net_ProxySend(cluster, prox, buf, length);
}
void Prox_SendPlayerStats(sv_t *qtv, oproxy_t *prox)
{
char buffer[MAX_MSGLEN];
netmsg_t msg;
int player, snum;
InitNetMsg(&msg, buffer, sizeof(buffer));
for (player = 0; player < MAX_CLIENTS; player++)
{
for (snum = 0; snum < MAX_STATS; snum++)
{
if (qtv->players[player].stats[snum])
{
if ((unsigned)qtv->players[player].stats[snum] > 255)
{
WriteByte(&msg, svc_updatestatlong);
WriteByte(&msg, snum);
WriteLong(&msg, qtv->players[player].stats[snum]);
}
else
{
WriteByte(&msg, svc_updatestat);
WriteByte(&msg, snum);
WriteByte(&msg, qtv->players[player].stats[snum]);
}
}
}
if (msg.cursize)
{
// Prox_SendMessage(prox, msg.data, msg.cursize, dem_stats|(player<<3), (1<<player));
msg.cursize = 0;
}
}
}
void Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox)
{
char buffer[MAX_MSGLEN*8];
netmsg_t msg;
int prespawn;
InitNetMsg(&msg, buffer, sizeof(buffer));
prox->flushing = false;
BuildServerData(qtv, &msg, true, false, 0);
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0;
for (prespawn = 0;prespawn >= 0;)
{
prespawn = SendList(qtv, prespawn, qtv->soundlist, svc_soundlist, &msg);
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0;
}
for (prespawn = 0;prespawn >= 0;)
{
prespawn = SendList(qtv, prespawn, qtv->modellist, svc_modellist, &msg);
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0;
}
Net_TryFlushProxyBuffer(qtv->cluster, prox); //that should be enough data to fill a packet.
for(prespawn = 0;prespawn>=0;)
{
prespawn = Prespawn(qtv, 0, &msg, prespawn, MAX_CLIENTS-1);
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0;
}
//playerstates arn't actually delta-compressed, so the first send (simply forwarded from server) entirly replaces the old.
//we do need to send entity states.
Prox_SendInitialEnts(qtv, prox, &msg);
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0;
WriteByte(&msg, svc_stufftext);
WriteString(&msg, "skins\n");
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0;
Net_TryFlushProxyBuffer(qtv->cluster, prox);
Prox_SendPlayerStats(qtv, prox);
Net_TryFlushProxyBuffer(qtv->cluster, prox);
if (!qtv->cluster->lateforward)
Net_ProxySend(qtv->cluster, prox, qtv->buffer, qtv->buffersize); //send all the info we've not yet processed.
if (prox->flushing)
{
Sys_Printf(qtv->cluster, "Connection data is too big, dropping proxy client\n");
prox->drop = true; //this is unfortunate...
}
else
Net_TryFlushProxyBuffer(qtv->cluster, prox);
}
oproxy_t *Net_FileProxy(sv_t *qtv, char *filename)
{
oproxy_t *prox;
FILE *f;
f = fopen(filename, "wb");
if (!f)
return NULL;
//no full proxy check, this is going to be used by proxy admins, who won't want to have to raise the limit to start recording.
prox = malloc(sizeof(*prox));
if (!prox)
return NULL;
memset(prox, 0, sizeof(*prox));
prox->sock = INVALID_SOCKET;
prox->file = f;
prox->next = qtv->proxies;
qtv->proxies = prox;
qtv->cluster->numproxies++;
Net_SendConnectionMVD(qtv, prox);
return prox;
}
qboolean Net_StopFileProxy(sv_t *qtv)
{
oproxy_t *prox;
for (prox = qtv->proxies; prox; prox = prox->next)
{
if (prox->file)
{
prox->drop = true;
return true;
}
}
return false;
}
void SV_ForwardStream(sv_t *qtv, char *buffer, int length)
{ //forward the stream on to connected clients
oproxy_t *prox, *next, *fre;
CheckMVDConsistancy(buffer, 0, length);
while (qtv->proxies && qtv->proxies->drop)
{
next = qtv->proxies->next;
fre = qtv->proxies;
if (fre->file)
fclose(fre->file);
else
closesocket(fre->sock);
free(fre);
qtv->cluster->numproxies--;
qtv->proxies = next;
}
for (prox = qtv->proxies; prox; prox = prox->next)
{
while (prox->next && prox->next->drop)
{
next = prox->next->next;
fre = prox->next;
if (fre->file)
fclose(fre->file);
else
closesocket(fre->sock);
free(fre);
qtv->cluster->numproxies--;
prox->next = next;
}
if (prox->flushing) //don't send it if we're trying to empty thier buffer.
{
if (prox->buffersize == prox->bufferpos)
{
if (!qtv->parsingconnectiondata)
Net_SendConnectionMVD(qtv, prox); //they're up to date, resend the connection info.
}
else
{
Net_TryFlushProxyBuffer(qtv->cluster, prox); //try and flush it.
continue;
}
}
if (prox->drop)
continue;
//add the new data
Net_ProxySend(qtv->cluster, prox, buffer, length);
Net_TryFlushProxyBuffer(qtv->cluster, prox);
// Net_TryFlushProxyBuffer(qtv->cluster, prox);
// Net_TryFlushProxyBuffer(qtv->cluster, prox);
}
}
void SV_GenerateNowPlayingHTTP(cluster_t *cluster, oproxy_t *dest)
{
int player;
char *s;
char buffer[1024];
char plname[64];
sv_t *streams;
s = "HTTP/1.1 200 OK\n"
"Content-Type: text/html\n"
"Connection: close\n"
"\n";
Net_ProxySend(cluster, dest, s, strlen(s));
sprintf(buffer, "<HEAD>"
"<TITLE>QuakeTV: Now Playing</TITLE>"
"</HEAD>"
"<BODY>");
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
for (streams = cluster->servers; streams; streams = streams->next)
{
sprintf(buffer, "<A HREF=\"watch.qtv?sid=%i\">%s (%s: %s)</A><br/>", streams->streamid, streams->server, streams->gamedir, streams->mapname);
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
for (player = 0; player < MAX_CLIENTS; player++)
{
if (*streams->players[player].userinfo)
{
Info_ValueForKey(streams->players[player].userinfo, "name", plname, sizeof(plname));
sprintf(buffer, "&nbsp;%s<br/>", plname);
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
}
}
}
sprintf(buffer, "</BODY>");
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
}
qboolean SV_GetHTTPHeaderField(char *s, char *field, char *buffer, int buffersize)
{
char *e;
char *colon;
int fieldnamelen = strlen(field);
buffer[0] = 0;
e = s;
while(*e)
{
if (*e == '\n')
{
*e = '\0';
colon = strchr(s, ':');
if (!colon)
{
if (!strncmp(field, s, fieldnamelen))
{
if (s[fieldnamelen] <= ' ')
{
return true;
}
}
}
else
{
if (fieldnamelen == colon - s)
{
if (!strncmp(field, s, colon-s))
{
colon++;
while (*colon == ' ')
colon++;
while (buffersize > 1)
{
if (*colon == '\r' || *colon == '\n')
break;
*buffer++ = *colon++;
}
*buffer = 0;
return true;
}
}
}
s = e+1;
}
e++;
}
return false;
}
void SV_GenerateQTVStub(cluster_t *cluster, oproxy_t *dest, int streamid)
{
char *s;
char hostname[64];
char buffer[1024];
if (!SV_GetHTTPHeaderField(dest->inbuffer, "Host", hostname, sizeof(hostname)))
{
s = "HTTP/1.1 400 OK\n"
"Content-Type: text/html\n"
"Connection: close\n"
"\n"
"<HTML>"
"<HEAD>"
"<TITLE>QuakeTV: Now Playing</TITLE>"
"</HEAD>"
"<BODY>"
"Your client did not send a Host field\n"
"</BODY>"
"</HTML>";
Net_ProxySend(cluster, dest, s, strlen(s));
return;
}
s = "HTTP/1.1 200 OK\n"
"Content-Type: text/x-quaketvident\n"
"Connection: close\n"
"\n";
Net_ProxySend(cluster, dest, s, strlen(s));
sprintf(buffer, "[QTV]\r\n"
"Stream: %i@%s\r\n"
"",
streamid, hostname);
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
}
//returns true if the pending proxy should be unlinked
//truth does not imply that it should be freed/released, just unlinked.
qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
{
char *s;
char *e;
char *colon;
int usableversion = 0;
int len;
qboolean raw;
sv_t *qtv;
if (pend->drop)
{
closesocket(pend->sock);
free(pend);
return true;
}
Net_TryFlushProxyBuffer(cluster, pend);
if (pend->flushing)
{
if (pend->bufferpos == pend->buffersize)
pend->drop = true;
return false;
}
len = sizeof(pend->inbuffer) - pend->inbuffersize - 1;
len = recv(pend->sock, pend->inbuffer+pend->inbuffersize, len, 0);
if (len == 0)
{
pend->drop = true;
return false;
}
if (len < 0)
return false;
pend->inbuffersize += len;
pend->inbuffer[pend->inbuffersize] = '\0';
if (pend->inbuffersize >= 4)
{
if (strncmp(pend->inbuffer, "QTV\n", 4) && strncmp(pend->inbuffer, "GET ", 4))
{ //I have no idea what the smeg you are.
pend->drop = true;
return false;
}
}
//make sure there's a double \n somewhere
for (s = pend->inbuffer; *s; s++)
{
if (s[0] == '\n' && (s[1] == '\n' || (s[1] == '\r' && s[2] == '\n')))
break;
}
if (!*s)
return false; //don't have enough yet
if (!strncmp(pend->inbuffer, "GET ", 4))
{
if (!strncmp(pend->inbuffer+4, "/nowplaying", 11))
{
SV_GenerateNowPlayingHTTP(cluster, pend);
}
else if (!strncmp(pend->inbuffer+4, "/watch.qtv?sid=", 15))
{
SV_GenerateQTVStub(cluster, pend, atoi(pend->inbuffer+19));
}
else if (!strncmp(pend->inbuffer+4, "/about", 6))
{
s = "HTTP/1.0 302 Found\n"
"Location: http://www.fteqw.com/\n"
"\n";
Net_ProxySend(cluster, pend, s, strlen(s));
}
else if (!strncmp(pend->inbuffer+4, "/ ", 2))
{
s = "HTTP/1.0 302 Found\n"
"Location: /nowplaying.html\n"
"\n";
Net_ProxySend(cluster, pend, s, strlen(s));
}
else if (!strncmp(pend->inbuffer+4, "/demos", 6))
{
s = "HTTP/1.0 302 Found\n"
"Location: http://www.fteqw.com/\n"
"\n";
Net_ProxySend(cluster, pend, s, strlen(s));
}
/* else
{
s = "HTTP/0.9 200 OK\n"
"Content-Type: text/plain\n"
"Content-Length: 12\n"
"\n"
"Hello World\n";
Net_ProxySend(cluster, pend, s, strlen(s));
}*/
else
{
s = "HTTP/1.1 404 OK\n"
"Content-Type: text/html\n"
"Connection: close\n"
"\n"
"<HEAD><TITLE>QuakeTV</TITLE></HEAD><BODY>The url you have specified was not recognised.</BODY>\n";
Net_ProxySend(cluster, pend, s, strlen(s));
}
pend->flushing = true;
return false;
}
raw = false;
qtv = pend->defaultstream;
e = pend->inbuffer;
s = e;
while(*e)
{
if (*e == '\n')
{
*e = '\0';
colon = strchr(s, ':');
if (!colon)
{
if (!strcmp(s, "SOURCELIST"))
{ //lists sources that are currently playing
s = "QTVSV 1\n";
Net_ProxySend(cluster, pend, s, strlen(s));
if (!cluster->servers)
{
s = "PERROR: No sources currently available\n";
Net_ProxySend(cluster, pend, s, strlen(s));
}
else
{
for (qtv = cluster->servers; qtv; qtv = qtv->next)
{
s = "ASOURCE: ";
Net_ProxySend(cluster, pend, s, strlen(s));
s = qtv->hostname;
if (!s || !*s)
s = qtv->server;
Net_ProxySend(cluster, pend, s, strlen(s));
s = "\n";
Net_ProxySend(cluster, pend, s, strlen(s));
}
qtv = NULL;
}
s = "\n";
Net_ProxySend(cluster, pend, s, strlen(s));
pend->flushing = true;
}
else if (!strcmp(s, "DEMOLIST"))
{ //lists the demos available on this proxy
s = "QTVSV 1\n";
Net_ProxySend(cluster, pend, s, strlen(s));
s = "PERROR: DEMOLIST command not yet implemented\n";
Net_ProxySend(cluster, pend, s, strlen(s));
s = "\n";
Net_ProxySend(cluster, pend, s, strlen(s));
pend->flushing = true;
}
}
else
{
*colon++ = '\0';
if (!strcmp(s, "VERSION"))
{
switch(atoi(colon))
{
case 1:
//got a usable version
usableversion = 1;
break;
default:
//not recognised.
break;
}
}
else if (!strcmp(s, "RAW"))
raw = atoi(colon);
/*else if (!strcmp(s, "ROUTE"))
{ //pure rewroute...
//is this safe? probably not.
s = "QTVSV 1\n"
"PERROR: ROUTE command not yet implemented\n"
"\n";
Net_ProxySend(cluster, pend, s, strlen(s));
pend->flushing = true;
}
*/
else if (!strcmp(s, "SOURCE"))
{ //connects, creating a new source
while (*colon == ' ')
colon++;
for (s = colon; *s; s++)
if (*s < '0' || *s > '9')
break;
if (*s)
qtv = QTV_NewServerConnection(cluster, colon, "", false, true, true);
else
{
//numerical source, use a stream id.
for (qtv = cluster->servers; qtv; qtv = qtv->next)
if (qtv->streamid == atoi(colon))
break;
}
// s = "QTVSV 1\n"
// "PERROR: SOURCE command not yet implemented\n"
// "\n";
// Net_ProxySend(cluster, pend, s, strlen(s));
}
else if (!strcmp(s, "DEMO"))
{ //starts a demo off the server... source does the same thing though...
s = "QTVSV 1\n"
"PERROR: DEMO command not yet implemented\n"
"\n";
Net_ProxySend(cluster, pend, s, strlen(s));
pend->flushing = true;
}
}
s = e+1;
}
e++;
}
if (!pend->flushing)
{
if (!usableversion)
{
s = "QTVSV 1\n"
"PERROR: Requested protocol version not supported\n"
"\n";
Net_ProxySend(cluster, pend, s, strlen(s));
pend->flushing = true;
}
if (!qtv)
{
s = "QTVSV 1\n"
"PERROR: No stream selected\n"
"\n";
Net_ProxySend(cluster, pend, s, strlen(s));
pend->flushing = true;
}
}
if (pend->flushing)
return false;
pend->next = qtv->proxies;
qtv->proxies = pend;
if (!raw)
{
s = "QTVSV 1\n\n";
Net_ProxySend(cluster, pend, s, strlen(s));
}
// else if (passwordprotected) //raw mode doesn't support passwords, so reject them
// {
// pend->flushing = true;
// return;
// }
Net_SendConnectionMVD(qtv, pend);
return true;
}

View file

@ -240,10 +240,81 @@ A 0 length will still generate a packet and deal with the reliable messages.
void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const unsigned char *data) void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const unsigned char *data)
{ {
netmsg_t send; netmsg_t send;
unsigned char send_buf[MAX_MSGLEN + PACKET_HEADER]; unsigned char send_buf[MAX_NQMSGLEN + PACKET_HEADER];
qboolean send_reliable; qboolean send_reliable;
unsigned w1, w2; unsigned w1, w2;
if (chan->isnqprotocol)
{
int i;
send.data = send_buf;
send.maxsize = MAX_NQMSGLEN + PACKET_HEADER;
send.cursize = 0;
if (!chan->reliable_length && chan->message.cursize)
{
memcpy (chan->reliable_buf, chan->message_buf, chan->message.cursize);
chan->reliable_length = chan->message.cursize;
chan->reliable_start = 0;
chan->message.cursize = 0;
}
i = chan->reliable_length - chan->reliable_start;
if (i>0)
{
WriteLong(&send, 0);
WriteLong(&send, SwapLong(chan->reliable_sequence));
if (i > MAX_NQDATAGRAM)
i = MAX_NQDATAGRAM;
WriteData (&send, chan->reliable_buf+chan->reliable_start, i);
if (length && send.cursize + length < send.maxsize)
{ //throw the unreliable packet into the same one as the reliable (but not sent reliably)
WriteData (&send, data, length);
length = 0;
}
if (chan->reliable_start+i == chan->reliable_length)
*(int*)send_buf = BigLong(NETFLAG_DATA | NETFLAG_EOM | send.cursize);
else
*(int*)send_buf = BigLong(NETFLAG_DATA | send.cursize);
NET_SendPacket(cluster, chan->sock, send.cursize, send.data, chan->remote_address);
if (chan->cleartime < curtime)
chan->cleartime = curtime + send.cursize*chan->rate;
else
chan->cleartime += send.cursize*chan->rate;
}
//send out the unreliable (if still unsent)
if (length)
{
WriteLong(&send, 0);
WriteLong(&send, SwapLong(chan->outgoing_unreliable));
chan->outgoing_unreliable++;
WriteData (&send, data, length);
*(int*)send_buf = BigLong(NETFLAG_UNRELIABLE | send.cursize);
NET_SendPacket (cluster, chan->sock, send.cursize, send.data, chan->remote_address);
if (chan->cleartime < curtime)
chan->cleartime = curtime + send.cursize*chan->rate;
else
chan->cleartime += send.cursize*chan->rate;
send.cursize = 0;
}
return;
}
// check for message overflow // check for message overflow
if (chan->message.overflowed) if (chan->message.overflowed)
{ {
@ -324,6 +395,110 @@ void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const un
*/ */
} }
qboolean NQNetchan_Process(cluster_t *cluster, netchan_t *chan, netmsg_t *msg)
{
int header;
int sequence;
int drop;
msg->readpos = 0;
header = SwapLong(ReadLong(msg));
if (msg->cursize != (header & NETFLAG_LENGTH_MASK))
return false; //size was wrong, couldn't have been ours.
if (header & NETFLAG_CTL)
return false; //huh?
sequence = SwapLong(ReadLong(msg));
if (header & NETFLAG_ACK)
{
if (sequence == chan->reliable_sequence)
{
chan->reliable_start += MAX_NQDATAGRAM;
if (chan->reliable_start >= chan->reliable_length)
{
chan->reliable_length = 0; //they got the entire message
chan->reliable_start = 0;
}
chan->incoming_reliable_acknowledged = chan->reliable_sequence;
chan->reliable_sequence++;
chan->last_received = curtime;
}
// else if (sequence < chan->reliable_sequence)
// Con_DPrintf("Stale ack recieved\n");
// else if (sequence > chan->reliable_sequence)
// Con_Printf("Future ack recieved\n");
return false; //don't try execing the 'payload'. I hate ack packets.
}
if (header & NETFLAG_UNRELIABLE)
{
if (sequence < chan->incoming_unreliable)
{
// Con_DPrintf("Stale datagram recieved\n");
return false;
}
drop = sequence - chan->incoming_unreliable - 1;
if (drop > 0)
{
// Con_DPrintf("Dropped %i datagrams\n", drop);
// chan->drop_count += drop;
}
chan->incoming_unreliable = sequence;
chan->last_received = curtime;
chan->incoming_acknowledged++;
// chan->good_count++;
return 1;
}
if (header & NETFLAG_DATA)
{
int runt[2];
//always reply. a stale sequence probably means our ack got lost.
runt[0] = BigLong(NETFLAG_ACK | 8);
runt[1] = BigLong(sequence);
NET_SendPacket (cluster, chan->sock, 8, (void*)runt, chan->remote_address);
chan->last_received = curtime;
if (sequence == chan->incoming_reliable_sequence)
{
chan->incoming_reliable_sequence++;
if (chan->in_fragment_length + msg->cursize-8 >= sizeof(chan->in_fragment_buf))
{
chan->drop = true;
return false;
}
memcpy(chan->in_fragment_buf + chan->in_fragment_length, msg->data+8, msg->cursize-8);
chan->in_fragment_length += msg->cursize-8;
if (header & NETFLAG_EOM)
{
msg->cursize = 0;
WriteData(msg, chan->in_fragment_buf, chan->in_fragment_length);
chan->in_fragment_length = 0;
msg->readpos = 0;
return 2; //we can read it now
}
}
// else
// Con_DPrintf("Stale reliable (%i)\n", sequence);
return false;
}
return false; //not supported.
}
/* /*
================= =================
Netchan_Process Netchan_Process

View file

@ -56,6 +56,38 @@ unsigned int ReadLong(netmsg_t *b)
return s1 | (s2<<16); return s1 | (s2<<16);
} }
unsigned int BigLong(unsigned int val)
{
union {
unsigned int i;
unsigned char c[4];
} v;
unsigned char s;
v.i = val;
return (v.c[0]<<24) | (v.c[1] << 16) | (v.c[2] << 8) | (v.c[3] << 0);
}
unsigned int SwapLong(unsigned int val)
{
union {
unsigned int i;
unsigned char c[4];
} v;
unsigned char s;
v.i = val;
s = v.c[0];
v.c[0] = v.c[3];
v.c[3] = s;
s = v.c[1];
v.c[1] = v.c[2];
v.c[2] = s;
return v.i;
}
float ReadFloat(netmsg_t *b) float ReadFloat(netmsg_t *b)
{ {
union { union {
@ -166,7 +198,7 @@ void SendBufferToViewer(viewer_t *v, const char *buffer, int length, qboolean re
} }
} }
void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playermask) void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playermask, int suitablefor)
{ {
viewer_t *v; viewer_t *v;
switch(to) switch(to)
@ -180,7 +212,10 @@ void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playerma
if (v->server == tv) if (v->server == tv)
if (v->trackplayer>=0) if (v->trackplayer>=0)
if ((1<<v->trackplayer)&playermask) if ((1<<v->trackplayer)&playermask)
SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type {
if (suitablefor&(v->netchan.isnqprotocol?NQ:QW))
SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type
}
} }
break; break;
default: default:
@ -188,17 +223,19 @@ void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playerma
for (v = tv->cluster->viewers; v; v = v->next) for (v = tv->cluster->viewers; v; v = v->next)
{ {
if (v->server == tv) if (v->server == tv)
SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type if (suitablefor&(v->netchan.isnqprotocol?NQ:QW))
SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type
} }
break; break;
} }
} }
void Broadcast(cluster_t *cluster, char *buffer, int length) void Broadcast(cluster_t *cluster, char *buffer, int length, int suitablefor)
{ {
viewer_t *v; viewer_t *v;
for (v = cluster->viewers; v; v = v->next) for (v = cluster->viewers; v; v = v->next)
{ {
SendBufferToViewer(v, buffer, length, true); if (suitablefor&(v->netchan.isnqprotocol?NQ:QW))
SendBufferToViewer(v, buffer, length, true);
} }
} }
@ -255,6 +292,7 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma
} }
tv->maxents = 0; //clear these tv->maxents = 0; //clear these
tv->spawnstatic_count = 0;
memset(tv->modellist, 0, sizeof(tv->modellist)); memset(tv->modellist, 0, sizeof(tv->modellist));
memset(tv->soundlist, 0, sizeof(tv->soundlist)); memset(tv->soundlist, 0, sizeof(tv->soundlist));
memset(tv->lightstyle, 0, sizeof(tv->lightstyle)); memset(tv->lightstyle, 0, sizeof(tv->lightstyle));
@ -275,10 +313,18 @@ 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) static void ParseCDTrack(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
{ {
char nqversion[3];
tv->cdtrack = ReadByte(m); tv->cdtrack = ReadByte(m);
if (!tv->parsingconnectiondata) if (!tv->parsingconnectiondata)
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); {
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);
nqversion[0] = svc_cdtrack;
nqversion[1] = tv->cdtrack;
nqversion[2] = tv->cdtrack;
Multicast(tv, nqversion, 3, to, mask, NQ);
}
} }
static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
{ {
@ -384,12 +430,12 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
} }
else if (tv->usequkeworldprotocols && !strncmp(text, "setinfo ", 8)) else if (tv->usequkeworldprotocols && !strncmp(text, "setinfo ", 8))
{ {
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1);
SendClientCommand(tv, text); SendClientCommand(tv, text);
} }
else else
{ {
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1);
return; return;
} }
} }
@ -407,7 +453,7 @@ static void ParseSetInfo(sv_t *tv, netmsg_t *m)
Info_SetValueForStarKey(tv->players[pnum].userinfo, key, value, sizeof(tv->players[pnum].userinfo)); Info_SetValueForStarKey(tv->players[pnum].userinfo, key, value, sizeof(tv->players[pnum].userinfo));
if (!tv->parsingconnectiondata) if (!tv->parsingconnectiondata)
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, dem_all, (unsigned)-1); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, dem_all, (unsigned)-1, QW);
} }
static void ParseServerinfo(sv_t *tv, netmsg_t *m) static void ParseServerinfo(sv_t *tv, netmsg_t *m)
@ -421,17 +467,29 @@ static void ParseServerinfo(sv_t *tv, netmsg_t *m)
Info_SetValueForStarKey(tv->serverinfo, key, value, sizeof(tv->serverinfo)); Info_SetValueForStarKey(tv->serverinfo, key, value, sizeof(tv->serverinfo));
if (!tv->parsingconnectiondata) if (!tv->parsingconnectiondata)
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, dem_all, (unsigned)-1); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, dem_all, (unsigned)-1, QW);
} }
static void ParsePrint(sv_t *tv, netmsg_t *m, int to, unsigned int mask) static void ParsePrint(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
{ {
unsigned char *t; unsigned char *t;
char text[1024]; char text[1024];
char buffer[1024];
int level; int level;
level = ReadByte(m); level = ReadByte(m);
ReadString(m, text, sizeof(text)); ReadString(m, text, sizeof(text)-2);
if (level == 3)
{
strcpy(buffer+2, text);
buffer[1] = 1;
}
else
{
strcpy(buffer+1, text);
}
buffer[0] = svc_print;
if (to == dem_all || to == dem_read) if (to == dem_all || to == dem_read)
{ {
@ -464,14 +522,15 @@ static void ParsePrint(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
} }
} }
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);
Multicast(tv, buffer, strlen(buffer), to, mask, NQ);
} }
static void ParseCenterprint(sv_t *tv, netmsg_t *m, int to, unsigned int mask) static void ParseCenterprint(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
{ {
char text[1024]; char text[1024];
ReadString(m, text, sizeof(text)); ReadString(m, text, sizeof(text));
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1);
} }
static int ParseList(sv_t *tv, netmsg_t *m, filename_t *list, int to, unsigned int mask) static int ParseList(sv_t *tv, netmsg_t *m, filename_t *list, int to, unsigned int mask)
{ {
@ -533,7 +592,7 @@ static void ParseStaticSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
tv->staticsound_count++; tv->staticsound_count++;
if (!tv->parsingconnectiondata) if (!tv->parsingconnectiondata)
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1);
} }
static void ParseIntermission(sv_t *tv, netmsg_t *m, int to, unsigned int mask) static void ParseIntermission(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
@ -545,7 +604,7 @@ static void ParseIntermission(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
ReadByte(m); ReadByte(m);
ReadByte(m); ReadByte(m);
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);
} }
void ParseSpawnStatic(sv_t *tv, netmsg_t *m, int to, unsigned int mask) void ParseSpawnStatic(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
@ -561,7 +620,7 @@ void ParseSpawnStatic(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
tv->spawnstatic_count++; tv->spawnstatic_count++;
if (!tv->parsingconnectiondata) if (!tv->parsingconnectiondata)
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1);
} }
extern const usercmd_t nullcmd; extern const usercmd_t nullcmd;
@ -610,6 +669,12 @@ static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers)
tv->players[num].current.angles[1] = nonnullcmd.angles[1]; tv->players[num].current.angles[1] = nonnullcmd.angles[1];
tv->players[num].current.angles[2] = nonnullcmd.angles[2]; tv->players[num].current.angles[2] = nonnullcmd.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;
}
for (i=0 ; i<3 ; i++) for (i=0 ; i<3 ; i++)
{ {
@ -786,7 +851,7 @@ static void ParseUpdatePing(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
else else
Sys_Printf(tv->cluster, "svc_updateping: invalid player number\n"); Sys_Printf(tv->cluster, "svc_updateping: invalid player number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);
} }
static void ParseUpdateFrags(sv_t *tv, netmsg_t *m, int to, unsigned int mask) static void ParseUpdateFrags(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
@ -801,7 +866,7 @@ static void ParseUpdateFrags(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
else else
Sys_Printf(tv->cluster, "svc_updatefrags: invalid player number\n"); Sys_Printf(tv->cluster, "svc_updatefrags: invalid player number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, (pnum < 16)?Q1:QW);
} }
static void ParseUpdateStat(sv_t *tv, netmsg_t *m, int to, unsigned int mask) static void ParseUpdateStat(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
@ -824,7 +889,7 @@ static void ParseUpdateStat(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
else else
Sys_Printf(tv->cluster, "svc_updatestat: invalid stat number\n"); Sys_Printf(tv->cluster, "svc_updatestat: invalid stat number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);
} }
static void ParseUpdateStatLong(sv_t *tv, netmsg_t *m, int to, unsigned int mask) static void ParseUpdateStatLong(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
{ {
@ -846,7 +911,7 @@ static void ParseUpdateStatLong(sv_t *tv, netmsg_t *m, int to, unsigned int mask
else else
Sys_Printf(tv->cluster, "svc_updatestatlong: invalid stat number\n"); Sys_Printf(tv->cluster, "svc_updatestatlong: invalid stat number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);
} }
static void ParseUpdateUserinfo(sv_t *tv, netmsg_t *m, int to, unsigned int mask) static void ParseUpdateUserinfo(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
@ -864,7 +929,7 @@ static void ParseUpdateUserinfo(sv_t *tv, netmsg_t *m, int to, unsigned int mask
} }
} }
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);
} }
static void ParsePacketloss(sv_t *tv, netmsg_t *m, int to, unsigned int mask) static void ParsePacketloss(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
@ -880,7 +945,7 @@ static void ParsePacketloss(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
else else
Sys_Printf(tv->cluster, "svc_updatepl: invalid player number\n"); Sys_Printf(tv->cluster, "svc_updatepl: invalid player number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);
} }
static void ParseUpdateEnterTime(sv_t *tv, netmsg_t *m, int to, unsigned int mask) static void ParseUpdateEnterTime(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
@ -896,7 +961,7 @@ static void ParseUpdateEnterTime(sv_t *tv, netmsg_t *m, int to, unsigned int mas
else else
Sys_Printf(tv->cluster, "svc_updateentertime: invalid player number\n"); Sys_Printf(tv->cluster, "svc_updateentertime: invalid player number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);
} }
static void ParseSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask) static void ParseSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
@ -930,7 +995,7 @@ static void ParseSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
for (i=0 ; i<3 ; i++) for (i=0 ; i<3 ; i++)
org[i] = ReadShort (m); org[i] = ReadShort (m);
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);
} }
static void ParseDamage(sv_t *tv, netmsg_t *m, int to, unsigned int mask) static void ParseDamage(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
@ -940,7 +1005,7 @@ static void ParseDamage(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
ReadShort (m); ReadShort (m);
ReadShort (m); ReadShort (m);
ReadShort (m); ReadShort (m);
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);
} }
enum { enum {
@ -1041,7 +1106,7 @@ static void ParseTempEntity(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
return; return;
} }
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);
} }
void ParseLightstyle(sv_t *tv, netmsg_t *m) void ParseLightstyle(sv_t *tv, netmsg_t *m)
@ -1058,7 +1123,7 @@ void ParseLightstyle(sv_t *tv, netmsg_t *m)
} }
} }
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, dem_read, (unsigned)-1); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, dem_read, (unsigned)-1, Q1);
} }
void ParseNails(sv_t *tv, netmsg_t *m, qboolean nails2) void ParseNails(sv_t *tv, netmsg_t *m, qboolean nails2)
@ -1121,6 +1186,10 @@ void ParseDownload(sv_t *tv, netmsg_t *m)
{ {
fclose(tv->file); fclose(tv->file);
tv->file = NULL; tv->file = NULL;
snprintf(buffer, sizeof(buffer), "%s/%s", (tv->gamedir&&*tv->gamedir)?tv->gamedir:"id1", tv->modellist[1].name);
rename(tv->downloadname, buffer);
Sys_Printf(tv->cluster, "Download complete\n"); Sys_Printf(tv->cluster, "Download complete\n");
tv->bsp = BSP_LoadModel(tv->cluster, tv->gamedir, tv->modellist[1].name); tv->bsp = BSP_LoadModel(tv->cluster, tv->gamedir, tv->modellist[1].name);
@ -1187,6 +1256,7 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
ParseSound(tv, &buf, to, mask); ParseSound(tv, &buf, to, mask);
break; break;
//#define svc_time 7 // [float] server time //#define svc_time 7 // [float] server time
case svc_print: case svc_print:
ParsePrint(tv, &buf, to, mask); ParsePrint(tv, &buf, to, mask);
break; break;
@ -1198,12 +1268,21 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
case svc_setangle: case svc_setangle:
if (!tv->usequkeworldprotocols) if (!tv->usequkeworldprotocols)
ReadByte(&buf); ReadByte(&buf);
ReadByte(&buf); tv->proxyplayerangles[0] = ReadByte(&buf)*360.0/255;
ReadByte(&buf); tv->proxyplayerangles[1] = ReadByte(&buf)*360.0/255;
ReadByte(&buf); tv->proxyplayerangles[2] = ReadByte(&buf)*360.0/255;
if (tv->usequkeworldprotocols && tv->controller) if (tv->usequkeworldprotocols && tv->controller)
SendBufferToViewer(tv->controller, buf.data+buf.startpos, buf.readpos - buf.startpos, true); SendBufferToViewer(tv->controller, buf.data+buf.startpos, buf.readpos - buf.startpos, true);
{
char nq[4];
nq[0] = svc_setangle;
nq[1] = tv->proxyplayerangles[0];
nq[2] = tv->proxyplayerangles[1];
nq[3] = tv->proxyplayerangles[2];
Multicast(tv, nq, 4, to, mask, Q1);
}
break; break;
case svc_serverdata: case svc_serverdata:
@ -1215,9 +1294,11 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
break; break;
//#define svc_updatename 13 // [qbyte] [string] //#define svc_updatename 13 // [qbyte] [string]
case svc_updatefrags: case svc_updatefrags:
ParseUpdateFrags(tv, &buf, to, mask); ParseUpdateFrags(tv, &buf, to, mask);
break; break;
//#define svc_clientdata 15 // <shortbits + data> //#define svc_clientdata 15 // <shortbits + data>
//#define svc_stopsound 16 // <see code> //#define svc_stopsound 16 // <see code>
//#define svc_updatecolors 17 // [qbyte] [qbyte] [qbyte] //#define svc_updatecolors 17 // [qbyte] [qbyte] [qbyte]
@ -1231,8 +1312,9 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
ReadByte(&buf); ReadByte(&buf);
ReadByte(&buf); ReadByte(&buf);
ReadByte(&buf); ReadByte(&buf);
Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, dem_read, (unsigned)-1); Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, dem_read, (unsigned)-1, Q1);
break; break;
case svc_damage: case svc_damage:
ParseDamage(tv, &buf, to, mask); ParseDamage(tv, &buf, to, mask);
break; break;
@ -1252,7 +1334,7 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
case svc_setpause: // [qbyte] on / off case svc_setpause: // [qbyte] on / off
tv->ispaused = ReadByte(&buf); tv->ispaused = ReadByte(&buf);
Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, dem_read, (unsigned)-1); Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, dem_read, (unsigned)-1, Q1);
break; break;
//#define svc_signonnum 25 // [qbyte] used for the signon sequence //#define svc_signonnum 25 // [qbyte] used for the signon sequence
@ -1277,13 +1359,14 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
case svc_cdtrack: case svc_cdtrack:
ParseCDTrack(tv, &buf, to, mask); ParseCDTrack(tv, &buf, to, mask);
break; break;
//#define svc_sellscreen 33 //#define svc_sellscreen 33
//#define svc_cutscene 34 //hmm... nq only... added after qw tree splitt? //#define svc_cutscene 34 //hmm... nq only... added after qw tree splitt?
case svc_smallkick: case svc_smallkick:
case svc_bigkick: case svc_bigkick:
Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask); Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask, QW);
break; break;
case svc_updateping: case svc_updateping:
@ -1300,7 +1383,7 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
case svc_muzzleflash: case svc_muzzleflash:
ReadShort(&buf); ReadShort(&buf);
Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask); Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask, QW);
break; break;
case svc_updateuserinfo: case svc_updateuserinfo:
@ -1310,16 +1393,19 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
case svc_download: // [short] size [size bytes] case svc_download: // [short] size [size bytes]
ParseDownload(tv, &buf); ParseDownload(tv, &buf);
break; break;
case svc_playerinfo: case svc_playerinfo:
ParsePlayerInfo(tv, &buf, clearoldplayers); ParsePlayerInfo(tv, &buf, clearoldplayers);
clearoldplayers = false; clearoldplayers = false;
break; break;
case svc_nails: case svc_nails:
ParseNails(tv, &buf, false); ParseNails(tv, &buf, false);
break; break;
case svc_chokecount: case svc_chokecount:
ReadByte(&buf); ReadByte(&buf);
break; break;
case svc_modellist: case svc_modellist:
i = ParseList(tv, &buf, tv->modellist, to, mask); i = ParseList(tv, &buf, tv->modellist, to, mask);
if (!i) if (!i)
@ -1328,7 +1414,7 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
if (tv->bsp) if (tv->bsp)
BSP_Free(tv->bsp); BSP_Free(tv->bsp);
if (tv->cluster->nobsp && !tv->usequkeworldprotocols) if (tv->cluster->nobsp || !tv->usequkeworldprotocols)
tv->bsp = NULL; tv->bsp = NULL;
else else
tv->bsp = BSP_LoadModel(tv->cluster, tv->gamedir, tv->modellist[1].name); tv->bsp = BSP_LoadModel(tv->cluster, tv->gamedir, tv->modellist[1].name);
@ -1352,10 +1438,10 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
{ {
fclose(tv->file); fclose(tv->file);
unlink(tv->downloadname); unlink(tv->downloadname);
Sys_Printf(tv->cluster, "Was already downloading %s\nOld download canceled\n"); Sys_Printf(tv->cluster, "Was already downloading %s\nOld download canceled\n", tv->downloadname);
tv->file = NULL; tv->file = NULL;
} }
snprintf(tv->downloadname, sizeof(tv->downloadname), "%s/%s", (tv->gamedir&&*tv->gamedir)?tv->gamedir:"qw", tv->modellist[1].name, sizeof(tv->downloadname)); snprintf(tv->downloadname, sizeof(tv->downloadname), "%s/%s.tmp", (tv->gamedir&&*tv->gamedir)?tv->gamedir:"id1", tv->modellist[1].name);
tv->file = fopen(tv->downloadname, "wb"); tv->file = fopen(tv->downloadname, "wb");
if (!tv->file) if (!tv->file)
tv->drop = true; tv->drop = true;
@ -1384,6 +1470,7 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
SendClientCommand(tv, "modellist %i 0\n", tv->clservercount); SendClientCommand(tv, "modellist %i 0\n", tv->clservercount);
} }
break; break;
case svc_packetentities: case svc_packetentities:
FlushPacketEntities(tv); FlushPacketEntities(tv);
ParsePacketEntities(tv, &buf); ParsePacketEntities(tv, &buf);
@ -1392,15 +1479,16 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
ReadByte(&buf); ReadByte(&buf);
ParsePacketEntities(tv, &buf); ParsePacketEntities(tv, &buf);
break; break;
//#define svc_maxspeed 49 // maxspeed change, for prediction //#define svc_maxspeed 49 // maxspeed change, for prediction
case svc_entgravity: // gravity change, for prediction case svc_entgravity: // gravity change, for prediction
ReadFloat(&buf); ReadFloat(&buf);
Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask); Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask, QW);
break; break;
case svc_maxspeed: case svc_maxspeed:
ReadFloat(&buf); ReadFloat(&buf);
Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask); Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask, QW);
break; break;
case svc_setinfo: case svc_setinfo:
ParseSetInfo(tv, &buf); ParseSetInfo(tv, &buf);
break; break;
@ -1413,6 +1501,7 @@ case svc_maxspeed:
case svc_nails2: case svc_nails2:
ParseNails(tv, &buf, true); ParseNails(tv, &buf, true);
break; break;
default: default:
buf.readpos = buf.startpos; buf.readpos = buf.startpos;
Sys_Printf(tv->cluster, "Can't handle svc %i\n", (unsigned int)ReadByte(&buf)); Sys_Printf(tv->cluster, "Can't handle svc %i\n", (unsigned int)ReadByte(&buf));

View file

@ -42,13 +42,28 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifdef _WIN32 #ifdef _WIN32
#include <conio.h> #include <conio.h>
#include <winsock.h> #include <winsock.h> //this includes windows.h and is the reason for much compiling slowness with windows builds.
#pragma comment (lib, "wsock32.lib") #ifdef _MSC_VER
#pragma comment (lib, "wsock32.lib")
#endif
#define qerrno WSAGetLastError() #define qerrno WSAGetLastError()
#define EWOULDBLOCK WSAEWOULDBLOCK #define EWOULDBLOCK WSAEWOULDBLOCK
#define EINPROGRESS WSAEINPROGRESS #define EINPROGRESS WSAEINPROGRESS
#define ECONNREFUSED WSAECONNREFUSED
#define ENOTCONN WSAENOTCONN #define ENOTCONN WSAENOTCONN
//we have special functions to properly terminate sprintf buffers in windows.
//we assume other systems are designed with even a minor thought to security.
#if !defined(__MINGW32_VERSION)
#define unlink _unlink //why do MS have to be so awkward?
int snprintf(char *buffer, int buffersize, char *format, ...);
#if !defined(_VC80_UPGRADE)
int vsnprintf(char *buffer, int buffersize, char *format, va_list argptr);
#endif
#else
#define unlink remove //seems mingw misses something
#endif
#ifdef _MSC_VER #ifdef _MSC_VER
//okay, so warnings are here to help... they're ugly though. //okay, so warnings are here to help... they're ugly though.
#pragma warning(disable: 4761) //integral size mismatch in argument #pragma warning(disable: 4761) //integral size mismatch in argument
@ -121,7 +136,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define MAX_SERVERINFO_STRING 1024 //standard quake has 512 here. #define MAX_SERVERINFO_STRING 1024 //standard quake has 512 here.
#define MAX_USERINFO 192 #define MAX_USERINFO 192
#define MAX_CLIENTS 32 #define MAX_CLIENTS 32
#define MAX_STATS 32
#define MAX_LIST 256 #define MAX_LIST 256
#define MAX_MODELS MAX_LIST #define MAX_MODELS MAX_LIST
#define MAX_SOUNDS MAX_LIST #define MAX_SOUNDS MAX_LIST
@ -131,6 +145,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define MAX_LIGHTSTYLES 64 #define MAX_LIGHTSTYLES 64
#define DEFAULT_HOSTNAME "FTEQTV" #define DEFAULT_HOSTNAME "FTEQTV"
#define MAX_PROXY_INBUFFER 4096
#define MAX_PROXY_BUFFER (1<<14) //must be power-of-two #define MAX_PROXY_BUFFER (1<<14) //must be power-of-two
#define PREFERED_PROXY_BUFFER 8192 //the ammount of data we try to leave in our input buffer (must be large enough to contain any single mvd frame) #define PREFERED_PROXY_BUFFER 8192 //the ammount of data we try to leave in our input buffer (must be large enough to contain any single mvd frame)
@ -141,16 +156,66 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define Z_EXT_SERVERTIME (1<<3) // STAT_TIME #define Z_EXT_SERVERTIME (1<<3) // STAT_TIME
#define Z_EXT_STRING "8" #define Z_EXT_STRING "8"
#define MAX_STATS 32
#define STAT_HEALTH 0
#define STAT_FRAGS 1
#define STAT_WEAPON 2
#define STAT_AMMO 3
#define STAT_ARMOR 4
#define STAT_WEAPONFRAME 5
#define STAT_SHELLS 6
#define STAT_NAILS 7
#define STAT_ROCKETS 8
#define STAT_CELLS 9
#define STAT_ACTIVEWEAPON 10
#define STAT_TOTALSECRETS 11
#define STAT_TOTALMONSTERS 12
#define STAT_SECRETS 13 // bumped on client side by svc_foundsecret
#define STAT_MONSTERS 14 // bumped by svc_killedmonster
#define STAT_ITEMS 15
#define STAT_TIME 17 //A ZQ hack, sending time via a stat. #define STAT_TIME 17 //A ZQ hack, sending time via a stat.
//this allows l33t engines to interpolate properly without spamming at a silly high fps. //this allows l33t engines to interpolate properly without spamming at a silly high fps.
#ifndef __cplusplus
typedef enum {false, true} qboolean; typedef enum {false, true} qboolean;
#else
typedef int qboolean;
extern "C" {
#endif
typedef unsigned char netadr_t[64]; typedef unsigned char netadr_t[64];
#define NQ_PACKETS_PER_SECOND 100
#define MAX_NQMSGLEN 8000
#define MAX_MSGLEN 1400 #define MAX_MSGLEN 1400
#define MAX_NQDATAGRAM 1024
#define MAX_BACKBUF_SIZE 1000 //this is smaller so we don't loose entities when lagging #define MAX_BACKBUF_SIZE 1000 //this is smaller so we don't loose entities when lagging
//NQ transport layer defines
#define NETFLAG_LENGTH_MASK 0x0000ffff
#define NETFLAG_DATA 0x00010000
#define NETFLAG_ACK 0x00020000
#define NETFLAG_NAK 0x00040000
#define NETFLAG_EOM 0x00080000
#define NETFLAG_UNRELIABLE 0x00100000
#define NETFLAG_CTL 0x80000000
#define CCREQ_CONNECT 0x01
#define CCREP_ACCEPT 0x81
#define CCREP_REJECT 0x82
#define NET_GAMENAME_NQ "QUAKE"
#define NET_PROTOCOL_VERSION 3
typedef struct { typedef struct {
unsigned int readpos; unsigned int readpos;
unsigned int cursize; unsigned int cursize;
@ -171,10 +236,11 @@ typedef struct {
int reliable_length; int reliable_length;
qboolean drop; qboolean drop;
qboolean isclient; qboolean isclient;
qboolean isnqprotocol;
netmsg_t message; netmsg_t message;
char message_buf[MAX_MSGLEN]; //reliable message being built char message_buf[MAX_NQMSGLEN]; //reliable message being built
char reliable_buf[MAX_MSGLEN]; //reliable message that we're making sure arrives. char reliable_buf[MAX_NQMSGLEN]; //reliable message that we're making sure arrives.
float rate; float rate;
@ -186,6 +252,15 @@ typedef struct {
unsigned int incoming_sequence; unsigned int incoming_sequence;
unsigned int outgoing_sequence; unsigned int outgoing_sequence;
unsigned int reliable_start;
unsigned int outgoing_unreliable;
unsigned int incoming_unreliable;
unsigned int in_fragment_length;
char in_fragment_buf[MAX_NQMSGLEN];
} netchan_t; } netchan_t;
typedef struct { typedef struct {
@ -199,7 +274,7 @@ typedef struct {
unsigned char colormap; unsigned char colormap;
unsigned char skinnum; unsigned char skinnum;
short origin[3]; short origin[3];
unsigned char angles[3]; char angles[3];
unsigned char effects; unsigned char effects;
} entity_state_t; } entity_state_t;
typedef struct { typedef struct {
@ -208,7 +283,7 @@ typedef struct {
unsigned char skinnum; unsigned char skinnum;
short origin[3]; short origin[3];
short velocity[3]; short velocity[3];
unsigned short angles[3]; short angles[3];
unsigned char effects; unsigned char effects;
unsigned char weaponframe; unsigned char weaponframe;
} player_state_t; } player_state_t;
@ -251,6 +326,8 @@ typedef struct sv_s sv_t;
typedef struct cluster_s cluster_t; typedef struct cluster_s cluster_t;
typedef struct viewer_s { typedef struct viewer_s {
qboolean drop; qboolean drop;
unsigned int timeout;
unsigned int nextpacket; //for nq clients
netchan_t netchan; netchan_t netchan;
qboolean maysend; qboolean maysend;
qboolean chokeme; qboolean chokeme;
@ -291,12 +368,21 @@ typedef struct viewer_s {
int fwdval; //for scrolling up/down the menu using +forward/+back :) int fwdval; //for scrolling up/down the menu using +forward/+back :)
} viewer_t; } viewer_t;
//'other proxy', these are mvd stream clients.
typedef struct oproxy_s { typedef struct oproxy_s {
int authkey;
unsigned int droptime;
qboolean flushing; qboolean flushing;
qboolean drop; qboolean drop;
FILE *file; sv_t *defaultstream;
SOCKET sock;
FILE *file; //recording a demo
SOCKET sock; //playing to a proxy
unsigned char inbuffer[MAX_PROXY_INBUFFER];
unsigned int inbuffersize; //amount of data available.
unsigned char buffer[MAX_PROXY_BUFFER]; unsigned char buffer[MAX_PROXY_BUFFER];
unsigned int buffersize; //use cyclic buffering. unsigned int buffersize; //use cyclic buffering.
@ -329,11 +415,16 @@ typedef struct {
} nail_t; } nail_t;
struct sv_s { struct sv_s {
char connectpassword[64]; //password given to server
netadr_t serveraddress; netadr_t serveraddress;
netchan_t netchan; netchan_t netchan;
unsigned char buffer[MAX_PROXY_BUFFER]; //this doesn't cycle. unsigned char buffer[MAX_PROXY_BUFFER]; //this doesn't cycle.
int buffersize; //it memmoves down int buffersize; //it memmoves down
qboolean parsingqtvheader;
unsigned char upstreambuffer[2048];
int upstreambuffersize;
unsigned int parsetime; unsigned int parsetime;
@ -389,17 +480,24 @@ struct sv_s {
qboolean ispaused; qboolean ispaused;
unsigned int packetratelimiter; unsigned int packetratelimiter;
viewer_t *controller; viewer_t *controller;
qboolean proxyplayer; //a player is actually playing on the proxy.
usercmd_t proxyplayerucmds[3];
int proxyplayerucmdnum;
int proxyplayerbuttons;
float proxyplayerangles[3];
float proxyplayerimpulse;
qboolean maysend; qboolean maysend;
FILE *file; FILE *file;
unsigned int filelength; unsigned int filelength;
SOCKET sourcesock; SOCKET sourcesock;
SOCKET listenmvd; //tcp + mvd protocol SOCKET tcpsocket; //tcp + mvd protocol
int tcplistenportnum; int tcplistenportnum;
oproxy_t *proxies; oproxy_t *proxies;
int numproxies;
qboolean parsingconnectiondata; //so reject any new connects for now qboolean parsingconnectiondata; //so reject any new connects for now
@ -424,10 +522,12 @@ struct sv_s {
//options: //options:
char server[MAX_QPATH]; char server[MAX_QPATH];
int streamid;
}; };
struct cluster_s { struct cluster_s {
SOCKET qwdsocket; //udp + quakeworld protocols SOCKET qwdsocket; //udp + quakeworld protocols
SOCKET tcpsocket; //tcp listening socket (for mvd and listings and stuff)
char commandinput[512]; char commandinput[512];
int inputlength; int inputlength;
@ -440,21 +540,28 @@ struct cluster_s {
int numviewers; int numviewers;
sv_t *servers; sv_t *servers;
int numservers; int numservers;
int nextstreamid;
//options //options
int qwlistenportnum; int qwlistenportnum;
char password[256]; int tcplistenportnum;
char adminpassword[256];//password required for rcon etc
char qtvpassword[256]; //password required to connect a proxy
char hostname[256]; char hostname[256];
char master[MAX_QPATH]; char master[MAX_QPATH];
qboolean chokeonnotupdated; qboolean chokeonnotupdated;
qboolean lateforward; qboolean lateforward;
qboolean notalking; qboolean notalking;
qboolean nobsp; qboolean nobsp;
int maxviewers; int maxviewers;
int numproxies;
int maxproxies; int maxproxies;
qboolean wanttoexit; qboolean wanttoexit;
oproxy_t *pendingproxies;
}; };
@ -475,7 +582,7 @@ void ReadString(netmsg_t *b, char *string, int maxlen);
#define clc_bad 0 #define clc_bad 0
#define clc_nop 1 #define clc_nop 1
//define clc_doublemove 2 #define clc_disconnect 2 //NQ only
#define clc_move 3 // [[usercmd_t] #define clc_move 3 // [[usercmd_t]
#define clc_stringcmd 4 // [string] message #define clc_stringcmd 4 // [string] message
#define clc_delta 5 // [byte] sequence number, requests delta compression of message #define clc_delta 5 // [byte] sequence number, requests delta compression of message
@ -492,9 +599,9 @@ void ReadString(netmsg_t *b, char *string, int maxlen);
#define svc_disconnect 2 #define svc_disconnect 2
#define svc_updatestat 3 // [qbyte] [qbyte] #define svc_updatestat 3 // [qbyte] [qbyte]
//#define svc_version 4 // [long] server version //#define svc_version 4 // [long] server version
//#define svc_setview 5 // [short] entity number #define svc_nqsetview 5 // [short] entity number
#define svc_sound 6 // <see code> #define svc_sound 6 // <see code>
//#define svc_time 7 // [float] server time #define svc_nqtime 7 // [float] server time
#define svc_print 8 // [qbyte] id [string] null terminated string #define svc_print 8 // [qbyte] id [string] null terminated string
#define svc_stufftext 9 // [string] stuffed into client's console buffer #define svc_stufftext 9 // [string] stuffed into client's console buffer
// the string should be \n terminated // the string should be \n terminated
@ -502,11 +609,11 @@ void ReadString(netmsg_t *b, char *string, int maxlen);
#define svc_serverdata 11 // [long] protocol ... #define svc_serverdata 11 // [long] protocol ...
#define svc_lightstyle 12 // [qbyte] [string] #define svc_lightstyle 12 // [qbyte] [string]
//#define svc_updatename 13 // [qbyte] [string] #define svc_nqupdatename 13 // [qbyte] [string]
#define svc_updatefrags 14 // [qbyte] [short] #define svc_updatefrags 14 // [qbyte] [short]
//#define svc_clientdata 15 // <shortbits + data> #define svc_nqclientdata 15 // <shortbits + data>
//#define svc_stopsound 16 // <see code> //#define svc_stopsound 16 // <see code>
//#define svc_updatecolors 17 // [qbyte] [qbyte] [qbyte] #define svc_nqupdatecolors 17 // [qbyte] [qbyte] [qbyte]
#define svc_particle 18 // [vec3] <variable> #define svc_particle 18 // [vec3] <variable>
#define svc_damage 19 #define svc_damage 19
@ -516,7 +623,7 @@ void ReadString(netmsg_t *b, char *string, int maxlen);
#define svc_temp_entity 23 // variable #define svc_temp_entity 23 // variable
#define svc_setpause 24 // [qbyte] on / off #define svc_setpause 24 // [qbyte] on / off
//#define svc_signonnum 25 // [qbyte] used for the signon sequence #define svc_nqsignonnum 25 // [qbyte] used for the signon sequence
#define svc_centerprint 26 // [string] to put in center of the screen #define svc_centerprint 26 // [string] to put in center of the screen
@ -577,6 +684,7 @@ void ReadString(netmsg_t *b, char *string, int maxlen);
#define dem_mask 7 #define dem_mask 7
#define PROTOCOL_VERSION_NQ 15
#define PROTOCOL_VERSION 28 #define PROTOCOL_VERSION 28
@ -617,8 +725,10 @@ void ReadString(netmsg_t *b, char *string, int maxlen);
//flags for where a message can be sent, for easy broadcasting
#define Q1 (NQ|QW)
#define QW 1
#define NQ 2
@ -636,10 +746,11 @@ void WriteString2(netmsg_t *b, const char *str);
void WriteString(netmsg_t *b, const char *str); void WriteString(netmsg_t *b, const char *str);
void WriteData(netmsg_t *b, const char *data, int length); void WriteData(netmsg_t *b, const char *data, int length);
void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playermask); void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playermask,int suitablefor);
void Broadcast(cluster_t *cluster, char *buffer, int length); void Broadcast(cluster_t *cluster, char *buffer, int length, int suitablefor);
void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask); void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask);
void BuildServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount); void BuildServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount);
void BuildNQServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount);
SOCKET QW_InitUDPSocket(int port); SOCKET QW_InitUDPSocket(int port);
void QW_UpdateUDPStuff(cluster_t *qtv); void QW_UpdateUDPStuff(cluster_t *qtv);
unsigned int Sys_Milliseconds(void); unsigned int Sys_Milliseconds(void);
@ -658,6 +769,7 @@ int Netchan_IsLocal (netadr_t adr);
void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, char *data, netadr_t adr); void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, char *data, netadr_t adr);
qboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2); qboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2);
qboolean Netchan_Process (netchan_t *chan, netmsg_t *msg); qboolean Netchan_Process (netchan_t *chan, netmsg_t *msg);
qboolean NQNetChan_Process(cluster_t *cluster, netchan_t *chan, netmsg_t *msg);
void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const unsigned char *data); void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const unsigned char *data);
int SendList(sv_t *qtv, int first, const filename_t *list, int svc, netmsg_t *msg); int SendList(sv_t *qtv, int first, const filename_t *list, int svc, netmsg_t *msg);
int Prespawn(sv_t *qtv, int curmsgsize, netmsg_t *msg, int bufnum, int thisplayer); int Prespawn(sv_t *qtv, int curmsgsize, netmsg_t *msg, int bufnum, int thisplayer);
@ -671,6 +783,7 @@ qboolean BSP_Visible(bsp_t *bsp, int leafcount, unsigned short *list);
void BSP_SetupForPosition(bsp_t *bsp, float x, float y, float z); void BSP_SetupForPosition(bsp_t *bsp, float x, float y, float z);
void QW_SetViewersServer(viewer_t *viewer, sv_t *sv); void QW_SetViewersServer(viewer_t *viewer, sv_t *sv);
unsigned short QCRC_Block (unsigned char *start, int count); unsigned short QCRC_Block (unsigned char *start, int count);
unsigned short QCRC_Value(unsigned short crcvalue);
void Netchan_OutOfBand (cluster_t *cluster, SOCKET sock, netadr_t adr, int length, unsigned char *data); void Netchan_OutOfBand (cluster_t *cluster, SOCKET sock, netadr_t adr, int length, unsigned char *data);
void WriteDeltaUsercmd (netmsg_t *m, const usercmd_t *from, usercmd_t *move); void WriteDeltaUsercmd (netmsg_t *m, const usercmd_t *from, usercmd_t *move);
void SendClientCommand(sv_t *qtv, char *fmt, ...); void SendClientCommand(sv_t *qtv, char *fmt, ...);
@ -682,16 +795,29 @@ char *Info_ValueForKey (char *s, const char *key, char *buffer, int buffersize);
void Info_SetValueForStarKey (char *s, const char *key, const char *value, int maxsize); void Info_SetValueForStarKey (char *s, const char *key, const char *value, int maxsize);
void ReadDeltaUsercmd (netmsg_t *m, const usercmd_t *from, usercmd_t *move); void ReadDeltaUsercmd (netmsg_t *m, const usercmd_t *from, usercmd_t *move);
unsigned Com_BlockChecksum (void *buffer, int length); unsigned Com_BlockChecksum (void *buffer, int length);
void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf);
void Sys_Printf(cluster_t *cluster, char *fmt, ...); void Sys_Printf(cluster_t *cluster, char *fmt, ...);
#ifdef _WIN32
int snprintf(char *buffer, int buffersize, char *format, ...);
#endif
#if (defined(_WIN32) && !defined(_VC80_UPGRADE))
int vsnprintf(char *buffer, int buffersize, char *format, va_list argptr);
#endif
qboolean Net_FileProxy(sv_t *qtv, char *filename); oproxy_t *Net_FileProxy(sv_t *qtv, char *filename);
sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, qboolean force, qboolean autoclose, qboolean noduplicates); sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, char *password, qboolean force, qboolean autoclose, qboolean noduplicates);
SOCKET Net_MVDListen(int port); SOCKET Net_MVDListen(int port);
qboolean Net_StopFileProxy(sv_t *qtv); qboolean Net_StopFileProxy(sv_t *qtv);
void SV_FindProxies(SOCKET sock, cluster_t *cluster, sv_t *defaultqtv);
qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend);
void SV_ForwardStream(sv_t *qtv, char *buffer, int length);
unsigned char *FS_ReadFile(char *gamedir, char *filename, unsigned int *size);
void ChooseFavoriteTrack(sv_t *tv);
void DemoViewer_Init(void);
void DemoViewer_Update(sv_t *svtest);
void DemoViewer_Shutdown(void);
#ifdef __cplusplus
}
#endif

View file

@ -63,6 +63,7 @@ LINK32=link.exe
# PROP Use_Debug_Libraries 1 # PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug" # PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug" # PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir "" # PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /Yu"qtv.h" /FD /GZ /c # ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /Yu"qtv.h" /FD /GZ /c
@ -90,10 +91,18 @@ SOURCE=.\bsp.c
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=.\control.c
# End Source File
# Begin Source File
SOURCE=.\crc.c SOURCE=.\crc.c
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=.\forward.c
# End Source File
# Begin Source File
SOURCE=.\mdfour.c SOURCE=.\mdfour.c
# End Source File # End Source File
# Begin Source File # Begin Source File
@ -165,5 +174,184 @@ SOURCE=.\qtv.h
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group # End Group
# Begin Group "viewer"
# PROP Default_Filter ""
# Begin Group "d3d"
# PROP Default_Filter ""
# Begin Source File
SOURCE=.\viewer\d3drend\d3d_backend.cpp
!IF "$(CFG)" == "qtvprox - Win32 Release"
# SUBTRACT CPP /YX
!ELSEIF "$(CFG)" == "qtvprox - Win32 Debug"
# SUBTRACT CPP /YX /Yc /Yu
!ENDIF
# End Source File
# Begin Source File
SOURCE=.\viewer\d3drend\d3d_image.cpp
!IF "$(CFG)" == "qtvprox - Win32 Release"
!ELSEIF "$(CFG)" == "qtvprox - Win32 Debug"
# SUBTRACT CPP /YX /Yc /Yu
!ENDIF
# End Source File
# Begin Source File
SOURCE=.\viewer\d3drend\d3d_video.cpp
!IF "$(CFG)" == "qtvprox - Win32 Release"
!ELSEIF "$(CFG)" == "qtvprox - Win32 Debug"
# SUBTRACT CPP /YX /Yc /Yu
!ENDIF
# End Source File
# End Group
# Begin Source File
SOURCE=.\viewer\cvar.cpp
!IF "$(CFG)" == "qtvprox - Win32 Release"
!ELSEIF "$(CFG)" == "qtvprox - Win32 Debug"
# SUBTRACT CPP /YX /Yc /Yu
!ENDIF
# End Source File
# Begin Source File
SOURCE=.\viewer\gl_backend.cpp
!IF "$(CFG)" == "qtvprox - Win32 Release"
!ELSEIF "$(CFG)" == "qtvprox - Win32 Debug"
# SUBTRACT CPP /YX /Yc /Yu
!ENDIF
# End Source File
# Begin Source File
SOURCE=.\viewer\gl_bsp29.cpp
!IF "$(CFG)" == "qtvprox - Win32 Release"
!ELSEIF "$(CFG)" == "qtvprox - Win32 Debug"
# SUBTRACT CPP /YX /Yc /Yu
!ENDIF
# End Source File
# Begin Source File
SOURCE=.\viewer\gl_image.cpp
!IF "$(CFG)" == "qtvprox - Win32 Release"
!ELSEIF "$(CFG)" == "qtvprox - Win32 Debug"
# SUBTRACT CPP /YX /Yc /Yu
!ENDIF
# End Source File
# Begin Source File
SOURCE=.\viewer\gl_mdl.cpp
!IF "$(CFG)" == "qtvprox - Win32 Release"
!ELSEIF "$(CFG)" == "qtvprox - Win32 Debug"
# SUBTRACT CPP /YX /Yc /Yu
!ENDIF
# End Source File
# Begin Source File
SOURCE=.\viewer\gl_testgrid.cpp
!IF "$(CFG)" == "qtvprox - Win32 Release"
!ELSEIF "$(CFG)" == "qtvprox - Win32 Debug"
# SUBTRACT CPP /YX /Yc /Yu
!ENDIF
# End Source File
# Begin Source File
SOURCE=.\viewer\gl_vidsdl.cpp
!IF "$(CFG)" == "qtvprox - Win32 Release"
!ELSEIF "$(CFG)" == "qtvprox - Win32 Debug"
# SUBTRACT CPP /YX /Yc /Yu
!ENDIF
# End Source File
# Begin Source File
SOURCE=.\viewer\matrix.cpp
!IF "$(CFG)" == "qtvprox - Win32 Release"
!ELSEIF "$(CFG)" == "qtvprox - Win32 Debug"
# SUBTRACT CPP /YX /Yc /Yu
!ENDIF
# End Source File
# Begin Source File
SOURCE=.\viewer\model.cpp
!IF "$(CFG)" == "qtvprox - Win32 Release"
!ELSEIF "$(CFG)" == "qtvprox - Win32 Debug"
# SUBTRACT CPP /YX /Yc /Yu
!ENDIF
# End Source File
# Begin Source File
SOURCE=.\viewer\renderer.cpp
!IF "$(CFG)" == "qtvprox - Win32 Release"
!ELSEIF "$(CFG)" == "qtvprox - Win32 Debug"
# SUBTRACT CPP /YX /Yc /Yu
!ENDIF
# End Source File
# End Group
# End Target # End Target
# End Project # End Project

File diff suppressed because it is too large Load diff

View file

@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define MAX_INFO_KEY 64 #define MAX_INFO_KEY 64
//I apologise for this if it breaks your formatting or anything
#define HELPSTRING "\ #define HELPSTRING "\
FTEQTV proxy commands: (build "__DATE__")\n\ FTEQTV proxy commands: (build "__DATE__")\n\
----------------------\n\ ----------------------\n\
@ -39,6 +40,10 @@ maxviewers, maxproxies\n\
- limit number of connections\n\ - limit number of connections\n\
status, choke, late, talking, nobsp, reconnect, exec, password, master, hostname, record, stop, quit\n\n" status, choke, late, talking, nobsp, reconnect, exec, password, master, hostname, record, stop, quit\n\n"
char *Info_ValueForKey (char *s, const char *key, char *buffer, int buffersize) char *Info_ValueForKey (char *s, const char *key, char *buffer, int buffersize)
{ {
char pkey[1024]; char pkey[1024];
@ -309,9 +314,400 @@ skipwhite:
return data; return data;
} }
#define MAX_ARGS 8 #define MAX_ARGS 8
#define ARG_LEN 512 #define ARG_LEN 512
char *Cluster_Rcon_Dispatch(cluster_t *cluster, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) typedef char *(*dispatchrconcommand_t)(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand);
char *Cmd_Hostname(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
strncpy(cluster->hostname, arg[1], sizeof(cluster->hostname)-1);
return "hostname will change at start of next map\n"; //I'm too lazy to alter the serverinfo here.
}
char *Cmd_Master(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
netadr_t addr;
strncpy(cluster->master, arg[1], sizeof(cluster->master)-1);
cluster->mastersendtime = cluster->curtime;
if (NET_StringToAddr(arg[1], &addr, 27000)) //send a ping like a qw server does. this is kinda pointless of course.
NET_SendPacket (cluster, cluster->qwdsocket, 1, "k", addr);
return "Master server set.\n";
}
char *Cmd_UDPPort(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
int news;
int newp = atoi(arg[1]);
news = QW_InitUDPSocket(newp);
if (news != INVALID_SOCKET)
{
cluster->mastersendtime = cluster->curtime;
closesocket(cluster->qwdsocket);
cluster->qwdsocket = news;
cluster->qwlistenportnum = newp;
return "Opened udp port (all connected qw clients will time out)\n";
}
else
return "Failed to open udp port\n";
}
char *Cmd_AdminPassword(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
if (!localcommand)
return "Rejecting remote password change.\n";
strncpy(cluster->adminpassword, arg[1], sizeof(cluster->adminpassword)-1);
return "Password changed.\n";
}
char *Cmd_QTVConnect(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
if (!*arg[1])
return "connect requires an ip:port parameter\n";
memmove(arg[1]+4, arg[1], ARG_LEN-5);
strncpy(arg[1], "tcp:", 4);
if (!QTV_NewServerConnection(cluster, arg[1], arg[2], false, false, false))
return "Failed to connect to server, connection aborted\n";
return "Source registered\n";
}
char *Cmd_QWConnect(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
if (!*arg[1])
return "connect requires an ip:port parameter\n";
memmove(arg[1]+4, arg[1], ARG_LEN-5);
strncpy(arg[1], "udp:", 4);
if (!QTV_NewServerConnection(cluster, arg[1], arg[2], false, false, false))
return "Failed to connect to server, connection aborted\n";
return "Source registered\n";
}
char *Cmd_MVDConnect(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
if (!*arg[1])
return "adddemo requires an filename parameter\n";
if (!localcommand)
if (*arg[1] == '\\' || *arg[1] == '/' || strstr(arg[1], "..") || arg[1][1] == ':')
return "Absolute paths are prohibited.\n";
memmove(arg[1]+5, arg[1], ARG_LEN-6);
strncpy(arg[1], "file:", 5);
if (!QTV_NewServerConnection(cluster, arg[1], arg[2], false, false, false))
return "Failed to connect to server, connection aborted\n";
return "Source registered\n";
}
char *Cmd_Exec(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
FILE *f;
char line[512], *res;
if (!localcommand)
if (*arg[1] == '\\' || *arg[1] == '/' || strstr(arg[1], "..") || arg[1][1] == ':')
return "Absolute paths are prohibited.\n";
f = fopen(arg[1], "rt");
if (!f)
{
snprintf(buffer, sizeofbuffer, "Couldn't exec \"%s\"\n", arg[1]);
return buffer;
}
else
{
while(fgets(line, sizeof(line)-1, f))
{
res = Rcon_Command(cluster, qtv, line, buffer, sizeofbuffer, localcommand);
Sys_Printf(cluster, "%s", res); //this is perhaps wrong.
}
fclose(f);
return "Execed\n";
}
}
char *Cmd_Status(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
buffer[0] = '\0';
sprintf(buffer, "%i connections\n", cluster->numservers);
strcat(buffer, "Options:\n");
strcat(buffer, " Hostname ");
strcat(buffer, cluster->hostname);
strcat(buffer, "\n");
if (cluster->chokeonnotupdated)
strcat(buffer, " Choke\n");
if (cluster->lateforward)
strcat(buffer, " Late forwarding\n");
if (!cluster->notalking)
strcat(buffer, " Talking allowed\n");
if (cluster->nobsp)
strcat(buffer, " No BSP loading\n");
strcat(buffer, "\n");
if (qtv)
{
strcat(buffer, "Selected server: ");
strcat(buffer, qtv->server);
strcat(buffer, "\n");
if (qtv->file)
strcat(buffer, "Playing from file\n");
if (qtv->sourcesock != INVALID_SOCKET)
strcat(buffer, "Connected\n");
if (qtv->parsingqtvheader || qtv->parsingconnectiondata)
strcat(buffer, "Waiting for gamestate\n");
if (qtv->controller)
{
strcat(buffer, "Spectating through ");
strcat(buffer, qtv->controller->name);
strcat(buffer, "\n");
}
if (*qtv->modellist[1].name)
{
strcat(buffer, "Map name ");
strcat(buffer, qtv->modellist[1].name);
strcat(buffer, "\n");
}
if (qtv->connectpassword)
strcat(buffer, "Using a password\n");
if (qtv->tcpsocket != INVALID_SOCKET)
{
strcat(buffer, "Listening for proxies (");
sprintf(arg[0], "%i", qtv->tcplistenportnum);
strcat(buffer, arg[0]);
strcat(buffer, ")\n");
}
if (qtv->bsp)
{
strcat(buffer, "BSP (");
strcat(buffer, qtv->mapname);
strcat(buffer, ") is loaded\n");
}
}
return buffer;
}
char *Cmd_Choke(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
cluster->chokeonnotupdated = !!atoi(arg[1]);
return "choke-until-update set\n";
}
char *Cmd_Late(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
cluster->lateforward = !!atoi(arg[1]);
return "late forwarding set\n";
}
char *Cmd_Talking(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
cluster->notalking = !atoi(arg[1]);
return "talking permissions set\n";
}
char *Cmd_NoBSP(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
cluster->nobsp = !!atoi(arg[1]);
return "nobsp will change at start of next map\n";
}
char *Cmd_MaxViewers(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
cluster->maxviewers = atoi(arg[2]);
return "maxviewers set\n";
}
char *Cmd_MaxProxies(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
cluster->maxproxies = atoi(arg[2]);
return "maxproxies set\n";
}
char *Cmd_Ping(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
netadr_t addr;
if (NET_StringToAddr(arg[1], &addr, 27500))
{
NET_SendPacket (cluster, cluster->qwdsocket, 1, "k", addr);
return "pinged\n";
}
return "couldn't resolve\n";
}
char *Cmd_Help(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
return HELPSTRING;
}
char *Cmd_Echo(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
return "Poly wants a cracker.\n";
}
char *Cmd_Quit(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
if (!localcommand)
return "Remote shutdown refused.\n";
cluster->wanttoexit = true;
return "Shutting down.\n";
}
char *Cmd_Disconnect(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
QTV_Shutdown(qtv);
return "Disconnected\n";
}
char *Cmd_Record(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
if (!*arg[1])
return "record requires a filename on the proxy's machine\n";
if (!localcommand)
if (*arg[1] == '\\' || *arg[1] == '/' || strstr(arg[1], "..") || arg[1][1] == ':')
return "Absolute paths are prohibited.\n";
if (Net_FileProxy(qtv, arg[1]))
return "Recording to disk\n";
else
return "Failed to open file\n";
}
char *Cmd_Stop(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
if (Net_StopFileProxy(qtv))
return "stopped\n";
else
return "not recording to disk\n";
}
char *Cmd_Reconnect(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
if (QTV_Connect(qtv, qtv->server))
return "Reconnected\n";
else
return "Failed to reconnect (will keep trying)\n";
}
char *Cmd_MVDPort(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
int news;
int newp = atoi(arg[1]);
if (!newp)
{
if (qtv->tcpsocket != INVALID_SOCKET)
{
closesocket(qtv->tcpsocket);
qtv->tcpsocket = INVALID_SOCKET;
qtv->tcplistenportnum = 0;
return "mvd port is now closed\n";
}
return "Already closed\n";
}
else
{
news = Net_MVDListen(newp);
if (news != INVALID_SOCKET)
{
if (qtv->tcpsocket != INVALID_SOCKET)
closesocket(qtv->tcpsocket);
qtv->tcpsocket = news;
qtv->disconnectwhennooneiswatching = false;
qtv->tcplistenportnum = newp;
return "Opened tcp port\n";
}
else
return "Failed to open tcp port\n";
}
}
char *Cmd_Commands(cluster_t *cluster, sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
return "";
}
typedef struct rconcommands_s {
char *name;
qboolean serverspecific; //works within a qtv context
qboolean clusterspecific; //works without a qtv context (ignores context)
dispatchrconcommand_t func;
} rconcommands_t;
const rconcommands_t rconcommands[] =
{
{"exec", 1, 1, Cmd_Exec},
{"status", 1, 1, Cmd_Status},
{"help", 0, 1, Cmd_Help},
{"commands", 0, 1, Cmd_Commands},
{"hostname", 0, 1, Cmd_Hostname},
{"master", 0, 1, Cmd_Master},
{"udpport", 0, 1, Cmd_UDPPort},
{"port", 0, 1, Cmd_UDPPort},
{"adminpassword",0, 1, Cmd_AdminPassword},
{"rconpassword",0, 1, Cmd_AdminPassword},
{"qtv", 0, 1, Cmd_QTVConnect},
{"addserver", 0, 1, Cmd_QTVConnect},
{"connect", 0, 1, Cmd_QTVConnect},
{"qw", 0, 1, Cmd_QWConnect},
{"demo", 0, 1, Cmd_MVDConnect},
{"playdemo", 0, 1, Cmd_MVDConnect},
{"choke", 0, 1, Cmd_Choke},
{"late", 0, 1, Cmd_Late},
{"talking", 0, 1, Cmd_Talking},
{"nobsp", 0, 1, Cmd_NoBSP},
{"maxviewers", 0, 1, Cmd_MaxViewers},
{"maxproxies", 0, 1, Cmd_MaxProxies},
{"ping", 0, 1, Cmd_Ping},
{"reconnect", 0, 1, Cmd_Reconnect},
{"echo", 0, 1, Cmd_Echo},
{"quit", 0, 1, Cmd_Quit},
{"disconnect", 1, 0, Cmd_Disconnect},
{"record", 1, 0, Cmd_Record},
{"stop", 1, 0, Cmd_Stop},
{"tcpport", 1, 0, Cmd_MVDPort},
{"mvdport", 1, 0, Cmd_MVDPort},
{NULL}
};
/*
static char *Cluster_Rcon_Dispatch(cluster_t *cluster, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{ {
if (!strcmp(arg[0], "hostname")) if (!strcmp(arg[0], "hostname"))
{ {
@ -363,9 +759,9 @@ char *Cluster_Rcon_Dispatch(cluster_t *cluster, char *arg[MAX_ARGS], char *buffe
memmove(arg[1]+4, arg[1], ARG_LEN-5); memmove(arg[1]+4, arg[1], ARG_LEN-5);
strncpy(arg[1], "tcp:", 4); strncpy(arg[1], "tcp:", 4);
if (!QTV_NewServerConnection(cluster, arg[1], false, !strcmp(arg[0], "connect"), false)) if (!QTV_NewServerConnection(cluster, arg[1], false, false, false))
return "Failed to connect to server, connection aborted\n"; return "Failed to connect to server, connection aborted\n";
return "Connection registered\n"; return "Source registered\n";
} }
else if (!strcmp(arg[0], "qw")) else if (!strcmp(arg[0], "qw"))
{ {
@ -377,7 +773,7 @@ char *Cluster_Rcon_Dispatch(cluster_t *cluster, char *arg[MAX_ARGS], char *buffe
if (!QTV_NewServerConnection(cluster, arg[1], false, false, false)) if (!QTV_NewServerConnection(cluster, arg[1], false, false, false))
return "Failed to connect to server, connection aborted\n"; return "Failed to connect to server, connection aborted\n";
return "Connection registered\n"; return "Source registered\n";
} }
else if (!strcmp(arg[0], "demo") || !strcmp(arg[0], "adddemo") || !strcmp(arg[0], "addfile")) else if (!strcmp(arg[0], "demo") || !strcmp(arg[0], "adddemo") || !strcmp(arg[0], "addfile"))
{ {
@ -393,7 +789,7 @@ char *Cluster_Rcon_Dispatch(cluster_t *cluster, char *arg[MAX_ARGS], char *buffe
if (!QTV_NewServerConnection(cluster, arg[1], false, false, false)) if (!QTV_NewServerConnection(cluster, arg[1], false, false, false))
return "Failed to connect to server, connection aborted\n"; return "Failed to connect to server, connection aborted\n";
return "Connection registered\n"; return "Source registered\n";
} }
else if (!strcmp(arg[0], "exec")) else if (!strcmp(arg[0], "exec"))
{ {
@ -522,7 +918,7 @@ char *Cluster_Rcon_Dispatch(cluster_t *cluster, char *arg[MAX_ARGS], char *buffe
return NULL; return NULL;
} }
char *Server_Rcon_Dispatch(sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand) static char *Server_Rcon_Dispatch(sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{ {
#define TOKENIZE_PUNCTUATION "" #define TOKENIZE_PUNCTUATION ""
@ -702,7 +1098,7 @@ char *Server_Rcon_Dispatch(sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int siz
return NULL; return NULL;
} }
} }
*/
char *Rcon_Command(cluster_t *cluster, sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qboolean localcommand) char *Rcon_Command(cluster_t *cluster, sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qboolean localcommand)
{ {
#define TOKENIZE_PUNCTUATION "" #define TOKENIZE_PUNCTUATION ""
@ -710,7 +1106,6 @@ char *Rcon_Command(cluster_t *cluster, sv_t *qtv, char *command, char *buffer, i
int i; int i;
char arg[MAX_ARGS][ARG_LEN]; char arg[MAX_ARGS][ARG_LEN];
char *argptrs[MAX_ARGS]; char *argptrs[MAX_ARGS];
char *result;
for (i = 0; i < MAX_ARGS; i++) for (i = 0; i < MAX_ARGS; i++)
{ {
@ -718,22 +1113,36 @@ char *Rcon_Command(cluster_t *cluster, sv_t *qtv, char *command, char *buffer, i
argptrs[i] = arg[i]; argptrs[i] = arg[i];
} }
if (!qtv && cluster->numservers==1)
qtv = cluster->servers;
buffer[0] = 0;
if (qtv) if (qtv)
{ //if there is a specific connection targetted { //if there is a specific connection targetted
result = Server_Rcon_Dispatch(qtv, argptrs, buffer, sizeofbuffer, localcommand);
if (result) for (i = 0; rconcommands[i].name; i++)
return result; {
} if (rconcommands[i].serverspecific)
else if (cluster->numservers == 1) if (!strcmp(rconcommands[i].name, argptrs[0]))
{ //if it's a single-connection proxy return rconcommands[i].func(cluster, qtv, argptrs, buffer, sizeofbuffer, localcommand);
result = Server_Rcon_Dispatch(cluster->servers, argptrs, buffer, sizeofbuffer, localcommand); }
if (result) }
return result;
for (i = 0; rconcommands[i].name; i++)
{
if (!strcmp(rconcommands[i].name, argptrs[0]))
{
if (rconcommands[i].clusterspecific)
return rconcommands[i].func(cluster, NULL, argptrs, buffer, sizeofbuffer, localcommand);
else if (rconcommands[i].serverspecific)
{
snprintf(buffer, sizeofbuffer, "Command \"%s\" requires a targeted server.\n", arg[0]);
return buffer;
}
}
} }
result = Cluster_Rcon_Dispatch(cluster, argptrs, buffer, sizeofbuffer, localcommand);
if (result)
return result;
snprintf(buffer, sizeofbuffer, "Command \"%s\" not recognised.\n", arg[0]); snprintf(buffer, sizeofbuffer, "Command \"%s\" not recognised.\n", arg[0]);
return buffer; return buffer;

File diff suppressed because it is too large Load diff