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:
parent
cf60390f10
commit
90d5e5d598
6 changed files with 929 additions and 223 deletions
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
218
fteqtv/parse.c
218
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);
|
||||
|
|
164
fteqtv/qtv.h
164
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 <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);
|
||||
|
||||
|
|
366
fteqtv/qw.c
366
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;
|
||||
|
|
369
fteqtv/source.c
369
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<<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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue