From 90d5e5d598312367d1e02999650b88ff261603bd Mon Sep 17 00:00:00 2001 From: Spoike Date: Sat, 24 Sep 2005 04:48:20 +0000 Subject: [PATCH] Changed a few things, rcon is in, scripts are different, pvs culling works, some things are fixed. Can use a master server, can change server whilst running, can supply it's own hostname, is queryable, made choking smoothable. I wonder how many bugs I've added. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@1355 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- fteqtv/Makefile | 10 +- fteqtv/netchan.c | 25 +++- fteqtv/parse.c | 218 +++++++++++++++++++++++----- fteqtv/qtv.h | 164 +++++++++++++++++---- fteqtv/qw.c | 366 +++++++++++++++++++++++++++++++++++++++------- fteqtv/source.c | 369 +++++++++++++++++++++++++++++++++++------------ 6 files changed, 929 insertions(+), 223 deletions(-) diff --git a/fteqtv/Makefile b/fteqtv/Makefile index 1ffb15ab2..a29480131 100644 --- a/fteqtv/Makefile +++ b/fteqtv/Makefile @@ -1,13 +1,13 @@ -CC=gcc +EXTRACFLAGS=-Wall -O2 +CC=gcc $(EXTRACFLAGS) STRIP=strip -CFLAGS=-Wall -O2 STRIPFLAGS=--strip-unneeded --remove-section=.comment -OBJS = netchan.o parse.o qw.o source.o +OBJS = netchan.o parse.o qw.o source.o bsp.o rcon.o -qtv: $(OBJS) - $(CC) $(CFLAGS) $(OBJS) -o $@.db -lm +qtv: $(OBJS) qtv.h + $(CC) $(CFLAGS) $(OBJS) -o $@.db -lm $(LDFLAGS) $(STRIP) $(STRIPFLAGS) $@.db -o $@ clean: diff --git a/fteqtv/netchan.c b/fteqtv/netchan.c index 4cfc8c3ef..99d9634a6 100644 --- a/fteqtv/netchan.c +++ b/fteqtv/netchan.c @@ -1,3 +1,23 @@ +/* +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. +*/ + + #include "qtv.h" #define curtime Sys_Milliseconds() @@ -157,7 +177,7 @@ void Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport) chan->message.allowoverflow = true; - chan->rate = 1.0/2500; + chan->rate = 1.0f/2500; } @@ -205,13 +225,12 @@ transmition / retransmition of the reliable messages. A 0 length will still generate a packet and deal with the reliable messages. ================ */ -void Netchan_Transmit (netchan_t *chan, int length, unsigned char *data) +void Netchan_Transmit (netchan_t *chan, int length, const unsigned char *data) { netmsg_t send; unsigned char send_buf[MAX_MSGLEN + PACKET_HEADER]; qboolean send_reliable; unsigned w1, w2; - int i; // check for message overflow if (chan->message.overflowed) diff --git a/fteqtv/parse.c b/fteqtv/parse.c index 4470cd703..331846f3f 100644 --- a/fteqtv/parse.c +++ b/fteqtv/parse.c @@ -1,3 +1,23 @@ +/* +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. +*/ + + #include "qtv.h" #define ParseError(m) (m)->cursize = (m)->cursize+1 // @@ -229,6 +249,9 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma memset(tv->soundlist, 0, sizeof(tv->soundlist)); memset(tv->lightstyle, 0, sizeof(tv->lightstyle)); tv->staticsound_count = 0; + memset(tv->staticsound, 0, sizeof(tv->staticsound)); + + memset(tv->players, 0, sizeof(tv->players)); } static void ParseCDTrack(sv_t *tv, netmsg_t *m, int to, unsigned int mask) @@ -241,6 +264,9 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) { viewer_t *v; char text[1024]; + char value[256]; + qboolean fromproxy; + ReadString(m, text, sizeof(text)); if (!strcmp(text, "skins\n")) @@ -248,6 +274,7 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) const char newcmd[10] = {svc_stufftext, 'c', 'm', 'd', ' ', 'n','e','w','\n','\0'}; tv->servercount++; tv->parsingconnectiondata = false; + for (v = tv->viewers; v; v = v->next) { SendBufferToViewer(v, newcmd, sizeof(newcmd), true); @@ -256,7 +283,39 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) } else if (!strncmp(text, "fullserverinfo ", 15)) { - strncpy(tv->serverinfo, text+15, sizeof(tv->serverinfo)-1); + text[strlen(text)-1] = '\0'; + text[strlen(text)-1] = '\0'; + + //copy over the server's serverinfo + strncpy(tv->serverinfo, text+16, sizeof(tv->serverinfo)-1); + + Info_ValueForKey(tv->serverinfo, "*qtv", value, sizeof(value)); + if (*value) + fromproxy = true; + else + fromproxy = false; + + //add on our extra infos + Info_SetValueForStarKey(tv->serverinfo, "*qtv", VERSION, sizeof(tv->serverinfo)); + Info_SetValueForStarKey(tv->serverinfo, "*z_ext", Z_EXT_STRING, sizeof(tv->serverinfo)); + + //change the hostname (the qtv's hostname with the server's hostname in brackets) + Info_ValueForKey(tv->serverinfo, "hostname", value, sizeof(value)); + if (fromproxy && strchr(value, '(') && value[strlen(value)-1] == ')') //already has brackets + { //the fromproxy check is because it's fairly common to find a qw server with brackets after it's name. + char *s; + s = strchr(value, '('); //so strip the parent proxy's hostname, and put our hostname first, leaving the origional server's hostname within the brackets + _snprintf(text, sizeof(text), "%s %s", tv->hostname, s); + } + else + { + if (tv->file) + _snprintf(text, sizeof(text), "%s (recorded from: %s)", tv->hostname, value); + else + _snprintf(text, sizeof(text), "%s (live: %s)", tv->hostname, value); + } + Info_SetValueForStarKey(tv->serverinfo, "hostname", text, sizeof(tv->serverinfo)); + return; } else if (!strncmp(text, "cmd ", 4)) @@ -296,7 +355,7 @@ static void ParseCenterprint(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 ParseList(sv_t *tv, netmsg_t *m, filename_t *list, int to, unsigned int mask) +static int ParseList(sv_t *tv, netmsg_t *m, filename_t *list, int to, unsigned int mask) { int first; @@ -304,17 +363,31 @@ static void ParseList(sv_t *tv, netmsg_t *m, filename_t *list, int to, unsigned for (; first < MAX_LIST; first++) { ReadString(m, list[first].name, sizeof(list[first].name)); - printf("read %i: %s\n", first, list[first].name); +// printf("read %i: %s\n", first, list[first].name); if (!*list[first].name) break; // printf("%i: %s\n", first, list[first].name); } - ReadByte(m); //wasted, we don't echo + return ReadByte(m); +} + +static void ParseEntityState(entity_state_t *es, netmsg_t *m) //for baselines/static entities +{ + int i; + + es->modelindex = ReadByte(m); + es->frame = ReadByte(m); + es->colormap = ReadByte(m); + es->skinnum = ReadByte(m); + for (i = 0; i < 3; i++) + { + es->origin[i] = ReadShort(m); + es->angles[i] = ReadByte(m); + } } static void ParseBaseline(sv_t *tv, netmsg_t *m, int to, unsigned int mask) { - int i; unsigned int entnum; entnum = ReadShort(m); if (entnum >= MAX_ENTITIES) @@ -322,23 +395,15 @@ static void ParseBaseline(sv_t *tv, netmsg_t *m, int to, unsigned int mask) ParseError(m); return; } - tv->baseline[entnum].modelindex = ReadByte(m); - tv->baseline[entnum].frame = ReadByte(m); - tv->baseline[entnum].colormap = ReadByte(m); - tv->baseline[entnum].skinnum = ReadByte(m); - for (i = 0; i < 3; i++) - { - tv->baseline[entnum].origin[i] = ReadShort(m); - tv->baseline[entnum].angles[i] = ReadByte(m); - } + ParseEntityState(&tv->entity[entnum].baseline, m); } static void ParseStaticSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask) { if (tv->staticsound_count == MAX_STATICSOUNDS) { - tv->staticsound_count = 0; // don't be fatal. - printf("Too many static sounds, wrapping\n"); + tv->staticsound_count--; // don't be fatal. + printf("Too many static sounds\n"); } tv->staticsound[tv->staticsound_count].origin[0] = ReadShort(m); @@ -351,6 +416,33 @@ static void ParseStaticSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask) tv->staticsound_count++; Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); } + +static void ParseIntermission(sv_t *tv, netmsg_t *m, int to, unsigned int mask) +{ + ReadShort(m); + ReadShort(m); + ReadShort(m); + ReadByte(m); + ReadByte(m); + ReadByte(m); + + Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); +} + +void ParseSpawnStatic(sv_t *tv, netmsg_t *m, int to, unsigned int mask) +{ + if (tv->spawnstatic_count == MAX_STATICENTITIES) + { + tv->spawnstatic_count--; // don't be fatal. + printf("Too many static entities\n"); + } + + ParseEntityState(&tv->spawnstatic[tv->spawnstatic_count], m); + + tv->spawnstatic_count++; + Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); +} + static void ParsePlayerInfo(sv_t *tv, netmsg_t *m) { int flags; @@ -397,22 +489,31 @@ static void ParsePlayerInfo(sv_t *tv, netmsg_t *m) 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, + tv->players[num].current.origin[1]/8.0f, + tv->players[num].current.origin[2]/8.0f, 32); } static void FlushPacketEntities(sv_t *tv) { int i; for (i = 0; i < MAX_ENTITIES; i++) - tv->curents[i].modelindex = 0; + tv->entity[i].current.modelindex = 0; } static void ParsePacketEntities(sv_t *tv, netmsg_t *m) { int entnum; int flags; + qboolean forcerelink; viewer_t *v; + tv->physicstime = tv->parsetime; + if (tv->chokeonnotupdated) for (v = tv->viewers; v; v = v->next) { @@ -436,44 +537,56 @@ static void ParsePacketEntities(sv_t *tv, netmsg_t *m) if (tv->maxents < entnum) tv->maxents = entnum; flags &= ~511; - memcpy(&tv->oldents[entnum], &tv->curents[entnum], sizeof(entity_state_t)); //ow. + memcpy(&tv->entity[entnum].old, &tv->entity[entnum].current, sizeof(entity_state_t)); //ow. if (flags & U_REMOVE) { - tv->curents[entnum].modelindex = 0; + tv->entity[entnum].current.modelindex = 0; continue; } - if (!tv->curents[entnum].modelindex) //lerp from baseline - memcpy(&tv->curents[entnum], &tv->baseline[entnum], sizeof(entity_state_t)); + if (!tv->entity[entnum].current.modelindex) //lerp from baseline + { + memcpy(&tv->entity[entnum].current, &tv->entity[entnum].baseline, sizeof(entity_state_t)); + forcerelink = true; + } + else + forcerelink = false; if (flags & U_MOREBITS) flags |= ReadByte(m); if (flags & U_MODEL) - tv->curents[entnum].modelindex = ReadByte(m); + tv->entity[entnum].current.modelindex = ReadByte(m); if (flags & U_FRAME) - tv->curents[entnum].frame = ReadByte(m); + tv->entity[entnum].current.frame = ReadByte(m); if (flags & U_COLORMAP) - tv->curents[entnum].colormap = ReadByte(m); + tv->entity[entnum].current.colormap = ReadByte(m); if (flags & U_SKIN) - tv->curents[entnum].skinnum = ReadByte(m); + tv->entity[entnum].current.skinnum = ReadByte(m); if (flags & U_EFFECTS) - tv->curents[entnum].effects = ReadByte(m); + tv->entity[entnum].current.effects = ReadByte(m); if (flags & U_ORIGIN1) - tv->curents[entnum].origin[0] = ReadShort(m); + tv->entity[entnum].current.origin[0] = ReadShort(m); if (flags & U_ANGLE1) - tv->curents[entnum].angles[0] = ReadByte(m); + tv->entity[entnum].current.angles[0] = ReadByte(m); if (flags & U_ORIGIN2) - tv->curents[entnum].origin[1] = ReadShort(m); + tv->entity[entnum].current.origin[1] = ReadShort(m); if (flags & U_ANGLE2) - tv->curents[entnum].angles[1] = ReadByte(m); + tv->entity[entnum].current.angles[1] = ReadByte(m); if (flags & U_ORIGIN3) - tv->curents[entnum].origin[2] = ReadShort(m); + tv->entity[entnum].current.origin[2] = ReadShort(m); if (flags & U_ANGLE3) - tv->curents[entnum].angles[2] = ReadByte(m); + tv->entity[entnum].current.angles[2] = ReadByte(m); - tv->entupdatetime[entnum] = tv->curtime; - if (!tv->oldents[entnum].modelindex) //no old state - memcpy(&tv->oldents[entnum], &tv->curents[entnum], sizeof(entity_state_t)); //copy the new to the old, so we don't end up with interpolation glitches + tv->entity[entnum].updatetime = tv->curtime; + if (!tv->entity[entnum].old.modelindex) //no old state + memcpy(&tv->entity[entnum].old, &tv->entity[entnum].current, sizeof(entity_state_t)); //copy the new to the old, so we don't end up with interpolation glitches + + + if ((flags & (U_ORIGIN1 | U_ORIGIN2 | U_ORIGIN3)) || forcerelink) + tv->entity[entnum].leafcount = BSP_SphereLeafNums(tv->bsp, MAX_ENTITY_LEAFS, tv->entity[entnum].leafs, + tv->entity[entnum].current.origin[0]/8.0f, + tv->entity[entnum].current.origin[1]/8.0f, + tv->entity[entnum].current.origin[2]/8.0f, 32); } } @@ -755,7 +868,10 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) printf("nop\n"); break; -//#define svc_disconnect 2 + case svc_disconnect: + QTV_Connect(tv, tv->server); //reconnect (this is probably a demo, and reconnecting like this will make it loop) + tv->buffersize = 0; //flush it + break; case svc_updatestat: ParseUpdateStat(tv, &buf, to, mask); @@ -802,7 +918,10 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) ParseDamage(tv, &buf, to, mask); break; -//#define svc_spawnstatic 20 + case svc_spawnstatic: + ParseSpawnStatic(tv, &buf, to, mask); + break; + //#define svc_spawnstatic2 21 case svc_spawnbaseline: ParseBaseline(tv, &buf, to, mask); @@ -826,7 +945,10 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) ParseStaticSound(tv, &buf, to, mask); break; -//#define svc_intermission 30 // [vec3_t] origin [vec3_t] angle + case svc_intermission: + ParseIntermission(tv, &buf, to, mask); + break; + //#define svc_finale 31 // [string] text case svc_cdtrack: @@ -871,7 +993,25 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) ReadByte(&buf); break; case svc_modellist: - ParseList(tv, &buf, tv->modellist, to, mask); + if (!ParseList(tv, &buf, tv->modellist, to, mask)) + { + int i; + if (tv->bsp) + BSP_Free(tv->bsp); + + if (tv->nobsp) + tv->bsp = NULL; + else + tv->bsp = BSP_LoadModel(tv->gamedir, tv->modellist[1].name); + + tv->numinlines = 0; + for (i = 2; i < 256; i++) + { + if (*tv->modellist[i].name != '*') + break; + tv->numinlines = i; + } + } break; case svc_soundlist: ParseList(tv, &buf, tv->soundlist, to, mask); diff --git a/fteqtv/qtv.h b/fteqtv/qtv.h index 409aebeaf..d2028fce3 100644 --- a/fteqtv/qtv.h +++ b/fteqtv/qtv.h @@ -1,3 +1,24 @@ +/* +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. +*/ + + + //each server that we are connected to has it's own state. //it should be easy enough to use one thread per server. @@ -7,17 +28,22 @@ //this means that when a new proxy connects, we have to send initial state as well as a chunk of pending state, expect to need to send new data before the proxy even has all the init stuff. We may need to raise MAX_PROXY_BUFFER to be larger than on the server #ifdef _WIN32 + #include #include #pragma comment (lib, "wsock32.lib") #define qerrno WSAGetLastError() #define EWOULDBLOCK WSAEWOULDBLOCK - #ifndef _DEBUG - #define static //it breaks my symbol lookups. :( + #ifdef _MSC_VER + //okay, so warnings are here to help... they're ugly though. + #pragma warning(disable: 4761) //integral size mismatch in argument + #pragma warning(disable: 4244) //conversion from float to short + #pragma warning(disable: 4018) //signed/unsigned mismatch #endif #elif defined(__CYGWIN__) + #include #include #include #include @@ -25,19 +51,14 @@ #include #include #include - - #ifndef SOCKET - #define SOCKET int - #endif - #ifndef INVALID_SOCKET - #define INVALID_SOCKET -1 - #endif - #define qerrno errno + #include + #include #define ioctlsocket ioctl #define closesocket close #elif defined(linux) + #include #include #include #include @@ -48,14 +69,6 @@ #include #include - #ifndef SOCKET - #define SOCKET int - #endif - #ifndef INVALID_SOCKET - #define INVALID_SOCKET -1 - #endif - #define qerrno errno - #define ioctlsocket ioctl #define closesocket close #else @@ -63,7 +76,25 @@ //try the cygwin ones #endif +#ifndef pgetaddrinfo + #ifndef _WIN32 + #define pgetaddrinfo getaddrinfo + #define pfreeaddrinfo freeaddrinfo + #endif +#endif +#ifndef SOCKET + #define SOCKET int +#endif +#ifndef INVALID_SOCKET + #define INVALID_SOCKET -1 +#endif +#ifndef qerrno + #define qerrno errno +#endif + + #include +#include #define VERSION "0.01" //this will be added to the serverinfo @@ -79,15 +110,23 @@ #define MAX_SOUNDS MAX_LIST #define MAX_ENTITIES 512 #define MAX_STATICSOUNDS 64 +#define MAX_STATICENTITIES 128 #define MAX_LIGHTSTYLES 64 +#define DEFAULT_HOSTNAME "FTEQTV" #define MAX_PROXY_BUFFER (1<<14) //must be power-of-two -#define PREFERED_PROXY_BUFFER 1500 //the ammount of data we try to leave in our input buffer (must be large enough to contain any single mvd frame) +#define PREFERED_PROXY_BUFFER 8192 //the ammount of data we try to leave in our input buffer (must be large enough to contain any single mvd frame) - -#define ENTS_PER_FRAME 512 //max number of entities per frame. +#define MAX_ENTITY_LEAFS 32 +#define ENTS_PER_FRAME 64 //max number of entities per frame (OUCH!). #define ENTITY_FRAMES 64 //number of frames to remember for deltaing + +#define Z_EXT_SERVERTIME (1<<3) // STAT_TIME +#define Z_EXT_STRING "8" +#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. + typedef enum {false, true} qboolean; typedef unsigned char netadr_t[64]; @@ -162,6 +201,9 @@ typedef struct { int frags; float entertime; + int leafcount; + unsigned short leafs[MAX_ENTITY_LEAFS]; + qboolean active:1; qboolean gibbed:1; qboolean dead:1; @@ -194,13 +236,21 @@ typedef struct viewer_s { struct viewer_s *next; + char name[32]; + + + int settime; //the time that we last told the client. + float origin[3]; } viewer_t; typedef struct oproxy_s { qboolean flushing; qboolean drop; + + FILE *file; SOCKET sock; + unsigned char buffer[MAX_PROXY_BUFFER]; unsigned int buffersize; //use cyclic buffering. unsigned int bufferpos; @@ -214,6 +264,18 @@ typedef struct { unsigned char attenuation; } staticsound_t; +typedef struct bsp_s bsp_t; + +typedef struct { + entity_state_t baseline; + entity_state_t current; + entity_state_t old; + unsigned int updatetime; //to stop lerping when it's an old entity (bodies, stationary grenades, ...) + + int leafcount; + unsigned short leafs[MAX_ENTITY_LEAFS]; +} entity_t; + typedef struct sv_s { netadr_t serveraddress; @@ -238,13 +300,12 @@ typedef struct sv_s { float friction; } movevars; int cdtrack; - entity_state_t baseline[MAX_ENTITIES]; - entity_state_t curents[MAX_ENTITIES]; - entity_state_t oldents[MAX_ENTITIES]; - unsigned int entupdatetime[MAX_ENTITIES]; //to stop lerping when it's an old entity (bodies, stationary grenades, ...) + entity_t entity[MAX_ENTITIES]; int maxents; staticsound_t staticsound[MAX_STATICSOUNDS]; int staticsound_count; + entity_state_t spawnstatic[MAX_STATICENTITIES]; + int spawnstatic_count; filename_t lightstyle[MAX_LIGHTSTYLES]; char serverinfo[MAX_SERVERINFO_STRING]; @@ -266,17 +327,32 @@ typedef struct sv_s { qboolean parsingconnectiondata; //so reject any new connects for now + unsigned int physicstime; //the last time all the ents moved. unsigned int curtime; unsigned int oldpackettime; unsigned int nextpackettime; int tcplistenportnum; int qwlistenportnum; - char server[MAX_QPATH]; + unsigned int mastersendtime; + unsigned int mastersequence; + + char commandinput[512]; + int inputlength; + + + bsp_t *bsp; + int numinlines; //options: qboolean chokeonnotupdated; qboolean lateforward; + qboolean notalking; + char password[256]; + char hostname[256]; + char server[MAX_QPATH]; + char master[MAX_QPATH]; + qboolean nobsp; } sv_t; typedef struct { @@ -315,7 +391,7 @@ void ReadString(netmsg_t *b, char *string, int maxlen); #define svc_bad 0 #define svc_nop 1 -//#define svc_disconnect 2 +#define svc_disconnect 2 #define svc_updatestat 3 // [qbyte] [qbyte] //#define svc_version 4 // [long] server version //#define svc_setview 5 // [short] entity number @@ -336,7 +412,7 @@ void ReadString(netmsg_t *b, char *string, int maxlen); //#define svc_particle 18 // [vec3] #define svc_damage 19 -//#define svc_spawnstatic 20 +#define svc_spawnstatic 20 //#define svc_spawnstatic2 21 #define svc_spawnbaseline 22 @@ -351,7 +427,7 @@ void ReadString(netmsg_t *b, char *string, int maxlen); #define svc_spawnstaticsound 29 // [coord3] [qbyte] samp [qbyte] vol [qbyte] aten -//#define svc_intermission 30 // [vec3_t] origin [vec3_t] angle +#define svc_intermission 30 // [vec3_t] origin [vec3_t] angle //#define svc_finale 31 // [string] text #define svc_cdtrack 32 // [qbyte] track @@ -397,7 +473,7 @@ void ReadString(netmsg_t *b, char *string, int maxlen); #define dem_read 1 #define dem_set 2 #define dem_multiple 3 -#define dem_single 4 +#define dem_single 4 #define dem_stats 5 #define dem_all 6 @@ -463,7 +539,35 @@ 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 ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask); void BuildServerData(sv_t *tv, netmsg_t *msg, qboolean mvd); SOCKET QW_InitUDPSocket(int port); void QW_UpdateUDPStuff(sv_t *qtv); +unsigned int Sys_Milliseconds(void); +void Prox_SendInitialEnts(sv_t *qtv, oproxy_t *prox, netmsg_t *msg); +qboolean QTV_Connect(sv_t *qtv, char *serverurl); +qboolean NET_StringToAddr (char *s, netadr_t *sadr); + +void SendBufferToViewer(viewer_t *v, const char *buffer, int length, qboolean reliable); + +void Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport); +void Netchan_OutOfBandPrint (SOCKET sock, netadr_t adr, char *format, ...); +qboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2); +qboolean Netchan_Process (netchan_t *chan, netmsg_t *msg); +void Netchan_Transmit (netchan_t *chan, int length, const unsigned char *data); +int SendList(sv_t *qtv, int first, filename_t *list, int svc, netmsg_t *msg); +int Prespawn(sv_t *qtv, int curmsgsize, netmsg_t *msg, int bufnum); + +bsp_t *BSP_LoadModel(char *gamedir, char *bspname); +void BSP_Free(bsp_t *bsp); +int BSP_LeafNum(bsp_t *bsp, float x, float y, float z); +int BSP_SphereLeafNums(bsp_t *bsp, int maxleafs, unsigned short *list, float x, float y, float z, float radius); +qboolean BSP_Visible(bsp_t *bsp, int leafcount, unsigned short *list); +void BSP_SetupForPosition(bsp_t *bsp, float x, float y, float z); + +char *Rcon_Command(sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qboolean localcommand); +char *COM_ParseToken (char *data, char *out, int outsize, const char *punctuation); +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); + diff --git a/fteqtv/qw.c b/fteqtv/qw.c index 0365cbab5..6601cc316 100644 --- a/fteqtv/qw.c +++ b/fteqtv/qw.c @@ -1,3 +1,23 @@ +/* +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. +*/ + + #include "qtv.h" @@ -178,10 +198,9 @@ void BuildServerData(sv_t *tv, netmsg_t *msg, qboolean mvd) WriteByte(msg, svc_stufftext); - WriteString2(msg, "fullserverinfo "); - WriteString2(msg, "\\*qtv\\" VERSION); + WriteString2(msg, "fullserverinfo \""); WriteString2(msg, tv->serverinfo); - WriteString(msg, "\n"); + WriteString(msg, "\"\n"); } void SendServerData(sv_t *tv, viewer_t *viewer) @@ -228,9 +247,21 @@ int SendCurrentUserinfos(sv_t *tv, int cursize, netmsg_t *msg, int i) return i; } +void WriteEntityState(netmsg_t *msg, entity_state_t *es) +{ + int i; + WriteByte(msg, es->modelindex); + WriteByte(msg, es->frame); + WriteByte(msg, es->colormap); + WriteByte(msg, es->skinnum); + for (i = 0; i < 3; i++) + { + WriteShort(msg, es->origin[i]); + WriteByte(msg, es->angles[i]); + } +} int SendCurrentBaselines(sv_t *tv, int cursize, netmsg_t *msg, int i) { - int j; if (i < 0 || i >= MAX_ENTITIES) return i; @@ -243,15 +274,7 @@ int SendCurrentBaselines(sv_t *tv, int cursize, netmsg_t *msg, int i) } WriteByte(msg, svc_spawnbaseline); WriteShort(msg, i); - WriteByte(msg, tv->baseline[i].modelindex); - WriteByte(msg, tv->baseline[i].frame); - WriteByte(msg, tv->baseline[i].colormap); - WriteByte(msg, tv->baseline[i].skinnum); - for (j = 0; j < 3; j++) - { - WriteShort(msg, tv->baseline[i].origin[j]); - WriteByte(msg, tv->baseline[i].angles[j]); - } + WriteEntityState(msg, &tv->entity[i].baseline); } return i; @@ -273,6 +296,51 @@ int SendCurrentLightmaps(sv_t *tv, int cursize, netmsg_t *msg, int i) } return i; } +int SendStaticSounds(sv_t *tv, int cursize, netmsg_t *msg, int i) +{ + if (i < 0 || i >= MAX_STATICSOUNDS) + return i; + + for (; i < MAX_STATICSOUNDS; i++) + { + if (msg->cursize+cursize+16 > 768) + { + return i; + } + if (!tv->staticsound[i].soundindex) + continue; + + WriteByte(msg, svc_spawnstaticsound); + WriteShort(msg, tv->staticsound[i].origin[0]); + WriteShort(msg, tv->staticsound[i].origin[1]); + WriteShort(msg, tv->staticsound[i].origin[2]); + WriteByte(msg, tv->staticsound[i].soundindex); + WriteByte(msg, tv->staticsound[i].volume); + WriteByte(msg, tv->staticsound[i].attenuation); + } + + return i; +} +int SendStaticEntities(sv_t *tv, int cursize, netmsg_t *msg, int i) +{ + if (i < 0 || i >= MAX_STATICENTITIES) + return i; + + for (; i < MAX_STATICENTITIES; i++) + { + if (msg->cursize+cursize+16 > 768) + { + return i; + } + if (!tv->spawnstatic[i].modelindex) + continue; + + WriteByte(msg, svc_spawnstatic); + WriteEntityState(msg, &tv->spawnstatic[i]); + } + + return i; +} int SendList(sv_t *qtv, int first, filename_t *list, int svc, netmsg_t *msg) { @@ -282,7 +350,7 @@ int SendList(sv_t *qtv, int first, filename_t *list, int svc, netmsg_t *msg) WriteByte(msg, first); for (i = first+1; i < 256; i++) { - printf("write %i: %s\n", i, list[i].name); +// printf("write %i: %s\n", i, list[i].name); WriteString(msg, list[i].name); if (!*list[i].name) //fixme: this probably needs testing for where we are close to the limit { //no more @@ -302,22 +370,6 @@ int SendList(sv_t *qtv, int first, filename_t *list, int svc, netmsg_t *msg) return i; } -void NewQWClient(sv_t *qtv, netadr_t *addr, int qport) -{ - viewer_t *viewer; - viewer = malloc(sizeof(viewer_t)); - memset(viewer, 0, sizeof(viewer_t)); - - viewer->trackplayer = -1; - Netchan_Setup (qtv->qwdsocket, &viewer->netchan, *addr, qport); - - viewer->next = qtv->viewers; - qtv->viewers = viewer; - viewer->delta_frame = -1; - - Netchan_OutOfBandPrint(qtv->qwdsocket, *addr, "j"); -} - //fixme: will these want to have state?.. int NewChallenge(netadr_t *addr) { @@ -330,14 +382,98 @@ qboolean ChallengePasses(netadr_t *addr, int challenge) return false; } +void NewQWClient(sv_t *qtv, netadr_t *addr, char *connectmessage) +{ + viewer_t *viewer; + + char qport[32]; + char challenge[32]; + char infostring[256]; + + connectmessage+=11; + + connectmessage = COM_ParseToken(connectmessage, qport, sizeof(qport), ""); + connectmessage = COM_ParseToken(connectmessage, challenge, sizeof(challenge), ""); + connectmessage = COM_ParseToken(connectmessage, infostring, sizeof(infostring), ""); + + if (!ChallengePasses(addr, atoi(challenge))) + { + Netchan_OutOfBandPrint(qtv->qwdsocket, *addr, "n" "Bad challenge"); + return; + } + + + viewer = malloc(sizeof(viewer_t)); + memset(viewer, 0, sizeof(viewer_t)); + + viewer->trackplayer = -1; + Netchan_Setup (qtv->qwdsocket, &viewer->netchan, *addr, atoi(qport)); + + viewer->next = qtv->viewers; + qtv->viewers = viewer; + viewer->delta_frame = -1; + + Info_ValueForKey(infostring, "name", viewer->name, sizeof(viewer->name)); + + Netchan_OutOfBandPrint(qtv->qwdsocket, *addr, "j"); +} + +void QTV_Rcon(sv_t *qtv, char *message, netadr_t *from) +{ + char buffer[8192]; + + char *command; + int passlen; + + while(*message > '\0' && *message <= ' ') + message++; + + command = strchr(message, ' '); + passlen = command-message; + if (passlen != strlen(qtv->password) || strncmp(message, qtv->password, passlen)) + { + Netchan_OutOfBandPrint(qtv->qwdsocket, *from, "n" "Rcon password is incorrect\n"); + return; + } + + Netchan_OutOfBandPrint(qtv->qwdsocket, *from, "n%s", Rcon_Command(qtv, command, buffer, sizeof(buffer), false)); +} +void QTV_Status(sv_t *qtv, netadr_t *from) +{ + char buffer[8192]; + + netmsg_t msg; + char buf[6]; + InitNetMsg(&msg, buffer, sizeof(buffer)); + WriteLong(&msg, -1); + WriteByte(&msg, 'n'); + WriteString2(&msg, qtv->serverinfo); + WriteString(&msg, "\n"); + NET_SendPacket(qtv->qwdsocket, msg.cursize, msg.data, *from); + + + //Netchan_OutOfBandPrint(qtv->qwdsocket, *from, "n%s\n", qtv->serverinfo); +} + + void ConnectionlessPacket(sv_t *qtv, netadr_t *from, netmsg_t *m) { - char buffer[1024]; + char buffer[MAX_MSGLEN]; int i; ReadLong(m); ReadString(m, buffer, sizeof(buffer)); + if (!strncmp(buffer, "rcon ", 5)) + { + QTV_Rcon(qtv, buffer+5, from); + return; + } + if (!strncmp(buffer, "status", 6)) + { + QTV_Status(qtv, from); + return; + } if (!strncmp(buffer, "getchallenge", 12)) { i = NewChallenge(from); @@ -346,11 +482,21 @@ void ConnectionlessPacket(sv_t *qtv, netadr_t *from, netmsg_t *m) } if (!strncmp(buffer, "connect 28 ", 11)) { - NewQWClient(qtv, from, atoi(buffer+11)); + if (qtv->sourcesock == INVALID_SOCKET && !qtv->file) //tell them what's going on instead of expecting them to see a lack of anything happening. + Netchan_OutOfBandPrint(qtv->qwdsocket, *from, "n" "Proxy is not connected to a server\n"); + else if (qtv->parsingconnectiondata) //connecting at this time is a bit silly. + Netchan_OutOfBandPrint(qtv->qwdsocket, *from, "n" "Buffering demo, please try again\n"); + else + NewQWClient(qtv, from, buffer); return; } + if (!strncmp(buffer, "l\n", 2)) + { + printf("Ack\n"); + } } + void SV_WriteDelta(int entnum, const entity_state_t *from, const entity_state_t *to, netmsg_t *msg, qboolean force) { unsigned int i; @@ -474,7 +620,7 @@ void SV_EmitPacketEntities (const sv_t *qtv, const viewer_t *v, const packet_ent if (newnum < oldnum) { // this is a new entity, send it from the baseline - baseline = &qtv->baseline[newnum]; + baseline = &qtv->entity[newnum].baseline; //Con_Printf ("baseline %i\n", newnum); SV_WriteDelta (newnum, baseline, &to->ents[newindex], msg, true); @@ -499,7 +645,7 @@ void Prox_SendInitialEnts(sv_t *qtv, oproxy_t *prox, netmsg_t *msg) int i; WriteByte(msg, svc_packetentities); for (i = 0; i < qtv->maxents; i++) - SV_WriteDelta(i, &nullentstate, &qtv->curents[i], msg, true); + SV_WriteDelta(i, &nullentstate, &qtv->entity[i].current, msg, true); WriteShort(msg, 0); } @@ -525,7 +671,28 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) short interp; float lerp; + if (tv->physicstime != v->settime && tv->chokeonnotupdated) + { + WriteByte(msg, svc_updatestatlong); + WriteByte(msg, STAT_TIME); + WriteLong(msg, v->settime); + + v->settime = tv->physicstime; + } + + memset(&to, 0, sizeof(to)); + + /*if (v->trackplayer>=0) + { + BSP_SetupForPosition(tv->bsp, tv->players[v->trackplayer].current.origin[0]/8.0f, + tv->players[v->trackplayer].current.origin[1]/8.0f, + tv->players[v->trackplayer].current.origin[2]/8.0f); + } + else*/ + { + BSP_SetupForPosition(tv->bsp, v->origin[0], v->origin[1], v->origin[2]); + } lerp = ((tv->curtime - tv->oldpackettime)/1000.0f) / ((tv->nextpackettime - tv->oldpackettime)/1000.0f); if (lerp < 0) @@ -539,6 +706,9 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) if (!tv->players[i].active) continue; + if (v->trackplayer != i && !BSP_Visible(tv->bsp, tv->players[i].leafcount, tv->players[i].leafs)) + continue; + flags = PF_COMMAND; if (v->trackplayer == i && tv->players[i].current.weaponframe) flags |= PF_WEAPONFRAME; @@ -598,17 +768,20 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) e->numents = 0; for (i = 0; i < tv->maxents; i++) { - if (!tv->curents[i].modelindex) + if (!tv->entity[i].current.modelindex) continue; - //FIXME: add interpolation. - e->entnum[e->numents] = i; - memcpy(&e->ents[e->numents], &tv->curents[i], sizeof(entity_state_t)); - if (tv->entupdatetime[i] == tv->oldpackettime) + if (tv->entity[i].current.modelindex >= tv->numinlines && !BSP_Visible(tv->bsp, tv->entity[i].leafcount, tv->entity[i].leafs)) + continue; + + e->entnum[e->numents] = i; + memcpy(&e->ents[e->numents], &tv->entity[i].current, sizeof(entity_state_t)); + + if (tv->entity[i].updatetime == tv->oldpackettime) { - e->ents[e->numents].origin[0] = (lerp)*tv->curents[i].origin[0] + (1-lerp)*tv->oldents[i].origin[0]; - e->ents[e->numents].origin[1] = (lerp)*tv->curents[i].origin[1] + (1-lerp)*tv->oldents[i].origin[1]; - e->ents[e->numents].origin[2] = (lerp)*tv->curents[i].origin[2] + (1-lerp)*tv->oldents[i].origin[2]; + e->ents[e->numents].origin[0] = (lerp)*tv->entity[i].current.origin[0] + (1-lerp)*tv->entity[i].old.origin[0]; + e->ents[e->numents].origin[1] = (lerp)*tv->entity[i].current.origin[1] + (1-lerp)*tv->entity[i].old.origin[1]; + e->ents[e->numents].origin[2] = (lerp)*tv->entity[i].current.origin[2] + (1-lerp)*tv->entity[i].old.origin[2]; } e->numents++; @@ -667,16 +840,29 @@ int Prespawn(sv_t *qtv, int curmsgsize, netmsg_t *msg, int bufnum) ni = SendCurrentUserinfos(qtv, curmsgsize, msg, bufnum); r += ni - bufnum; + bufnum = ni; bufnum -= MAX_CLIENTS; + ni = SendCurrentBaselines(qtv, curmsgsize, msg, bufnum); r += ni - bufnum; bufnum = ni; bufnum -= MAX_ENTITIES; + ni = SendCurrentLightmaps(qtv, curmsgsize, msg, bufnum); r += ni - bufnum; bufnum = ni; bufnum -= MAX_LIGHTSTYLES; + ni = SendStaticSounds(qtv, curmsgsize, msg, bufnum); + r += ni - bufnum; + bufnum = ni; + bufnum -= MAX_STATICSOUNDS; + + ni = SendStaticEntities(qtv, curmsgsize, msg, bufnum); + r += ni - bufnum; + bufnum = ni; + bufnum -= MAX_STATICENTITIES; + if (bufnum == 0) return -1; @@ -721,6 +907,25 @@ void PMove(viewer_t *v, usercmd_t *cmd) v->origin[i] += (cmd->forwardmove*fwd[i] + cmd->sidemove*rgt[i] + cmd->upmove*up[i])*(cmd->msec/1000.0f); } +void QTV_Say(sv_t *qtv, viewer_t *v, char *message) +{ + char buf[1024]; + netmsg_t msg; + + message[strlen(message)-1] = '\0'; + + InitNetMsg(&msg, buf, sizeof(buf)); + + WriteByte(&msg, svc_print); + WriteByte(&msg, 3); //PRINT_CHAT + WriteString2(&msg, v->name); + WriteString2(&msg, "\x8d "); + WriteString2(&msg, message+5); + WriteString(&msg, "\n"); + + Multicast(qtv, msg.data, msg.cursize, dem_all, (unsigned int)-1); +} + void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m) { usercmd_t oldest, oldcmd, newcmd; @@ -740,10 +945,12 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m) break; case clc_stringcmd: ReadString (m, buf, sizeof(buf)); - printf("stringcmd: %s\n", buf); +// printf("stringcmd: %s\n", buf); if (!strcmp(buf, "new")) SendServerData(qtv, v); + else if (!strncmp(buf, "say \"", 5) && !qtv->notalking) + QTV_Say(qtv, v, buf); else if (!strncmp(buf, "modellist ", 10)) { char *cmd = buf+10; @@ -842,6 +1049,48 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m) v->drop = true; else if (!strncmp(buf, "ptrack ", 7)) v->trackplayer = atoi(buf+7); + else if (!strncmp(buf, "ptrack", 6)) + v->trackplayer = -1; + else if (!strncmp(buf, "serverinfo", 5)) + { + char *key, *value, *end; + int len; + netmsg_t m; + InitNetMsg(&m, buf, sizeof(buf)); + WriteByte(&m, svc_print); + WriteByte(&m, 2); + end = qtv->serverinfo; + for(;;) + { + if (!*end) + break; + key = end; + value = strchr(key+1, '\\'); + if (!value) + break; + end = strchr(value+1, '\\'); + if (!end) + end = value+strlen(value); + + len = value-key; + + key++; + while(*key != '\\' && *key) + WriteByte(&m, *key++); + + for (; len < 20; len++) + WriteByte(&m, ' '); + + value++; + while(*value != '\\' && *value) + WriteByte(&m, *value++); + WriteByte(&m, '\n'); + } + WriteByte(&m, 0); + +// WriteString(&m, qtv->serverinfo); + SendBufferToViewer(v, m.data, m.cursize, true); + } else { printf("Client sent unknown string command: %s\n", buf); @@ -858,9 +1107,9 @@ void ParseQWC(sv_t *qtv, viewer_t *v, netmsg_t *m) PMove(v, &newcmd); break; case clc_tmove: - v->origin[0] = ReadShort(m)/8.0f; - v->origin[1] = ReadShort(m)/8.0f; - v->origin[2] = ReadShort(m)/8.0f; + v->origin[0] = ((signed short)ReadShort(m))/8.0f; + v->origin[1] = ((signed short)ReadShort(m))/8.0f; + v->origin[2] = ((signed short)ReadShort(m))/8.0f; break; case clc_upload: @@ -886,8 +1135,20 @@ void QW_UpdateUDPStuff(sv_t *qtv) viewer_t *v, *f; - if (qtv->parsingconnectiondata) - return; //don't accept new qw clients, no sending incompleate data, etc. + if (*qtv->master && (qtv->curtime > qtv->mastersendtime || qtv->mastersendtime > qtv->curtime + 4*1000*60)) //urm... time wrapped? + { + if (NET_StringToAddr(qtv->master, &from)) + { + sprintf(buffer, "a\n%i\n0\n", qtv->mastersequence++); //fill buffer with a heartbeat +//why is there no \xff\xff\xff\xff ?.. + NET_SendPacket(qtv->qwdsocket, 1, "k", from); //ping, just like qw. + NET_SendPacket(qtv->qwdsocket, strlen(buffer), buffer, from); //ping, just like qw. + } + else + printf("Cannot resolve master %s\n", qtv->master); + + qtv->mastersendtime = qtv->curtime + 3*1000*60; //3 minuites. + } m.data = buffer; m.cursize = 0; @@ -898,7 +1159,7 @@ void QW_UpdateUDPStuff(sv_t *qtv) { read = recvfrom(qtv->qwdsocket, buffer, sizeof(buffer), 0, (struct sockaddr*)from, &fromsize); - if (read <= 6) //otherwise it's a runt or bad. + if (read <= 5) //otherwise it's a runt or bad. { if (read < 0) //it's bad. break; @@ -928,11 +1189,10 @@ void QW_UpdateUDPStuff(sv_t *qtv) if (Netchan_Process(&v->netchan, &m)) { v->netchan.outgoing_sequence = v->netchan.incoming_sequence; //compensate for client->server packetloss. - if (!v->chokeme) + if (!v->chokeme || !qtv->chokeonnotupdated) { v->maysend = true; - if (qtv->chokeonnotupdated) - v->chokeme = true; + v->chokeme = qtv->chokeonnotupdated; } ParseQWC(qtv, v, &m); @@ -968,7 +1228,7 @@ void QW_UpdateUDPStuff(sv_t *qtv) free(f); } - if (v->maysend) + if (v->maysend && !qtv->parsingconnectiondata) //don't send incompleate connection data. { v->maysend = false; m.cursize = 0; diff --git a/fteqtv/source.c b/fteqtv/source.c index 994b66aa1..b99effc71 100644 --- a/fteqtv/source.c +++ b/fteqtv/source.c @@ -1,3 +1,23 @@ +/* +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. +*/ + + #include "qtv.h" @@ -32,7 +52,7 @@ qboolean NET_StringToAddr (char *s, netadr_t *sadr) else #endif #ifdef IPPROTO_IPV6 - if (pgetaddrinfo) + if (getaddrinfo) { struct addrinfo *addrinfo, *pos; struct addrinfo udp6hint; @@ -63,12 +83,12 @@ qboolean NET_StringToAddr (char *s, netadr_t *sadr) len = sizeof(dupbase)-1; strncpy(dupbase, s, len); dupbase[len] = '\0'; - error = pgetaddrinfo(dupbase, port+1, &udp6hint, &addrinfo); + error = getaddrinfo(dupbase, port+1, &udp6hint, &addrinfo); } else error = EAI_NONAME; if (error) //failed, try string with no port. - error = pgetaddrinfo(s, NULL, &udp6hint, &addrinfo); //remember, this func will return any address family that could be using the udp protocol... (ip4 or ip6) + error = getaddrinfo(s, NULL, &udp6hint, &addrinfo); //remember, this func will return any address family that could be using the udp protocol... (ip4 or ip6) if (error) { return false; @@ -214,18 +234,21 @@ qboolean Net_ConnectToServer(sv_t *qtv, char *ip) if (bind(qtv->sourcesock, (struct sockaddr *)&from, sizeof(from)) == -1) { closesocket(qtv->sourcesock); + qtv->sourcesock = INVALID_SOCKET; return false; } if (connect(qtv->sourcesock, (struct sockaddr *)&qtv->serveraddress, sizeof(qtv->serveraddress)) == INVALID_SOCKET) { 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; } @@ -305,7 +328,12 @@ void Net_TryFlushProxyBuffer(oproxy_t *prox) if (bufpos+length > MAX_PROXY_BUFFER) printf("oversize flush\n"); - length = send(prox->sock, buffer, length, 0); + 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 @@ -368,12 +396,49 @@ void Prox_SendMessage(oproxy_t *prox, char *buf, int length, int dem_type, unsig WriteByte(&msg, 0); WriteByte(&msg, dem_type); WriteLong(&msg, length); + if (dem_type == dem_multiple) + WriteLong(&msg, playermask); + Net_ProxySend(prox, msg.data, msg.cursize); Net_ProxySend(prox, buf, length); } +void Prox_SendPlayerStats(sv_t *qtv, oproxy_t *prox) +{ + char buffer[MAX_MSGLEN*8]; + 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<lateforward) Net_ProxySend(prox, qtv->buffer, qtv->buffersize); //send all the info we've not yet processed. @@ -450,7 +517,10 @@ void Net_ForwardStream(sv_t *qtv, char *buffer, int length) { next = qtv->proxies->next; fre = qtv->proxies; - closesocket(fre->sock); + if (fre->file) + fclose(fre->file); + else + closesocket(fre->sock); free(fre); qtv->proxies = next; } @@ -461,7 +531,10 @@ void Net_ForwardStream(sv_t *qtv, char *buffer, int length) { next = prox->next->next; fre = prox->next; - closesocket(fre->sock); + if (fre->file) + fclose(fre->file); + else + closesocket(fre->sock); free(fre); prox->next = next; } @@ -528,6 +601,7 @@ qboolean Net_ReadStream(sv_t *qtv) { if (qtv->sourcesock != INVALID_SOCKET) { + printf("Error: source socket error %i\n", qerrno); closesocket(qtv->sourcesock); qtv->sourcesock = INVALID_SOCKET; } @@ -550,25 +624,101 @@ unsigned int Sys_Milliseconds(void) #ifdef _WIN32 #pragma comment(lib, "winmm.lib") return timeGetTime(); +#else + //assume every other system follows standards. + struct timeval tv; + + gettimeofday(&tv, NULL); + return ((unsigned)tv.tv_sec)*1000 + (((unsigned)tv.tv_usec)/1000); #endif } void NetSleep(sv_t *tv) { + int m; + int ret; struct timeval timeout; fd_set socketset; -Sleep(0); - FD_ZERO(&socketset); + FD_ZERO(&socketset); + m = 0; if (tv->sourcesock != INVALID_SOCKET) + { FD_SET(tv->sourcesock, &socketset); + if (tv->sourcesock >= m) + m = tv->sourcesock+1; + } if (tv->qwdsocket != INVALID_SOCKET) + { FD_SET(tv->qwdsocket, &socketset); + if (tv->sourcesock >= m) + m = tv->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; - select(2, &socketset, NULL, NULL, &timeout); + ret = select(m, &socketset, NULL, NULL, &timeout); + +#ifdef _WIN32 + for (;;) +#else + if (FD_ISSET(STDIN, &socketset)) +#endif + { + char buffer[8192]; + char *result; + char c; + +#ifdef _WIN32 + if (!kbhit()) + break; + else + c = getch(); +#else + c = recv(STDIN, &c, 1, 0); +#endif + + if (c == '\n' || c == '\r') + { + printf("\n"); + if (tv->inputlength) + { + tv->commandinput[tv->inputlength] = '\0'; + result = Rcon_Command(tv, tv->commandinput, buffer, sizeof(buffer), true); + printf("%s", result); + tv->inputlength = 0; + tv->commandinput[0] = '\0'; + } + } + else if (c == '\b') + { + if (tv->inputlength > 0) + { + tv->inputlength--; + tv->commandinput[tv->inputlength] = '\0'; + } + } + else + { + if (tv->inputlength < sizeof(tv->commandinput)-1) + { + tv->commandinput[tv->inputlength++] = c; + tv->commandinput[tv->inputlength] = '\0'; + } + } + + printf("\r%s \b", tv->commandinput); + } } void Trim(char *s) @@ -582,41 +732,78 @@ void Trim(char *s) *s = '\0'; } +qboolean QTV_Connect(sv_t *qtv, char *serverurl) +{ + if (qtv->sourcesock != INVALID_SOCKET) + { + closesocket(qtv->sourcesock); + qtv->sourcesock = INVALID_SOCKET; + } + + if (qtv->file) + { + fclose(qtv->file); + qtv->file = NULL; + } + + *qtv->serverinfo = '\0'; + Info_SetValueForStarKey(qtv->serverinfo, "*version", "FTEQTV", sizeof(qtv->serverinfo)); + Info_SetValueForStarKey(qtv->serverinfo, "*qtv", VERSION, sizeof(qtv->serverinfo)); + Info_SetValueForStarKey(qtv->serverinfo, "hostname", qtv->hostname, sizeof(qtv->serverinfo)); + Info_SetValueForStarKey(qtv->serverinfo, "maxclients", "99", sizeof(qtv->serverinfo)); + if (!strncmp(qtv->server, "file:", 5)) + Info_SetValueForStarKey(qtv->serverinfo, "server", "file", sizeof(qtv->serverinfo)); + else + Info_SetValueForStarKey(qtv->serverinfo, "server", qtv->server, sizeof(qtv->serverinfo)); + + memcpy(qtv->server, serverurl, sizeof(qtv->server)-1); + + if (!Net_ConnectToServer(qtv, qtv->server)) + { + printf("Couldn't connect (%s)\n", qtv->server); + return false; + } + printf("Connected\n"); + + if (qtv->sourcesock == INVALID_SOCKET) + { + qtv->parsetime = Sys_Milliseconds(); + printf("Playing from file\n"); + } + else + { + qtv->parsetime = Sys_Milliseconds() + BUFFERTIME*1000; + printf("Buffering for %i seconds\n", BUFFERTIME); + } + return true; +} + void QTV_Run(sv_t *qtv) { int lengthofs; unsigned int length; unsigned char *buffer; int oldcurtime; + int packettime; while(1) { + NetSleep(qtv); + if (qtv->sourcesock == INVALID_SOCKET && !qtv->file) - { - if (!Net_ConnectToServer(qtv, qtv->server)) + if (!QTV_Connect(qtv, qtv->server)) { - printf("Couldn't connect\n"); + QW_UpdateUDPStuff(qtv); continue; } - printf("Connected\n"); - if (qtv->sourcesock == INVALID_SOCKET) - { - qtv->parsetime = Sys_Milliseconds(); - printf("Playing from file\n"); - } - else - { - qtv->parsetime = Sys_Milliseconds() + BUFFERTIME*1000; - printf("Buffering for %i seconds\n", BUFFERTIME); - } - } - - NetSleep(qtv); Net_FindProxies(qtv); if (!Net_ReadStream(qtv)) + { + QW_UpdateUDPStuff(qtv); continue; + } //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 @@ -732,14 +919,19 @@ void QTV_Run(sv_t *qtv) qtv->oldpackettime = qtv->curtime; - qtv->parsetime += buffer[0]; + packettime = buffer[0]; if (qtv->lateforward) Net_ForwardStream(qtv, qtv->buffer, lengthofs+4+length); - memmove(qtv->buffer, qtv->buffer+lengthofs+4+length, qtv->buffersize-(lengthofs+length+4)); - qtv->buffersize -= lengthofs+4+length; + if (qtv->buffersize) + { //svc_disconnect can flush our input buffer (to prevent the EndOfDemo part from interfering) + memmove(qtv->buffer, qtv->buffer+lengthofs+4+length, qtv->buffersize-(lengthofs+length+4)); + qtv->buffersize -= lengthofs+4+length; + } if (qtv->file) Net_ReadStream(qtv); + + qtv->parsetime += packettime; } else break; @@ -758,69 +950,9 @@ int main(int argc, char **argv) sv_t qtv; - char *configfilename; char line[1024]; - char *eq,*semi; - - memset(&qtv, 0, sizeof(qtv)); - //set up a default config - qtv.tcplistenportnum = PROX_DEFAULTLISTENPORT; - qtv.qwlistenportnum = PROX_DEFAULTLISTENPORT; - strcpy(qtv.server, PROX_DEFAULTSERVER); - - - line[sizeof(line)-1] = '\0'; - if (argc < 2) - configfilename = "ftv.cfg"; - else - configfilename = argv[1]; - f = fopen(configfilename, "rt"); - if (!f) - printf("Couldn't open config file \"%s\", using defaults\n", configfilename); - else - { - while(fgets(line, sizeof(line)-1, f)) - { - eq = strchr(line, '='); - if (!eq) - continue; - semi = strchr(eq, ';'); - if (!semi) - { - printf("Missing ; in config file\n"); - continue; - } - *eq = '\0'; - *semi = '\0'; - eq++; - Trim(line); - Trim(eq); - if (!strcmp(line, "server")) - strncpy(qtv.server, eq, sizeof(qtv.server)-1); - else if (!strcmp(line, "file")) - { - strcpy(qtv.server, "file:"); - strncpy(qtv.server+5, eq, sizeof(qtv.server)-1); - } - else if (!strcmp(line, "tcpport")) - qtv.tcplistenportnum = atoi(eq); - else if (!strcmp(line, "udpport")) - qtv.qwlistenportnum = atoi(eq); - else if (!strcmp(line, "choke")) - qtv.chokeonnotupdated = !!atoi(eq); - else if (!strcmp(line, "lateforward")) - qtv.lateforward = !!atoi(eq); - else - { - printf("config: can't recognise %s\n", line); - } - } - fclose(f); - } - - qtv.qwdsocket = INVALID_SOCKET; - qtv.listenmvd = INVALID_SOCKET; - qtv.sourcesock = INVALID_SOCKET; + char buffer[8192]; + char *res; #ifdef _WIN32 { @@ -829,10 +961,61 @@ int main(int argc, char **argv) } #endif - qtv.qwdsocket = QW_InitUDPSocket(qtv.qwlistenportnum); - qtv.listenmvd = Net_MVDListen(qtv.tcplistenportnum); + + memset(&qtv, 0, sizeof(qtv)); + //set up a default config + qtv.tcplistenportnum = PROX_DEFAULTLISTENPORT; + qtv.qwlistenportnum = PROX_DEFAULTLISTENPORT; + strcpy(qtv.server, PROX_DEFAULTSERVER); + strcpy(qtv.hostname, DEFAULT_HOSTNAME); + qtv.chokeonnotupdated = true; + + qtv.qwdsocket = INVALID_SOCKET; + qtv.listenmvd = INVALID_SOCKET; + qtv.sourcesock = INVALID_SOCKET; + + + line[sizeof(line)-1] = '\0'; + if (argc < 2) + res = "ftv.cfg"; + else + res = argv[1]; + f = fopen(res, "rt"); + if (!f) + printf("Couldn't open config file \"%s\"\n", res); + else + { + while(fgets(line, sizeof(line)-1, f)) + { + res = Rcon_Command(&qtv, line, buffer, sizeof(buffer), true); + printf("%s", res); + } + fclose(f); + } + + + //make sure there's a use for this proxy. + if (qtv.qwdsocket == INVALID_SOCKET) + { //still not opened one? try again + qtv.qwdsocket = QW_InitUDPSocket(qtv.qwlistenportnum); + if (qtv.qwdsocket == INVALID_SOCKET) + printf("Warning: couldn't open udp socket\n"); + } + if (qtv.listenmvd == INVALID_SOCKET) + { + qtv.listenmvd = Net_MVDListen(qtv.tcplistenportnum); + if (qtv.listenmvd == INVALID_SOCKET) + printf("Warning: couldn't open mvd socket\n"); + } + if (qtv.qwdsocket == INVALID_SOCKET && qtv.listenmvd == INVALID_SOCKET) + { + printf("Shutting down, couldn't open listening ports (useless proxy)\n"); + return 0; + } qtv.parsingconnectiondata = true; QTV_Run(&qtv); + + return 0; }