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:
parent
42c43d960f
commit
1d4ada39d2
11 changed files with 3626 additions and 1201 deletions
|
@ -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
|
||||
|
|
99
fteqtv/bsp.c
99
fteqtv/bsp.c
|
@ -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
441
fteqtv/control.c
Normal 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
840
fteqtv/forward.c
Normal 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, " %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;
|
||||
}
|
177
fteqtv/netchan.c
177
fteqtv/netchan.c
|
@ -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
|
||||
|
|
179
fteqtv/parse.c
179
fteqtv/parse.c
|
@ -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));
|
||||
|
|
188
fteqtv/qtv.h
188
fteqtv/qtv.h
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
1018
fteqtv/qw.c
1018
fteqtv/qw.c
File diff suppressed because it is too large
Load diff
449
fteqtv/rcon.c
449
fteqtv/rcon.c
|
@ -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;
|
||||
|
|
1240
fteqtv/source.c
1240
fteqtv/source.c
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue