1
0
Fork 0
forked from fte/fteqw

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
This commit is contained in:
Spoike 2005-09-24 04:48:20 +00:00
parent cf60390f10
commit 90d5e5d598
6 changed files with 929 additions and 223 deletions

View file

@ -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:

View file

@ -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)

View file

@ -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);

View file

@ -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 <conio.h>
#include <winsock.h>
#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 <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/errno.h>
@ -25,19 +51,14 @@
#include <stdarg.h>
#include <netdb.h>
#include <stdlib.h>
#ifndef SOCKET
#define SOCKET int
#endif
#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif
#define qerrno errno
#include <sys/ioctl.h>
#include <unistd.h>
#define ioctlsocket ioctl
#define closesocket close
#elif defined(linux)
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
@ -48,14 +69,6 @@
#include <sys/ioctl.h>
#include <unistd.h>
#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 <stdio.h>
#include <string.h>
#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] <variable>
#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);

View file

@ -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;

View file

@ -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<<player));
}
}
void Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox)
{
char buffer[MAX_MSGLEN*8];
@ -426,6 +491,8 @@ void Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox)
Net_TryFlushProxyBuffer(prox);
Prox_SendPlayerStats(qtv, prox);
if (!qtv->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;
}