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
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
$(CC) $(CFLAGS) $(OBJS) -o $@.db -lm $(LDFLAGS)
$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $@.db -lm
$(STRIP) $(STRIPFLAGS) $@.db -o $@
qtv.exe:
qtv.exe: *.c *.h
$(MAKE) qtv CFLAGS=-mno-cygwin LDFLAGS="-lwsock32 -lwinmm"
mv qtv qtv.exe
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)
{
unsigned char *data;
@ -215,18 +129,11 @@ bsp_t *BSP_LoadModel(cluster_t *cluster, char *gamedir, char *bspname)
bsp_t *bsp;
if (!gamedir || !*gamedir)
gamedir = "qw";
data = ReadFile_WINDOWSSUCKS(gamedir, bspname, &size);
data = FS_ReadFile(gamedir, bspname, &size);
if (!data)
{
data = ReadFile_WINDOWSSUCKS("id1", bspname, &size);
if (!data)
{
Sys_Printf(cluster, "Couldn't open bsp file \"%s\" (gamedir \"%s\")\n", bspname, gamedir);
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)
{
netmsg_t send;
unsigned char send_buf[MAX_MSGLEN + PACKET_HEADER];
unsigned char send_buf[MAX_NQMSGLEN + PACKET_HEADER];
qboolean send_reliable;
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
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

View file

@ -56,6 +56,38 @@ unsigned int ReadLong(netmsg_t *b)
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)
{
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;
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->trackplayer>=0)
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;
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)
{
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;
}
}
void Broadcast(cluster_t *cluster, char *buffer, int length)
void Broadcast(cluster_t *cluster, char *buffer, int length, int suitablefor)
{
viewer_t *v;
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->spawnstatic_count = 0;
memset(tv->modellist, 0, sizeof(tv->modellist));
memset(tv->soundlist, 0, sizeof(tv->soundlist));
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)
{
char nqversion[3];
tv->cdtrack = ReadByte(m);
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)
{
@ -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))
{
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);
}
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;
}
}
@ -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));
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)
@ -421,17 +467,29 @@ static void ParseServerinfo(sv_t *tv, netmsg_t *m)
Info_SetValueForStarKey(tv->serverinfo, key, value, sizeof(tv->serverinfo));
if (!tv->parsingconnectiondata)
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, dem_all, (unsigned)-1);
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)
{
unsigned char *t;
char text[1024];
char buffer[1024];
int level;
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)
{
@ -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)
{
char text[1024];
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)
{
@ -533,7 +592,7 @@ static void ParseStaticSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
tv->staticsound_count++;
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)
@ -545,7 +604,7 @@ static void ParseIntermission(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
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)
@ -561,7 +620,7 @@ void ParseSpawnStatic(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
tv->spawnstatic_count++;
if (!tv->parsingconnectiondata)
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1);
}
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[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++)
{
@ -786,7 +851,7 @@ static void ParseUpdatePing(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
else
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)
@ -801,7 +866,7 @@ static void ParseUpdateFrags(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
else
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)
@ -824,7 +889,7 @@ static void ParseUpdateStat(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
else
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)
{
@ -846,7 +911,7 @@ static void ParseUpdateStatLong(sv_t *tv, netmsg_t *m, int to, unsigned int mask
else
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)
@ -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)
@ -880,7 +945,7 @@ static void ParsePacketloss(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
else
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)
@ -896,7 +961,7 @@ static void ParseUpdateEnterTime(sv_t *tv, netmsg_t *m, int to, unsigned int mas
else
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)
@ -930,7 +995,7 @@ static void ParseSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
for (i=0 ; i<3 ; i++)
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)
@ -940,7 +1005,7 @@ static void ParseDamage(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
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 {
@ -1041,7 +1106,7 @@ static void ParseTempEntity(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
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)
@ -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)
@ -1121,6 +1186,10 @@ void ParseDownload(sv_t *tv, netmsg_t *m)
{
fclose(tv->file);
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");
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);
break;
//#define svc_time 7 // [float] server time
case svc_print:
ParsePrint(tv, &buf, to, mask);
break;
@ -1198,12 +1268,21 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
case svc_setangle:
if (!tv->usequkeworldprotocols)
ReadByte(&buf);
ReadByte(&buf);
ReadByte(&buf);
ReadByte(&buf);
tv->proxyplayerangles[0] = ReadByte(&buf)*360.0/255;
tv->proxyplayerangles[1] = ReadByte(&buf)*360.0/255;
tv->proxyplayerangles[2] = ReadByte(&buf)*360.0/255;
if (tv->usequkeworldprotocols && tv->controller)
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;
case svc_serverdata:
@ -1215,9 +1294,11 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
break;
//#define svc_updatename 13 // [qbyte] [string]
case svc_updatefrags:
ParseUpdateFrags(tv, &buf, to, mask);
break;
//#define svc_clientdata 15 // <shortbits + data>
//#define svc_stopsound 16 // <see code>
//#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);
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;
case svc_damage:
ParseDamage(tv, &buf, to, mask);
break;
@ -1252,7 +1334,7 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
case svc_setpause: // [qbyte] on / off
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;
//#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:
ParseCDTrack(tv, &buf, to, mask);
break;
//#define svc_sellscreen 33
//#define svc_cutscene 34 //hmm... nq only... added after qw tree splitt?
case svc_smallkick:
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;
case svc_updateping:
@ -1300,7 +1383,7 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
case svc_muzzleflash:
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;
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]
ParseDownload(tv, &buf);
break;
case svc_playerinfo:
ParsePlayerInfo(tv, &buf, clearoldplayers);
clearoldplayers = false;
break;
case svc_nails:
ParseNails(tv, &buf, false);
break;
case svc_chokecount:
ReadByte(&buf);
break;
case svc_modellist:
i = ParseList(tv, &buf, tv->modellist, to, mask);
if (!i)
@ -1328,7 +1414,7 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
if (tv->bsp)
BSP_Free(tv->bsp);
if (tv->cluster->nobsp && !tv->usequkeworldprotocols)
if (tv->cluster->nobsp || !tv->usequkeworldprotocols)
tv->bsp = NULL;
else
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);
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;
}
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");
if (!tv->file)
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);
}
break;
case svc_packetentities:
FlushPacketEntities(tv);
ParsePacketEntities(tv, &buf);
@ -1392,15 +1479,16 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
ReadByte(&buf);
ParsePacketEntities(tv, &buf);
break;
//#define svc_maxspeed 49 // maxspeed change, for prediction
case svc_entgravity: // gravity change, for prediction
ReadFloat(&buf);
Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask);
break;
case svc_maxspeed:
ReadFloat(&buf);
Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask);
break;
case svc_entgravity: // gravity change, for prediction
ReadFloat(&buf);
Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask, QW);
break;
case svc_maxspeed:
ReadFloat(&buf);
Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask, QW);
break;
case svc_setinfo:
ParseSetInfo(tv, &buf);
break;
@ -1413,6 +1501,7 @@ case svc_maxspeed:
case svc_nails2:
ParseNails(tv, &buf, true);
break;
default:
buf.readpos = buf.startpos;
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
#include <conio.h>
#include <winsock.h>
#pragma comment (lib, "wsock32.lib")
#include <winsock.h> //this includes windows.h and is the reason for much compiling slowness with windows builds.
#ifdef _MSC_VER
#pragma comment (lib, "wsock32.lib")
#endif
#define qerrno WSAGetLastError()
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EINPROGRESS WSAEINPROGRESS
#define ECONNREFUSED WSAECONNREFUSED
#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
//okay, so warnings are here to help... they're ugly though.
#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_USERINFO 192
#define MAX_CLIENTS 32
#define MAX_STATS 32
#define MAX_LIST 256
#define MAX_MODELS 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 DEFAULT_HOSTNAME "FTEQTV"
#define MAX_PROXY_INBUFFER 4096
#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)
@ -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_STRING "8"
#define MAX_STATS 32
#define STAT_HEALTH 0
#define STAT_FRAGS 1
#define STAT_WEAPON 2
#define STAT_AMMO 3
#define STAT_ARMOR 4
#define STAT_WEAPONFRAME 5
#define STAT_SHELLS 6
#define STAT_NAILS 7
#define STAT_ROCKETS 8
#define STAT_CELLS 9
#define STAT_ACTIVEWEAPON 10
#define STAT_TOTALSECRETS 11
#define STAT_TOTALMONSTERS 12
#define STAT_SECRETS 13 // bumped on client side by svc_foundsecret
#define STAT_MONSTERS 14 // bumped by svc_killedmonster
#define STAT_ITEMS 15
#define STAT_TIME 17 //A ZQ hack, sending time via a stat.
//this allows l33t engines to interpolate properly without spamming at a silly high fps.
#ifndef __cplusplus
typedef enum {false, true} qboolean;
#else
typedef int qboolean;
extern "C" {
#endif
typedef unsigned char netadr_t[64];
#define NQ_PACKETS_PER_SECOND 100
#define MAX_NQMSGLEN 8000
#define MAX_MSGLEN 1400
#define MAX_NQDATAGRAM 1024
#define MAX_BACKBUF_SIZE 1000 //this is smaller so we don't loose entities when lagging
//NQ transport layer defines
#define NETFLAG_LENGTH_MASK 0x0000ffff
#define NETFLAG_DATA 0x00010000
#define NETFLAG_ACK 0x00020000
#define NETFLAG_NAK 0x00040000
#define NETFLAG_EOM 0x00080000
#define NETFLAG_UNRELIABLE 0x00100000
#define NETFLAG_CTL 0x80000000
#define CCREQ_CONNECT 0x01
#define CCREP_ACCEPT 0x81
#define CCREP_REJECT 0x82
#define NET_GAMENAME_NQ "QUAKE"
#define NET_PROTOCOL_VERSION 3
typedef struct {
unsigned int readpos;
unsigned int cursize;
@ -171,10 +236,11 @@ typedef struct {
int reliable_length;
qboolean drop;
qboolean isclient;
qboolean isnqprotocol;
netmsg_t message;
char message_buf[MAX_MSGLEN]; //reliable message being built
char reliable_buf[MAX_MSGLEN]; //reliable message that we're making sure arrives.
char message_buf[MAX_NQMSGLEN]; //reliable message being built
char reliable_buf[MAX_NQMSGLEN]; //reliable message that we're making sure arrives.
float rate;
@ -186,6 +252,15 @@ typedef struct {
unsigned int incoming_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;
typedef struct {
@ -199,7 +274,7 @@ typedef struct {
unsigned char colormap;
unsigned char skinnum;
short origin[3];
unsigned char angles[3];
char angles[3];
unsigned char effects;
} entity_state_t;
typedef struct {
@ -208,7 +283,7 @@ typedef struct {
unsigned char skinnum;
short origin[3];
short velocity[3];
unsigned short angles[3];
short angles[3];
unsigned char effects;
unsigned char weaponframe;
} player_state_t;
@ -251,6 +326,8 @@ typedef struct sv_s sv_t;
typedef struct cluster_s cluster_t;
typedef struct viewer_s {
qboolean drop;
unsigned int timeout;
unsigned int nextpacket; //for nq clients
netchan_t netchan;
qboolean maysend;
qboolean chokeme;
@ -291,12 +368,21 @@ typedef struct viewer_s {
int fwdval; //for scrolling up/down the menu using +forward/+back :)
} viewer_t;
//'other proxy', these are mvd stream clients.
typedef struct oproxy_s {
int authkey;
unsigned int droptime;
qboolean flushing;
qboolean drop;
FILE *file;
SOCKET sock;
sv_t *defaultstream;
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 int buffersize; //use cyclic buffering.
@ -329,11 +415,16 @@ typedef struct {
} nail_t;
struct sv_s {
char connectpassword[64]; //password given to server
netadr_t serveraddress;
netchan_t netchan;
unsigned char buffer[MAX_PROXY_BUFFER]; //this doesn't cycle.
int buffersize; //it memmoves down
qboolean parsingqtvheader;
unsigned char upstreambuffer[2048];
int upstreambuffersize;
unsigned int parsetime;
@ -389,17 +480,24 @@ struct sv_s {
qboolean ispaused;
unsigned int packetratelimiter;
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;
FILE *file;
unsigned int filelength;
SOCKET sourcesock;
SOCKET listenmvd; //tcp + mvd protocol
SOCKET tcpsocket; //tcp + mvd protocol
int tcplistenportnum;
oproxy_t *proxies;
int numproxies;
qboolean parsingconnectiondata; //so reject any new connects for now
@ -424,10 +522,12 @@ struct sv_s {
//options:
char server[MAX_QPATH];
int streamid;
};
struct cluster_s {
SOCKET qwdsocket; //udp + quakeworld protocols
SOCKET tcpsocket; //tcp listening socket (for mvd and listings and stuff)
char commandinput[512];
int inputlength;
@ -440,21 +540,28 @@ struct cluster_s {
int numviewers;
sv_t *servers;
int numservers;
int nextstreamid;
//options
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 master[MAX_QPATH];
qboolean chokeonnotupdated;
qboolean lateforward;
qboolean notalking;
qboolean nobsp;
int maxviewers;
int numproxies;
int maxproxies;
qboolean wanttoexit;
oproxy_t *pendingproxies;
};
@ -475,7 +582,7 @@ void ReadString(netmsg_t *b, char *string, int maxlen);
#define clc_bad 0
#define clc_nop 1
//define clc_doublemove 2
#define clc_disconnect 2 //NQ only
#define clc_move 3 // [[usercmd_t]
#define clc_stringcmd 4 // [string] message
#define clc_delta 5 // [byte] sequence number, requests delta compression of message
@ -492,9 +599,9 @@ void ReadString(netmsg_t *b, char *string, int maxlen);
#define svc_disconnect 2
#define svc_updatestat 3 // [qbyte] [qbyte]
//#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_time 7 // [float] server time
#define svc_nqtime 7 // [float] server time
#define svc_print 8 // [qbyte] id [string] null terminated string
#define svc_stufftext 9 // [string] stuffed into client's console buffer
// the string should be \n terminated
@ -502,11 +609,11 @@ void ReadString(netmsg_t *b, char *string, int maxlen);
#define svc_serverdata 11 // [long] protocol ...
#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_clientdata 15 // <shortbits + data>
#define svc_nqclientdata 15 // <shortbits + data>
//#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_damage 19
@ -516,7 +623,7 @@ void ReadString(netmsg_t *b, char *string, int maxlen);
#define svc_temp_entity 23 // variable
#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
@ -577,6 +684,7 @@ void ReadString(netmsg_t *b, char *string, int maxlen);
#define dem_mask 7
#define PROTOCOL_VERSION_NQ 15
#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 WriteData(netmsg_t *b, const char *data, int length);
void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playermask);
void Broadcast(cluster_t *cluster, char *buffer, int length);
void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playermask,int suitablefor);
void Broadcast(cluster_t *cluster, char *buffer, int length, int suitablefor);
void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask);
void 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);
void QW_UpdateUDPStuff(cluster_t *qtv);
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);
qboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2);
qboolean Netchan_Process (netchan_t *chan, netmsg_t *msg);
qboolean NQNetChan_Process(cluster_t *cluster, netchan_t *chan, netmsg_t *msg);
void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const unsigned char *data);
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);
@ -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 QW_SetViewersServer(viewer_t *viewer, sv_t *sv);
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 WriteDeltaUsercmd (netmsg_t *m, const usercmd_t *from, usercmd_t *move);
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 ReadDeltaUsercmd (netmsg_t *m, const usercmd_t *from, usercmd_t *move);
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, ...);
#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);
sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, qboolean force, qboolean autoclose, qboolean noduplicates);
oproxy_t *Net_FileProxy(sv_t *qtv, char *filename);
sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, char *password, qboolean force, qboolean autoclose, qboolean noduplicates);
SOCKET Net_MVDListen(int port);
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 Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# 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 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
# Begin Source File
SOURCE=.\control.c
# End Source File
# Begin Source File
SOURCE=.\crc.c
# End Source File
# Begin Source File
SOURCE=.\forward.c
# End Source File
# Begin Source File
SOURCE=.\mdfour.c
# End 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"
# 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 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
//I apologise for this if it breaks your formatting or anything
#define HELPSTRING "\
FTEQTV proxy commands: (build "__DATE__")\n\
----------------------\n\
@ -39,6 +40,10 @@ maxviewers, maxproxies\n\
- limit number of connections\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 pkey[1024];
@ -309,9 +314,400 @@ skipwhite:
return data;
}
#define MAX_ARGS 8
#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"))
{
@ -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);
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 "Connection registered\n";
return "Source registered\n";
}
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))
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"))
{
@ -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))
return "Failed to connect to server, connection aborted\n";
return "Connection registered\n";
return "Source registered\n";
}
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;
}
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 ""
@ -702,7 +1098,7 @@ char *Server_Rcon_Dispatch(sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int siz
return NULL;
}
}
*/
char *Rcon_Command(cluster_t *cluster, sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qboolean localcommand)
{
#define TOKENIZE_PUNCTUATION ""
@ -710,7 +1106,6 @@ char *Rcon_Command(cluster_t *cluster, sv_t *qtv, char *command, char *buffer, i
int i;
char arg[MAX_ARGS][ARG_LEN];
char *argptrs[MAX_ARGS];
char *result;
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];
}
if (!qtv && cluster->numservers==1)
qtv = cluster->servers;
buffer[0] = 0;
if (qtv)
{ //if there is a specific connection targetted
result = Server_Rcon_Dispatch(qtv, argptrs, buffer, sizeofbuffer, localcommand);
if (result)
return result;
}
else if (cluster->numservers == 1)
{ //if it's a single-connection proxy
result = Server_Rcon_Dispatch(cluster->servers, argptrs, buffer, sizeofbuffer, localcommand);
if (result)
return result;
for (i = 0; rconcommands[i].name; i++)
{
if (rconcommands[i].serverspecific)
if (!strcmp(rconcommands[i].name, argptrs[0]))
return rconcommands[i].func(cluster, qtv, argptrs, buffer, sizeofbuffer, localcommand);
}
}
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]);
return buffer;

File diff suppressed because it is too large Load diff