diff --git a/fteqtv/Makefile b/fteqtv/Makefile index 1e89411be..10909d433 100644 --- a/fteqtv/Makefile +++ b/fteqtv/Makefile @@ -4,7 +4,7 @@ STRIP=strip STRIPFLAGS=--strip-unneeded --remove-section=.comment -OBJS = netchan.o parse.o qw.o source.o bsp.o rcon.o +OBJS = netchan.o parse.o qw.o source.o bsp.o rcon.o mdfour.o qtv: $(OBJS) qtv.h $(CC) $(CFLAGS) $(OBJS) -o $@.db -lm $(LDFLAGS) diff --git a/fteqtv/bsp.c b/fteqtv/bsp.c index 3b922d4d4..037db9cbd 100644 --- a/fteqtv/bsp.c +++ b/fteqtv/bsp.c @@ -28,6 +28,8 @@ typedef struct { int child[2]; } node_t; struct bsp_s { + unsigned int fullchecksum; + unsigned int visedchecksum; node_t *nodes; unsigned char *pvslump; unsigned char **pvsofs; @@ -69,6 +71,7 @@ typedef struct { int fileofs, filelen; } lump_t; +#define LUMP_ENTITIES 0 #define LUMP_PLANES 1 #define LUMP_VISIBILITY 4 #define LUMP_NODES 5 @@ -186,7 +189,8 @@ unsigned char *ReadFile_WINDOWSSUCKS(char *gamedir, char *filename, int *size) fseek(f, 0, SEEK_SET); } data = malloc(*size); - fread(data, 1, *size, f); + if (data) + fread(data, 1, *size, f); fclose(f); return data; @@ -204,6 +208,7 @@ bsp_t *BSP_LoadModel(cluster_t *cluster, char *gamedir, char *bspname) int numnodes, i; int numleafs; + unsigned int chksum; bsp_t *bsp; @@ -237,29 +242,45 @@ bsp_t *BSP_LoadModel(cluster_t *cluster, char *gamedir, char *bspname) numleafs = header->lumps[LUMP_LEAFS].filelen/sizeof(dleaf_t); bsp = malloc(sizeof(bsp_t) + sizeof(node_t)*numnodes + header->lumps[LUMP_VISIBILITY].filelen + sizeof(unsigned char *)*numleafs); - bsp->nodes = (node_t*)(bsp+1); - bsp->pvsofs = (unsigned char**)(bsp->nodes+numnodes); - bsp->pvslump = (unsigned char*)(bsp->pvsofs+numleafs); - - bsp->pvsbytecount = (numleafs+7)/8; - - for (i = 0; i < numnodes; i++) + if (bsp) { - bsp->nodes[i].child[0] = nodes[i].children[0]; - bsp->nodes[i].child[1] = nodes[i].children[1]; - bsp->nodes[i].planedist = planes[nodes[i].planenum].dist; - bsp->nodes[i].planen[0] = planes[nodes[i].planenum].normal[0]; - bsp->nodes[i].planen[1] = planes[nodes[i].planenum].normal[1]; - bsp->nodes[i].planen[2] = planes[nodes[i].planenum].normal[2]; - } - memcpy(bsp->pvslump, data+header->lumps[LUMP_VISIBILITY].fileofs, header->lumps[LUMP_VISIBILITY].filelen); + bsp->fullchecksum = 0; + bsp->visedchecksum = 0; + for (i = 0; i < HEADER_LUMPS; i++) + { + if (i == LUMP_ENTITIES) + continue; //entities never appear in any checksums - for (i = 0; i < numleafs; i++) - { - if (leaf[i].visofs < 0) - bsp->pvsofs[i] = NULL; - else - bsp->pvsofs[i] = bsp->pvslump+leaf[i].visofs; + chksum = Com_BlockChecksum(data + header->lumps[i].fileofs, header->lumps[i].filelen); + bsp->fullchecksum ^= chksum; + if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES) + continue; + bsp->visedchecksum ^= chksum; + } + bsp->nodes = (node_t*)(bsp+1); + bsp->pvsofs = (unsigned char**)(bsp->nodes+numnodes); + bsp->pvslump = (unsigned char*)(bsp->pvsofs+numleafs); + + bsp->pvsbytecount = (numleafs+7)/8; + + for (i = 0; i < numnodes; i++) + { + bsp->nodes[i].child[0] = nodes[i].children[0]; + bsp->nodes[i].child[1] = nodes[i].children[1]; + bsp->nodes[i].planedist = planes[nodes[i].planenum].dist; + bsp->nodes[i].planen[0] = planes[nodes[i].planenum].normal[0]; + bsp->nodes[i].planen[1] = planes[nodes[i].planenum].normal[1]; + bsp->nodes[i].planen[2] = planes[nodes[i].planenum].normal[2]; + } + memcpy(bsp->pvslump, data+header->lumps[LUMP_VISIBILITY].fileofs, header->lumps[LUMP_VISIBILITY].filelen); + + for (i = 0; i < numleafs; i++) + { + if (leaf[i].visofs < 0) + bsp->pvsofs[i] = NULL; + else + bsp->pvsofs[i] = bsp->pvslump+leaf[i].visofs; + } } free(data); @@ -314,6 +335,13 @@ int BSP_SphereLeafNums_r(bsp_t *bsp, int first, int maxleafs, unsigned short *li return numleafs; } +unsigned int BSP_Checksum(bsp_t *bsp) +{ + if (!bsp) + return 0; + return bsp->visedchecksum; +} + int BSP_SphereLeafNums(bsp_t *bsp, int maxleafs, unsigned short *list, float x, float y, float z, float radius) { float pos[3]; diff --git a/fteqtv/mdfour.c b/fteqtv/mdfour.c new file mode 100644 index 000000000..c838a5016 --- /dev/null +++ b/fteqtv/mdfour.c @@ -0,0 +1,254 @@ +/* + mdfour.c + + An implementation of MD4 designed for use in the samba SMB + authentication protocol + + Copyright (C) 1997-1998 Andrew Tridgell + + 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 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#include /* XoXus: needed for memset call */ + + + + +#ifndef _MDFOUR_H +#define _MDFOUR_H + +#ifndef int32 +#define int32 int +#endif + +#if SIZEOF_INT > 4 +#define LARGE_INT32 +#endif + +#ifndef uint32 +#define uint32 unsigned int32 +#endif + +struct mdfour { + uint32 A, B, C, D; + uint32 totalN; +}; + +void mdfour_begin(struct mdfour *md); // old: MD4Init +void mdfour_update(struct mdfour *md, unsigned char *in, int n); //old: MD4Update +void mdfour_result(struct mdfour *md, unsigned char *out); // old: MD4Final +void mdfour(unsigned char *out, unsigned char *in, int n); + +#endif // _MDFOUR_H + + + +/* NOTE: This code makes no attempt to be fast! + + It assumes that a int is at least 32 bits long +*/ + +static struct mdfour *m; + +#define F(X,Y,Z) (((X)&(Y)) | ((~(X))&(Z))) +#define G(X,Y,Z) (((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z))) +#define H(X,Y,Z) ((X)^(Y)^(Z)) +#ifdef LARGE_INT32 +#define lshift(x,s) ((((x)<<(s))&0xFFFFFFFF) | (((x)>>(32-(s)))&0xFFFFFFFF)) +#else +#define lshift(x,s) (((x)<<(s)) | ((x)>>(32-(s)))) +#endif + +#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s) +#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + 0x5A827999,s) +#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + 0x6ED9EBA1,s) + +/* this applies md4 to 64 byte chunks */ +static void mdfour64(uint32 *M) +{ + int j; + uint32 AA, BB, CC, DD; + uint32 X[16]; + uint32 A,B,C,D; + + for (j=0;j<16;j++) + X[j] = M[j]; + + A = m->A; B = m->B; C = m->C; D = m->D; + AA = A; BB = B; CC = C; DD = D; + + ROUND1(A,B,C,D, 0, 3); ROUND1(D,A,B,C, 1, 7); + ROUND1(C,D,A,B, 2, 11); ROUND1(B,C,D,A, 3, 19); + ROUND1(A,B,C,D, 4, 3); ROUND1(D,A,B,C, 5, 7); + ROUND1(C,D,A,B, 6, 11); ROUND1(B,C,D,A, 7, 19); + ROUND1(A,B,C,D, 8, 3); ROUND1(D,A,B,C, 9, 7); + ROUND1(C,D,A,B, 10, 11); ROUND1(B,C,D,A, 11, 19); + ROUND1(A,B,C,D, 12, 3); ROUND1(D,A,B,C, 13, 7); + ROUND1(C,D,A,B, 14, 11); ROUND1(B,C,D,A, 15, 19); + + ROUND2(A,B,C,D, 0, 3); ROUND2(D,A,B,C, 4, 5); + ROUND2(C,D,A,B, 8, 9); ROUND2(B,C,D,A, 12, 13); + ROUND2(A,B,C,D, 1, 3); ROUND2(D,A,B,C, 5, 5); + ROUND2(C,D,A,B, 9, 9); ROUND2(B,C,D,A, 13, 13); + ROUND2(A,B,C,D, 2, 3); ROUND2(D,A,B,C, 6, 5); + ROUND2(C,D,A,B, 10, 9); ROUND2(B,C,D,A, 14, 13); + ROUND2(A,B,C,D, 3, 3); ROUND2(D,A,B,C, 7, 5); + ROUND2(C,D,A,B, 11, 9); ROUND2(B,C,D,A, 15, 13); + + ROUND3(A,B,C,D, 0, 3); ROUND3(D,A,B,C, 8, 9); + ROUND3(C,D,A,B, 4, 11); ROUND3(B,C,D,A, 12, 15); + ROUND3(A,B,C,D, 2, 3); ROUND3(D,A,B,C, 10, 9); + ROUND3(C,D,A,B, 6, 11); ROUND3(B,C,D,A, 14, 15); + ROUND3(A,B,C,D, 1, 3); ROUND3(D,A,B,C, 9, 9); + ROUND3(C,D,A,B, 5, 11); ROUND3(B,C,D,A, 13, 15); + ROUND3(A,B,C,D, 3, 3); ROUND3(D,A,B,C, 11, 9); + ROUND3(C,D,A,B, 7, 11); ROUND3(B,C,D,A, 15, 15); + + A += AA; B += BB; C += CC; D += DD; + +#ifdef LARGE_INT32 + A &= 0xFFFFFFFF; B &= 0xFFFFFFFF; + C &= 0xFFFFFFFF; D &= 0xFFFFFFFF; +#endif + + for (j=0;j<16;j++) + X[j] = 0; + + m->A = A; m->B = B; m->C = C; m->D = D; +} + +static void copy64(uint32 *M, unsigned char *in) +{ + int i; + + for (i=0;i<16;i++) + M[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) | + (in[i*4+1]<<8) | (in[i*4+0]<<0); +} + +static void copy4(unsigned char *out,uint32 x) +{ + out[0] = x&0xFF; + out[1] = (x>>8)&0xFF; + out[2] = (x>>16)&0xFF; + out[3] = (x>>24)&0xFF; +} + +void mdfour_begin(struct mdfour *md) +{ + md->A = 0x67452301; + md->B = 0xefcdab89; + md->C = 0x98badcfe; + md->D = 0x10325476; + md->totalN = 0; +} + + +static void mdfour_tail(unsigned char *in, int n) +{ + unsigned char buf[128]; + uint32 M[16]; + uint32 b; + + m->totalN += n; + + b = m->totalN * 8; + + memset(buf, 0, 128); + if (n) memcpy(buf, in, n); + buf[n] = 0x80; + + if (n <= 55) { + copy4(buf+56, b); + copy64(M, buf); + mdfour64(M); + } else { + copy4(buf+120, b); + copy64(M, buf); + mdfour64(M); + copy64(M, buf+64); + mdfour64(M); + } +} + +void mdfour_update(struct mdfour *md, unsigned char *in, int n) +{ + uint32 M[16]; + + m = md; + +// if (n == 0) mdfour_tail(in, n); //Spike: This is where the bug was. + + while (n >= 64) { + copy64(M, in); + mdfour64(M); + in += 64; + n -= 64; + m->totalN += 64; + } + + mdfour_tail(in, n); +} + + +void mdfour_result(struct mdfour *md, unsigned char *out) +{ + m = md; + + copy4(out, m->A); + copy4(out+4, m->B); + copy4(out+8, m->C); + copy4(out+12, m->D); +} + + +void mdfour(unsigned char *out, unsigned char *in, int n) +{ + struct mdfour md; + mdfour_begin(&md); + mdfour_update(&md, in, n); + mdfour_result(&md, out); +} + +/////////////////////////////////////////////////////////////// +// MD4-based checksum utility functions +// +// Copyright (C) 2000 Jeff Teunissen +// +// Author: Jeff Teunissen +// Date: 01 Jan 2000 + +unsigned Com_BlockChecksum (void *buffer, int length) +{ + int digest[4]; + unsigned val; + + mdfour ( (unsigned char *) digest, (unsigned char *) buffer, length ); + + val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3]; + + return val; +} + +void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf) +{ + mdfour ( outbuf, (unsigned char *) buffer, len ); +} diff --git a/fteqtv/netchan.c b/fteqtv/netchan.c index 61fcea81a..261de6f0b 100644 --- a/fteqtv/netchan.c +++ b/fteqtv/netchan.c @@ -39,8 +39,7 @@ int Netchan_IsLocal (netadr_t adr) { struct sockaddr_in *sadr = (struct sockaddr_in *)adr; unsigned char *bytes; - bytes = (char *)&sadr->sin_addr; - + bytes = (unsigned char *)&sadr->sin_addr; if (bytes[0] == 127 && bytes[1] == 0 && bytes[2] == 0 && @@ -175,13 +174,14 @@ Netchan_Setup called to open a channel to a remote system ============== */ -void Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport) +void Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport, qboolean isclient) { memset (chan, 0, sizeof(*chan)); chan->sock = sock; memcpy(&chan->remote_address, adr, sizeof(netadr_t)); chan->qport = qport; + chan->isclient = isclient; chan->last_received = curtime; @@ -284,8 +284,8 @@ void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const un WriteLong (&send, w2); // send the qport if we are a client -// if (chan->sock == NS_CLIENT) -// MSG_WriteShort (&send, chan->qport); + if (chan->isclient) + WriteShort (&send, chan->qport); // copy the reliable message to the packet first if (send_reliable) @@ -343,7 +343,7 @@ qboolean Netchan_Process (netchan_t *chan, netmsg_t *msg) sequence_ack = ReadLong (msg); // read the qport if we are a server -// if (chan->sock == NS_SERVER) + if (!chan->isclient) ReadShort (msg); reliable_message = sequence >> 31; diff --git a/fteqtv/parse.c b/fteqtv/parse.c index 11dc5cbeb..563f44f8e 100644 --- a/fteqtv/parse.c +++ b/fteqtv/parse.c @@ -216,11 +216,17 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma tv->parsingconnectiondata = true; - ReadLong(m); //we don't care about server's servercount, it's all reliable data anyway. + tv->clservercount = ReadLong(m); //we don't care about server's servercount, it's all reliable data anyway. ReadString(m, tv->gamedir, sizeof(tv->gamedir)); - /*tv->servertime =*/ ReadFloat(m); + if (tv->usequkeworldprotocols) + tv->thisplayer = ReadByte(m)&~128; + else + { + tv->thisplayer = MAX_CLIENTS-1; + /*tv->servertime =*/ ReadFloat(m); + } ReadString(m, tv->mapname, sizeof(tv->mapname)); Sys_Printf(tv->cluster, "Gamedir: %s\n", tv->gamedir); @@ -254,6 +260,13 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma memset(tv->staticsound, 0, sizeof(tv->staticsound)); memset(tv->players, 0, sizeof(tv->players)); + + + + if (tv->usequkeworldprotocols) + { + SendClientCommand(tv, "soundlist %i 0\n", tv->clservercount); + } } static void ParseCDTrack(sv_t *tv, netmsg_t *m, int to, unsigned int mask) @@ -271,6 +284,7 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) qboolean fromproxy; ReadString(m, text, sizeof(text)); + Sys_Printf(tv->cluster, "stuffcmd: %s", text); if (!strcmp(text, "skins\n")) { @@ -285,6 +299,9 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) SendBufferToViewer(v, newcmd, sizeof(newcmd), true); } } + + if (tv->usequkeworldprotocols) + SendClientCommand(tv, "begin %i\n", tv->clservercount); return; } else if (!strncmp(text, "fullserverinfo ", 15)) @@ -325,7 +342,11 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) return; } else if (!strncmp(text, "cmd ", 4)) + { + if (tv->usequkeworldprotocols) + SendClientCommand(tv, "%s", text+4); return; //commands the game server asked for are pointless. + } Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); } @@ -500,8 +521,10 @@ void ParseSpawnStatic(sv_t *tv, netmsg_t *m, int to, unsigned int mask) Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); } +extern const usercmd_t nullcmd; static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers) { + usercmd_t nonnullcmd; int flags; int num; int i; @@ -515,50 +538,100 @@ static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers) } } - - num = ReadByte(m); if (num >= MAX_CLIENTS) { num = 0; // don't be fatal. Sys_Printf(tv->cluster, "Too many svc_playerinfos, wrapping\n"); } - tv->players[num].old = tv->players[num].current; - flags = ReadShort(m); - tv->players[num].gibbed = !!(flags & DF_GIB); - tv->players[num].dead = !!(flags & DF_DEAD); - tv->players[num].current.frame = ReadByte(m); - - for (i = 0; i < 3; i++) + if (tv->usequkeworldprotocols) { - if (flags & (DF_ORIGIN << i)) - tv->players[num].current.origin[i] = ReadShort (m); - } + tv->players[num].old = tv->players[num].current; + flags = (unsigned short)ReadShort (m); - for (i = 0; i < 3; i++) - { - if (flags & (DF_ANGLES << i)) + tv->players[num].current.origin[0] = ReadShort (m); + tv->players[num].current.origin[1] = ReadShort (m); + tv->players[num].current.origin[2] = ReadShort (m); + + tv->players[num].current.frame = ReadByte(m); + + if (flags & PF_MSEC) + ReadByte (m); + + if (flags & PF_COMMAND) { - tv->players[num].current.angles[i] = ReadShort(m); + ReadDeltaUsercmd(m, &nullcmd, &nonnullcmd); + tv->players[num].current.angles[0] = nonnullcmd.angles[0]; + tv->players[num].current.angles[1] = nonnullcmd.angles[1]; + tv->players[num].current.angles[2] = nonnullcmd.angles[2]; } + + for (i=0 ; i<3 ; i++) + { + if (flags & (PF_VELOCITY1<players[num].current.modelindex = ReadByte (m); + else + tv->players[num].current.modelindex = tv->modelindex_player; + + if (flags & PF_SKINNUM) + tv->players[num].current.skinnum = ReadByte (m); + else + tv->players[num].current.skinnum = 0; + + if (flags & PF_EFFECTS) + tv->players[num].current.effects = ReadByte (m); + else + tv->players[num].current.effects = 0; + + if (flags & PF_WEAPONFRAME) + tv->players[num].current.weaponframe = ReadByte (m); + else + tv->players[num].current.weaponframe = 0; + + tv->players[num].active = (num != tv->thisplayer); } + else + { + flags = ReadShort(m); + tv->players[num].gibbed = !!(flags & DF_GIB); + tv->players[num].dead = !!(flags & DF_DEAD); + tv->players[num].current.frame = ReadByte(m); - if (flags & DF_MODEL) - tv->players[num].current.modelindex = ReadByte (m); + for (i = 0; i < 3; i++) + { + if (flags & (DF_ORIGIN << i)) + tv->players[num].current.origin[i] = ReadShort (m); + } - if (flags & DF_SKINNUM) - tv->players[num].current.skinnum = ReadByte (m); + for (i = 0; i < 3; i++) + { + if (flags & (DF_ANGLES << i)) + { + tv->players[num].current.angles[i] = ReadShort(m); + } + } - if (flags & DF_EFFECTS) - tv->players[num].current.effects = ReadByte (m); + if (flags & DF_MODEL) + tv->players[num].current.modelindex = ReadByte (m); - if (flags & DF_WEAPONFRAME) - tv->players[num].current.weaponframe = ReadByte (m); + if (flags & DF_SKINNUM) + tv->players[num].current.skinnum = ReadByte (m); - tv->players[num].active = true; + if (flags & DF_EFFECTS) + tv->players[num].current.effects = ReadByte (m); + if (flags & DF_WEAPONFRAME) + tv->players[num].current.weaponframe = ReadByte (m); + + tv->players[num].active = true; + + } tv->players[num].leafcount = BSP_SphereLeafNums(tv->bsp, MAX_ENTITY_LEAFS, tv->players[num].leafs, tv->players[num].current.origin[0]/8.0f, @@ -671,8 +744,10 @@ static void ParseUpdatePing(sv_t *tv, netmsg_t *m, int to, unsigned int mask) static void ParseUpdateFrags(sv_t *tv, netmsg_t *m, int to, unsigned int mask) { + int best; int pnum; int frags; + char buffer[64]; pnum = ReadByte(m); frags = ReadShort(m); @@ -682,6 +757,26 @@ static void ParseUpdateFrags(sv_t *tv, netmsg_t *m, int to, unsigned int mask) Sys_Printf(tv->cluster, "svc_updatefrags: invalid player number\n"); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); + + if (!tv->usequkeworldprotocols) + return; + + frags = -10000; + best = -1; + for (pnum = 0; pnum < MAX_CLIENTS; pnum++) + { + if (*tv->players[pnum].userinfo && !atoi(Info_ValueForKey(tv->players[pnum].userinfo, "*spectator", buffer, sizeof(buffer)))) + if (frags < tv->players[pnum].frags) + { + best = pnum; + frags = tv->players[pnum].frags; + } + } + if (best != tv->trackplayer) + { + SendClientCommand (tv, "ptrack %i\n", best); + tv->trackplayer = best; + } } static void ParseUpdateStat(sv_t *tv, netmsg_t *m, int to, unsigned int mask) @@ -729,7 +824,7 @@ static void ParseUpdateStatLong(sv_t *tv, netmsg_t *m, int to, unsigned int mask Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); } -static void ParseUpdateUserinfo(sv_t *tv, netmsg_t *m) +static void ParseUpdateUserinfo(sv_t *tv, netmsg_t *m, int to, unsigned int mask) { int pnum; pnum = ReadByte(m); @@ -743,6 +838,8 @@ static void ParseUpdateUserinfo(sv_t *tv, netmsg_t *m) { } } + + Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); } static void ParsePacketloss(sv_t *tv, netmsg_t *m, int to, unsigned int mask) @@ -957,6 +1054,7 @@ void ParseNails2(sv_t *tv, netmsg_t *m) void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) { + int i; netmsg_t buf; qboolean clearoldplayers = true; buf.cursize = length; @@ -1100,7 +1198,7 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) break; case svc_updateuserinfo: - ParseUpdateUserinfo(tv, &buf); + ParseUpdateUserinfo(tv, &buf, to, mask); break; //#define svc_download 41 // [short] size [size bytes] @@ -1113,9 +1211,10 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) ReadByte(&buf); break; case svc_modellist: - if (!ParseList(tv, &buf, tv->modellist, to, mask)) + i = ParseList(tv, &buf, tv->modellist, to, mask); + if (!i) { - int i; + int j; if (tv->bsp) BSP_Free(tv->bsp); @@ -1125,16 +1224,30 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) tv->bsp = BSP_LoadModel(tv->cluster, tv->gamedir, tv->modellist[1].name); tv->numinlines = 0; - for (i = 2; i < 256; i++) + for (j = 2; j < 256; j++) { - if (*tv->modellist[i].name != '*') + if (*tv->modellist[j].name != '*') break; - tv->numinlines = i; + tv->numinlines = j; } } + if (tv->usequkeworldprotocols) + { + if (i) + SendClientCommand(tv, "modellist %i %i\n", tv->clservercount, i); + else + SendClientCommand(tv, "prespawn %i 0 %i\n", tv->clservercount, BSP_Checksum(tv->bsp)); + } break; case svc_soundlist: - ParseList(tv, &buf, tv->soundlist, to, mask); + i = ParseList(tv, &buf, tv->soundlist, to, mask); + if (tv->usequkeworldprotocols) + { + if (i) + SendClientCommand(tv, "soundlist %i %i\n", tv->clservercount, i); + else + SendClientCommand(tv, "modellist %i 0\n", tv->clservercount); + } break; case svc_packetentities: FlushPacketEntities(tv); @@ -1145,7 +1258,9 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) ParsePacketEntities(tv, &buf); break; //#define svc_maxspeed 49 // maxspeed change, for prediction -//#define svc_entgravity 50 // gravity change, for prediction +case svc_entgravity: // gravity change, for prediction +ReadFloat(&buf); +break; case svc_setinfo: ParseSetInfo(tv, &buf); break; diff --git a/fteqtv/qtv.h b/fteqtv/qtv.h index 54fecb9bc..7cc1b6afa 100644 --- a/fteqtv/qtv.h +++ b/fteqtv/qtv.h @@ -164,6 +164,7 @@ typedef struct { int reliable_length; qboolean drop; + qboolean isclient; netmsg_t message; char message_buf[MAX_MSGLEN]; //reliable message being built @@ -301,8 +302,17 @@ typedef struct { unsigned short leafs[MAX_ENTITY_LEAFS]; } entity_t; +typedef struct { + unsigned char msec; + unsigned short angles[3]; + short forwardmove, sidemove, upmove; + unsigned char buttons; + unsigned char impulse; +} usercmd_t; + struct sv_s { netadr_t serveraddress; + netchan_t netchan; unsigned char buffer[MAX_PROXY_BUFFER]; //this doesn't cycle. int buffersize; //it memmoves down @@ -339,12 +349,24 @@ struct sv_s { filename_t modellist[MAX_MODELS]; filename_t soundlist[MAX_SOUNDS]; int modelindex_spike; // qw is wierd. + int modelindex_player; // qw is wierd. + + qboolean usequkeworldprotocols; + int challenge; + unsigned short qport; + int isconnected; + int clservercount; + unsigned int nextsendpings; + unsigned int trackplayer; + int thisplayer; + unsigned timeout; FILE *file; unsigned int filelength; SOCKET sourcesock; SOCKET listenmvd; //tcp + mvd protocol + int tcplistenportnum; oproxy_t *proxies; int numproxies; @@ -355,9 +377,13 @@ struct sv_s { unsigned int curtime; unsigned int oldpackettime; unsigned int nextpackettime; - unsigned int nextconnectattemp; + unsigned int nextconnectattempt; + + + + qboolean disconnectwhennooneiswatching; + unsigned int numviewers; - int tcplistenportnum; cluster_t *cluster; @@ -499,7 +525,7 @@ void ReadString(netmsg_t *b, char *string, int maxlen); #define svc_packetentities 47 // [...] #define svc_deltapacketentities 48 // [...] //#define svc_maxspeed 49 // maxspeed change, for prediction -//#define svc_entgravity 50 // gravity change, for prediction +#define svc_entgravity 50 // gravity change, for prediction #define svc_setinfo 51 // setinfo on a client #define svc_serverinfo 52 // serverinfo #define svc_updatepl 53 // [qbyte] [qbyte] @@ -596,7 +622,7 @@ void SendBufferToViewer(viewer_t *v, const char *buffer, int length, qboolean re void QW_PrintfToViewer(viewer_t *v, char *format, ...); void QW_StuffcmdToViewer(viewer_t *v, char *format, ...); -void Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport); +void Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport, qboolean isclient); void Netchan_OutOfBandPrint (cluster_t *cluster, SOCKET sock, netadr_t adr, char *format, ...); int Netchan_IsLocal (netadr_t adr); void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, char *data, netadr_t adr); @@ -621,6 +647,6 @@ void Info_SetValueForStarKey (char *s, const char *key, const char *value, int m void Sys_Printf(cluster_t *cluster, char *fmt, ...); qboolean Net_FileProxy(sv_t *qtv, char *filename); -sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, qboolean force); +sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, qboolean force, qboolean autoclose); SOCKET Net_MVDListen(int port); qboolean Net_StopFileProxy(sv_t *qtv); diff --git a/fteqtv/qw.c b/fteqtv/qw.c index 9888c2f2f..4ffda3be7 100644 --- a/fteqtv/qw.c +++ b/fteqtv/qw.c @@ -20,14 +20,20 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "qtv.h" +#ifdef _WIN32 +int snprintf(char *buffer, int buffersize, char *format, ...) +{ + va_list argptr; + int ret; + va_start (argptr, format); + ret = _vsnprintf (buf, buffersize, format, argptr); + buf[buffersize - 1] = '\0'; + va_end (argptr); + + return ret; +} +#endif -typedef struct { - unsigned char msec; - unsigned short angles[3]; - short forwardmove, sidemove, upmove; - unsigned char buttons; - unsigned char impulse; -} usercmd_t; const usercmd_t nullcmd; #define CM_ANGLE1 (1<<0) @@ -417,6 +423,20 @@ int SendList(sv_t *qtv, int first, const filename_t *list, int svc, netmsg_t *ms return i; } +void QW_SetViewersServer(viewer_t *viewer, sv_t *sv) +{ + if (viewer->server) + viewer->server->numviewers--; + viewer->server = sv; + if (viewer->server) + viewer->server->numviewers++; + QW_StuffcmdToViewer(viewer, "cmd new\n"); + viewer->servercount++; + viewer->origin[0] = 0; + viewer->origin[1] = 0; + viewer->origin[2] = 0; +} + //fixme: will these want to have state?.. int NewChallenge(netadr_t *addr) { @@ -431,6 +451,7 @@ qboolean ChallengePasses(netadr_t *addr, int challenge) void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage) { + sv_t *initialserver; viewer_t *viewer; char qport[32]; @@ -451,23 +472,33 @@ void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage) viewer = malloc(sizeof(viewer_t)); + if (!viewer) + { + Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *addr, "n" "Out of memory"); + return; + } memset(viewer, 0, sizeof(viewer_t)); viewer->trackplayer = -1; - Netchan_Setup (cluster->qwdsocket, &viewer->netchan, *addr, atoi(qport)); + Netchan_Setup (cluster->qwdsocket, &viewer->netchan, *addr, atoi(qport), false); viewer->next = cluster->viewers; cluster->viewers = viewer; viewer->delta_frame = -1; + initialserver = NULL; if (cluster->numservers == 1) { - viewer->server = cluster->servers; - if (!viewer->server->modellist[1].name[0]) - viewer->server = NULL; //damn, that server isn't ready + initialserver = cluster->servers; + if (!initialserver->modellist[1].name[0]) + initialserver = NULL; //damn, that server isn't ready } - if (!viewer->server) + viewer->server = initialserver; + if (viewer->server) + viewer->server->numviewers++; + + if (!initialserver) viewer->menunum = 1; cluster->numviewers++; @@ -1078,17 +1109,11 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message) } else if (!strcmp(v->expectcommand, "addserver")) { -#ifdef _WIN32 - _snprintf(buf, sizeof(buf), "tcp:%s", message); - buf[suzeof(buf)-1] = '\0'; -#else snprintf(buf, sizeof(buf), "tcp:%s", message); -#endif - qtv = QTV_NewServerConnection(cluster, buf, false); + qtv = QTV_NewServerConnection(cluster, buf, false, false); if (qtv) { - v->server = qtv; - QW_StuffcmdToViewer(v, "cmd new\n"); + QW_SetViewersServer(v, qtv); QW_PrintfToViewer(v, "Connected\n", message); } else @@ -1110,22 +1135,46 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message) } else if (!strcmp(v->expectcommand, "adddemo")) { -#ifdef _WIN32 - _snprintf(buf, sizeof(buf), "file:%s", message); - buf[sizeof(buf)-1] = '\0'; -#else snprintf(buf, sizeof(buf), "file:%s", message); -#endif - qtv = QTV_NewServerConnection(cluster, buf, false); + qtv = QTV_NewServerConnection(cluster, buf, false, false); if (!qtv) QW_PrintfToViewer(v, "Failed to play demo \"%s\"\n", message); else { - v->server = qtv; - QW_StuffcmdToViewer(v, "cmd new\n"); + QW_SetViewersServer(v, qtv); QW_PrintfToViewer(v, "Opened demo file.\n", message); } } + else if (!strcmp(v->expectcommand, "setmvdport")) + { + int newp; + int news; + if (qtv) + { + newp = atoi(message); + + if (newp) + { + news = Net_MVDListen(newp); + + if (news != INVALID_SOCKET) + { + if (qtv->listenmvd != INVALID_SOCKET) + closesocket(qtv->listenmvd); + qtv->listenmvd = news; + qtv->tcplistenportnum = newp; + qtv->disconnectwhennooneiswatching = false; + } + } + else if (qtv->listenmvd != INVALID_SOCKET) + { + closesocket(qtv->listenmvd); + qtv->listenmvd = INVALID_SOCKET; + } + } + else + QW_PrintfToViewer(v, "You were disconnected from that stream\n"); + } else { QW_PrintfToViewer(v, "Command %s was not recognised\n", v->expectcommand); @@ -1134,18 +1183,68 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message) *v->expectcommand = '\0'; return; } - *v->expectcommand = '\0'; + if (!strncmp(message, ".qw ", 4)) + { + message += 4; + snprintf(buf, sizeof(buf), "udp:%s", message); + qtv = QTV_NewServerConnection(cluster, buf, false, false); + if (qtv) + { +// QW_SetViewersServer(v, qtv); + QW_PrintfToViewer(v, "Connected\n", message); + } + else + QW_PrintfToViewer(v, "Failed to connect to server \"%s\", connection aborted\n", message); + } + else if (!strncmp(message, ".connect ", 9)) + { + message += 9; + snprintf(buf, sizeof(buf), "tcp:%s", message); + qtv = QTV_NewServerConnection(cluster, buf, false, true); + if (qtv) + { + QW_SetViewersServer(v, qtv); + QW_PrintfToViewer(v, "Connected\n", message); + } + else + QW_PrintfToViewer(v, "Failed to connect to server \"%s\", connection aborted\n", message); + } + else if (!strncmp(message, ".demo ", 6)) + { + message += 6; + snprintf(buf, sizeof(buf), "file:%s", message); + qtv = QTV_NewServerConnection(cluster, buf, false, true); + if (qtv) + { + QW_SetViewersServer(v, qtv); + QW_PrintfToViewer(v, "Connected\n", message); + } + else + QW_PrintfToViewer(v, "Failed to connect to server \"%s\", connection aborted\n", message); + } + else if (!strncmp(message, ".disconnect", 11)) + { + QW_SetViewersServer(v, NULL); + QW_PrintfToViewer(v, "Connected\n", message); + } + else + { + if (cluster->notalking) + return; - InitNetMsg(&msg, buf, sizeof(buf)); + *v->expectcommand = '\0'; - WriteByte(&msg, svc_print); - WriteByte(&msg, 3); //PRINT_CHAT - WriteString2(&msg, v->name); - WriteString2(&msg, "\x8d "); - WriteString2(&msg, message); - WriteString(&msg, "\n"); + InitNetMsg(&msg, buf, sizeof(buf)); - Broadcast(cluster, msg.data, msg.cursize); + WriteByte(&msg, svc_print); + WriteByte(&msg, 3); //PRINT_CHAT + WriteString2(&msg, v->name); + WriteString2(&msg, "\x8d "); + WriteString2(&msg, message); + WriteString(&msg, "\n"); + + Broadcast(cluster, msg.data, msg.cursize); + } } viewer_t *QW_IsOn(cluster_t *cluster, char *name) @@ -1230,7 +1329,12 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) // printf("stringcmd: %s\n", buf); if (!strcmp(buf, "new")) - SendServerData(qtv, v); + { + if (qtv->parsingconnectiondata) + QW_StuffcmdToViewer(v, "cmd new\n"); + else + SendServerData(qtv, v); + } else if (!strncmp(buf, "modellist ", 10)) { char *cmd = buf+10; @@ -1357,9 +1461,9 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) else if (!strncmp(buf, "pings", 5)) { } - else if (!strncmp(buf, "say \"", 5) && !cluster->notalking) + else if (!strncmp(buf, "say \"", 5)) QTV_Say(cluster, qtv, v, buf+5); - else if (!strncmp(buf, "say ", 4) && !cluster->notalking) + else if (!strncmp(buf, "say ", 4)) QTV_Say(cluster, qtv, v, buf+4); else if (!strncmp(buf, "servers", 7)) @@ -1368,8 +1472,7 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) } else if (!strncmp(buf, "reset", 5)) { - QW_StuffcmdToViewer(v, "cmd new\n"); - v->server = NULL; + QW_SetViewersServer(v, NULL); v->menunum = 1; } else if (!strncmp(buf, "admin", 5)) @@ -1514,10 +1617,12 @@ void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum) if (viewer->server) { i = 0; + sv = viewer->server; if (i++ == viewer->menuop) { //mvd port - QW_StuffcmdToViewer(viewer, "echo You will need to reconnect\n"); - cluster->qwlistenportnum += buttonnum?-1:1; + WriteString(&m, "echo Please enter a new tcp port number\nmessagemode\n"); + SendBufferToViewer(viewer, m.data, m.cursize, true); + strcpy(viewer->expectcommand, "setmvdport"); } if (i++ == viewer->menuop) { //disconnect @@ -1534,8 +1639,9 @@ void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum) case 1: if (!cluster->servers) { - WriteString(&m, "messagemode\n"); + WriteString(&m, "echo Please enter a server ip\nmessagemode\n"); SendBufferToViewer(viewer, m.data, m.cursize, true); + strcpy(viewer->expectcommand, "insecadddemo"); } else { @@ -1560,9 +1666,7 @@ void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum) } else { - WriteString(&m, "cmd new\n"); - viewer->servercount++; - viewer->server = sv; + QW_SetViewersServer(viewer, sv); SendBufferToViewer(viewer, m.data, m.cursize, true); viewer->menunum = 0; viewer->thinksitsconnected = false; @@ -1675,7 +1779,10 @@ void Menu_Draw(cluster_t *cluster, viewer_t *viewer) i = 0; WriteString2(&m, " port"); WriteString2(&m, (viewer->menuop==(i++))?" \r ":" : "); - sprintf(str, "%-20i", sv->tcplistenportnum); + if (sv->listenmvd == INVALID_SOCKET) + sprintf(str, "!%-19i", sv->tcplistenportnum); + else + sprintf(str, "%-20i", sv->tcplistenportnum); WriteString2(&m, str); WriteString2(&m, "\n"); @@ -1845,6 +1952,9 @@ void QW_FreeViewer(cluster_t *cluster, viewer_t *viewer) free(viewer->backbuf[i].data); } + if (viewer->server) + viewer->server->numviewers--; + free(viewer); cluster->numviewers--; diff --git a/fteqtv/rcon.c b/fteqtv/rcon.c index 652327f08..3bbf9edcc 100644 --- a/fteqtv/rcon.c +++ b/fteqtv/rcon.c @@ -347,7 +347,19 @@ 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)) + if (!QTV_NewServerConnection(cluster, arg[1], false, !strcmp(arg[0], "connect"))) + return "Failed to connect to server, connection aborted\n"; + return "Connection registered\n"; + } + else if (!strcmp(arg[0], "qw")) + { + 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], false, false)) return "Failed to connect to server, connection aborted\n"; return "Connection registered\n"; } @@ -363,7 +375,7 @@ char *Cluster_Rcon_Dispatch(cluster_t *cluster, char *arg[MAX_ARGS], char *buffe memmove(arg[1]+5, arg[1], ARG_LEN-6); strncpy(arg[1], "file:", 5); - if (!QTV_NewServerConnection(cluster, arg[1], false)) + if (!QTV_NewServerConnection(cluster, arg[1], false, false)) return "Failed to connect to server, connection aborted\n"; return "Connection registered\n"; } @@ -558,7 +570,7 @@ char *Server_Rcon_Dispatch(sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int siz else if (!strcmp(arg[0], "disconnect")) { QTV_Shutdown(qtv); - return NULL; + return "Disconnected\n"; } else if (!strcmp(arg[0], "file") || !strcmp(arg[0], "play") || !strcmp(arg[0], "playdemo")) @@ -630,8 +642,10 @@ char *Server_Rcon_Dispatch(sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int siz if (news != INVALID_SOCKET) { - closesocket(qtv->listenmvd); + if (qtv->listenmvd != INVALID_SOCKET) + closesocket(qtv->listenmvd); qtv->listenmvd = news; + qtv->disconnectwhennooneiswatching = false; qtv->tcplistenportnum = newp; return "Opened tcp port\n"; } diff --git a/fteqtv/source.c b/fteqtv/source.c index 8657487ef..901a5ad14 100644 --- a/fteqtv/source.c +++ b/fteqtv/source.c @@ -21,6 +21,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "qtv.h" #define RECONNECT_TIME (1000*30) +#define UDPRECONNECT_TIME (1000) +#define PINGSINTERVAL_TIME (1000*5) +#define UDPTIMEOUT_LENGTH (1000*20) qboolean NET_StringToAddr (char *s, netadr_t *sadr) @@ -202,34 +205,11 @@ SOCKET Net_MVDListen(int port) return sock; } -qboolean Net_ConnectToServer(sv_t *qtv, char *ip) +qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip) { netadr_t from; unsigned long nonblocking = true; - if (!strncmp(ip, "file:", 5)) - { - qtv->sourcesock = INVALID_SOCKET; - qtv->file = fopen(ip+5, "rb"); - if (qtv->file) - { - fseek(qtv->file, 0, SEEK_END); - qtv->filelength = ftell(qtv->file); - fseek(qtv->file, 0, SEEK_SET); - return true; - } - Sys_Printf(qtv->cluster, "Unable to open file %s\n", ip+5); - return false; - } - - qtv->nextconnectattemp = qtv->curtime + RECONNECT_TIME; //wait half a minuite before trying to reconnect - - if (strncmp(ip, "tcp:", 4)) - { - Sys_Printf(qtv->cluster, "Unknown source type %s\n", ip); - return false; - } - if (!NET_StringToAddr(ip+4, &qtv->serveraddress)) { Sys_Printf(qtv->cluster, "Unable to resolve %s\n", ip); @@ -264,10 +244,78 @@ qboolean Net_ConnectToServer(sv_t *qtv, char *ip) return false; } } + return true; +} +qboolean Net_ConnectToUDPServer(sv_t *qtv, char *ip) +{ + netadr_t from; + unsigned long nonblocking = true; + + if (!NET_StringToAddr(ip+4, &qtv->serveraddress)) + { + Sys_Printf(qtv->cluster, "Unable to resolve %s\n", ip); + return false; + } + qtv->sourcesock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (qtv->sourcesock == INVALID_SOCKET) + return false; + + memset(&from, 0, sizeof(from)); + ((struct sockaddr*)&from)->sa_family = ((struct sockaddr*)&qtv->serveraddress)->sa_family; + if (bind(qtv->sourcesock, (struct sockaddr *)&from, sizeof(from)) == -1) + { + closesocket(qtv->sourcesock); + qtv->sourcesock = INVALID_SOCKET; + return false; + } + + if (ioctlsocket (qtv->sourcesock, FIONBIO, &nonblocking) == -1) + { + closesocket(qtv->sourcesock); + qtv->sourcesock = INVALID_SOCKET; + return false; + } + + qtv->qport = Sys_Milliseconds()*1000+Sys_Milliseconds(); return true; } +qboolean Net_ConnectToServer(sv_t *qtv, char *ip) +{ + qtv->usequkeworldprotocols = false; + + if (!strncmp(ip, "file:", 5)) + { + qtv->sourcesock = INVALID_SOCKET; + qtv->file = fopen(ip+5, "rb"); + if (qtv->file) + { + fseek(qtv->file, 0, SEEK_END); + qtv->filelength = ftell(qtv->file); + fseek(qtv->file, 0, SEEK_SET); + return true; + } + Sys_Printf(qtv->cluster, "Unable to open file %s\n", ip+5); + return false; + } + + qtv->nextconnectattempt = qtv->curtime + RECONNECT_TIME; //wait half a minuite before trying to reconnect + + if (!strncmp(ip, "udp:", 4)) + { + qtv->usequkeworldprotocols = true; + return Net_ConnectToUDPServer(qtv, ip); + } + else if (!strncmp(ip, "tcp:", 4)) + return Net_ConnectToTCPServer(qtv, ip); + else + { + Sys_Printf(qtv->cluster, "Unknown source type %s\n", ip); + return false; + } +} + void Net_FindProxies(sv_t *qtv) { oproxy_t *prox; @@ -286,6 +334,11 @@ void Net_FindProxies(sv_t *qtv) } prox = malloc(sizeof(*prox)); + if (!prox) + {//out of mem? + closesocket(sock); + return; + } memset(prox, 0, sizeof(*prox)); prox->flushing = true; //allow the buffer overflow resumption code to send the connection info. prox->sock = sock; @@ -309,6 +362,8 @@ qboolean Net_FileProxy(sv_t *qtv, char *filename) //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 false; memset(prox, 0, sizeof(*prox)); prox->flushing = true; //allow the buffer overflow resumption code to send the connection info. prox->sock = INVALID_SOCKET; @@ -865,8 +920,8 @@ qboolean QTV_Connect(sv_t *qtv, char *serverurl) void QTV_Shutdown(sv_t *qtv) { - return; -/* + oproxy_t *prox; + oproxy_t *old; viewer_t *v; sv_t *peer; cluster_t *cluster; @@ -888,6 +943,8 @@ void QTV_Shutdown(sv_t *qtv) fclose(qtv->file); qtv->file = NULL; } + if (qtv->listenmvd != INVALID_SOCKET) + closesocket(qtv->listenmvd); BSP_Free(qtv->bsp); qtv->bsp = NULL; @@ -910,13 +967,25 @@ void QTV_Shutdown(sv_t *qtv) { if (v->server == qtv) { - v->server = NULL; + QW_SetViewersServer(v, NULL); v->menunum = 1; } } + for (prox = qtv->proxies; prox; ) + { + if (prox->file) + fclose(prox->file); + if (prox->sock != INVALID_SOCKET) + closesocket(prox->sock); + old = prox; + prox = prox->next; + free(old); + } + + free(qtv); -*/ + cluster->numservers--; } /* @@ -1199,9 +1268,81 @@ int main(int argc, char **argv) +void SendClientCommand(sv_t *qtv, char *fmt, ...) +{ + va_list argptr; + char buf[1024]; + va_start (argptr, fmt); +#ifdef _WIN32 + _vsnprintf (buf, sizeof(buf) - 1, fmt, argptr); + buf[sizeof(buf) - 1] = '\0'; +#else + vsnprintf (buf, sizeof(buf), fmt, argptr); +#endif // _WIN32 + va_end (argptr); + WriteByte(&qtv->netchan.message, clc_stringcmd); + WriteString(&qtv->netchan.message, buf); +} +void QTV_ParseQWStream(sv_t *qtv) +{ + char buffer[1500]; + netadr_t from; + int fromlen; + int readlen; + netmsg_t msg; + fromlen = sizeof(from); //bug: this won't work on (free)bsd + + for (;;) + { + readlen = recvfrom(qtv->sourcesock, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&from, &fromlen); + if (readlen < 0) + { + //FIXME: Check for error + break; + } + buffer[readlen] = 0; + if (*(int*)buffer == -1) + { + if (buffer[4] == 'c') + { //got a challenge + qtv->challenge = atoi(buffer+5); + sprintf(buffer, "connect %i %i %i \"%s\"", 28, qtv->qport, qtv->challenge, "\\*ver\\fteqtv\\name\\fteqtv\\spectator\\1"); + Netchan_OutOfBand(qtv->cluster, qtv->sourcesock, qtv->serveraddress, strlen(buffer), buffer); + continue; + } + if (buffer[4] == 'n') + { + Sys_Printf(qtv->cluster, "%s: %s", qtv->server, buffer+5); + continue; + } + if (buffer[4] == 'j') + { + Netchan_Setup(qtv->sourcesock, &qtv->netchan, qtv->serveraddress, qtv->qport, true); + + qtv->trackplayer = -1; + + qtv->isconnected = true; + qtv->timeout = qtv->curtime + UDPTIMEOUT_LENGTH; + SendClientCommand(qtv, "new\n"); + Sys_Printf(qtv->cluster, "Connected!\n"); + continue; + } + Sys_Printf(qtv->cluster, "%s: unrecognised connectionless packet:\n%s\n", qtv->server, buffer+4); + continue; + } + memset(&msg, 0, sizeof(msg)); + msg.cursize = readlen; + msg.data = buffer; + msg.maxsize = readlen; + qtv->timeout = qtv->curtime + UDPTIMEOUT_LENGTH; + if (!Netchan_Process(&qtv->netchan, &msg)) + continue; + ParseMessage(qtv, msg.data + msg.readpos, msg.cursize - msg.readpos, dem_all, -1); + } +} void QTV_Run(sv_t *qtv) @@ -1212,6 +1353,12 @@ void QTV_Run(sv_t *qtv) int oldcurtime; int packettime; + if (qtv->disconnectwhennooneiswatching && qtv->numviewers == 0) + { + QTV_Shutdown(qtv); + return; + } + //we will read out as many packets as we can until we're up to date //note: this can cause real issues when we're overloaded for any length of time @@ -1235,9 +1382,51 @@ void QTV_Run(sv_t *qtv) } + + + if (qtv->usequkeworldprotocols) + { + if (!qtv->isconnected && (qtv->curtime >= qtv->nextconnectattempt || qtv->curtime < qtv->nextconnectattempt - UDPRECONNECT_TIME*2)) + { + Netchan_OutOfBand(qtv->cluster, qtv->sourcesock, qtv->serveraddress, 12, "getchallenge"); + qtv->nextconnectattempt = qtv->curtime + UDPRECONNECT_TIME; + } + QTV_ParseQWStream(qtv); + + if (qtv->isconnected) + { + char buffer[64]; + netmsg_t msg; + memset(&msg, 0, sizeof(msg)); + msg.data = buffer; + msg.maxsize = sizeof(buffer); + + if (qtv->curtime >= qtv->timeout || qtv->curtime < qtv->timeout - UDPTIMEOUT_LENGTH*2) + { + Sys_Printf(qtv->cluster, "Timeout\n"); + qtv->isconnected = false; + return; + } + + if (qtv->curtime >= qtv->nextsendpings || qtv->curtime < qtv->nextsendpings - PINGSINTERVAL_TIME*2) + { + qtv->nextsendpings = qtv->curtime + PINGSINTERVAL_TIME; + SendClientCommand(qtv, "pings\n"); + } + WriteByte(&msg, clc_tmove); + WriteShort(&msg, qtv->players[qtv->trackplayer].current.origin[0]); + WriteShort(&msg, qtv->players[qtv->trackplayer].current.origin[1]); + WriteShort(&msg, qtv->players[qtv->trackplayer].current.origin[2]); + + Netchan_Transmit(qtv->cluster, &qtv->netchan, msg.cursize, msg.data); + } + return; + } + + if (qtv->sourcesock == INVALID_SOCKET && !qtv->file) { - if (qtv->curtime >= qtv->nextconnectattemp || qtv->curtime < qtv->nextconnectattemp - RECONNECT_TIME*2) + if (qtv->curtime >= qtv->nextconnectattempt || qtv->curtime < qtv->nextconnectattempt - RECONNECT_TIME*2) if (!QTV_Connect(qtv, qtv->server)) { return; @@ -1380,7 +1569,7 @@ void QTV_Run(sv_t *qtv) void Cluster_Run(cluster_t *cluster) { - sv_t *sv; + sv_t *sv, *old; int m; struct timeval timeout; @@ -1394,17 +1583,17 @@ void Cluster_Run(cluster_t *cluster) if (cluster->qwdsocket >= m) m = cluster->qwdsocket+1; } -/* + for (sv = cluster->servers; sv; sv = sv->next) { - if (sv->sourcesock != INVALID_SOCKET) + 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 @@ -1491,17 +1680,21 @@ void Cluster_Run(cluster_t *cluster) - for (sv = cluster->servers; sv; sv = sv->next) + for (sv = cluster->servers; sv; ) { - QTV_Run(sv); + old = sv; + sv = sv->next; + QTV_Run(old); } QW_UpdateUDPStuff(cluster); } -sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, qboolean force) +sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, qboolean force, qboolean autoclose) { sv_t *qtv = malloc(sizeof(sv_t)); + if (!qtv) + return NULL; memset(qtv, 0, sizeof(*qtv)); //set up a default config @@ -1510,6 +1703,8 @@ sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, qboolean force) qtv->listenmvd = INVALID_SOCKET; qtv->sourcesock = INVALID_SOCKET; + qtv->disconnectwhennooneiswatching = autoclose; + qtv->parsingconnectiondata = true; qtv->cluster = cluster; qtv->next = cluster->servers;