revised the proxy a bit. one cluster, one udp socket, multiple streams. Hurrah! I wonder how many bugs I just added.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@1435 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2005-10-07 02:02:15 +00:00
parent cf2a589dbf
commit dad5de099b
7 changed files with 1438 additions and 496 deletions

View File

@ -176,7 +176,6 @@ unsigned char *ReadFile_WINDOWSSUCKS(char *gamedir, char *filename, int *size)
f = FindInPaks("id1", filename, size);
if (!f)
{
printf("Couldn't open bsp file\n");
return NULL;
}
}
@ -193,7 +192,7 @@ unsigned char *ReadFile_WINDOWSSUCKS(char *gamedir, char *filename, int *size)
return data;
}
bsp_t *BSP_LoadModel(char *gamedir, char *bspname)
bsp_t *BSP_LoadModel(cluster_t *cluster, char *gamedir, char *bspname)
{
unsigned char *data;
unsigned int size;
@ -217,7 +216,7 @@ bsp_t *BSP_LoadModel(char *gamedir, char *bspname)
data = ReadFile_WINDOWSSUCKS("id1", bspname, &size);
if (!data)
{
printf("Couldn't open bsp file \"%s\" (gamedir \"%s\")\n", bspname, gamedir);
Sys_Printf(cluster, "Couldn't open bsp file \"%s\" (gamedir \"%s\")\n", bspname, gamedir);
return NULL;
}
}
@ -226,7 +225,7 @@ bsp_t *BSP_LoadModel(char *gamedir, char *bspname)
header = (dheader_t*)data;
if (data[0] != 29)
{
printf("BSP not version 29\n", bspname, gamedir);
Sys_Printf(cluster, "BSP not version 29\n", bspname, gamedir);
return NULL;
}

View File

@ -25,13 +25,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
void NET_SendPacket(SOCKET sock, int length, char *data, netadr_t adr)
void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, char *data, netadr_t adr)
{
int ret;
ret = sendto(sock, data, length, 0, (struct sockaddr *)adr, sizeof(struct sockaddr_in));
if (ret < 0)
{
printf("udp send error\n");
Sys_Printf(cluster, "udp send error\n");
}
}
@ -104,7 +104,7 @@ Netchan_OutOfBand
Sends an out-of-band datagram
================
*/
void Netchan_OutOfBand (SOCKET sock, netadr_t adr, int length, unsigned char *data)
void Netchan_OutOfBand (cluster_t *cluster, SOCKET sock, netadr_t adr, int length, unsigned char *data)
{
netmsg_t send;
unsigned char send_buf[MAX_MSGLEN + PACKET_HEADER];
@ -116,7 +116,7 @@ void Netchan_OutOfBand (SOCKET sock, netadr_t adr, int length, unsigned char *da
WriteData (&send, data, length);
// send the datagram
NET_SendPacket (sock, send.cursize, send.data, adr);
NET_SendPacket (cluster, sock, send.cursize, send.data, adr);
}
@ -127,7 +127,7 @@ Netchan_OutOfBandPrint
Sends a text message in an out-of-band datagram
================
*/
void Netchan_OutOfBandPrint (SOCKET sock, netadr_t adr, char *format, ...)
void Netchan_OutOfBandPrint (cluster_t *cluster, SOCKET sock, netadr_t adr, char *format, ...)
{
va_list argptr;
char string[8192];
@ -141,7 +141,7 @@ void Netchan_OutOfBandPrint (SOCKET sock, netadr_t adr, char *format, ...)
#endif // _WIN32
va_end (argptr);
Netchan_OutOfBand (sock, adr, strlen(string), (unsigned char *)string);
Netchan_OutOfBand (cluster, sock, adr, strlen(string), (unsigned char *)string);
}
/*
@ -225,7 +225,7 @@ 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, const unsigned char *data)
void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const unsigned char *data)
{
netmsg_t send;
unsigned char send_buf[MAX_MSGLEN + PACKET_HEADER];
@ -291,7 +291,7 @@ void Netchan_Transmit (netchan_t *chan, int length, const unsigned char *data)
// chan->outgoing_size[i] = send.cursize;
// chan->outgoing_time[i] = curtime;
NET_SendPacket (chan->sock, send.cursize, send.data, chan->remote_address);
NET_SendPacket (cluster, chan->sock, send.cursize, send.data, chan->remote_address);
if (chan->cleartime < curtime)
chan->cleartime = curtime + send.cursize*chan->rate;

View File

@ -186,18 +186,20 @@ void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playerma
case dem_single:
case dem_stats:
//check and send to them only if they're tracking this player(s).
for (v = tv->viewers; v; v = v->next)
for (v = tv->cluster->viewers; v; v = v->next)
{
if (v->trackplayer>=0)
if ((1<<v->trackplayer)&playermask)
SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type
if (v->server == tv)
if (v->trackplayer>=0)
if ((1<<v->trackplayer)&playermask)
SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type
}
break;
default:
//send to all
for (v = tv->viewers; v; v = v->next)
for (v = tv->cluster->viewers; v; v = v->next)
{
SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type
if (v->server == tv)
SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type
}
break;
}
@ -224,10 +226,10 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma
/*tv->servertime =*/ ReadFloat(m);
ReadString(m, tv->mapname, sizeof(tv->mapname));
printf("Gamedir: %s\n", tv->gamedir);
printf("---------------------\n");
printf("%s\n", tv->mapname);
printf("---------------------\n");
Sys_Printf(tv->cluster, "Gamedir: %s\n", tv->gamedir);
Sys_Printf(tv->cluster, "---------------------\n");
Sys_Printf(tv->cluster, "%s\n", tv->mapname);
Sys_Printf(tv->cluster, "---------------------\n");
// get the movevars
tv->movevars.gravity = ReadFloat(m);
@ -241,8 +243,11 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma
tv->movevars.waterfriction = ReadFloat(m);
tv->movevars.entgrav = ReadFloat(m);
for (v = tv->viewers; v; v = v->next)
v->thinksitsconnected = false;
for (v = tv->cluster->viewers; v; v = v->next)
{
if (v->server == tv)
v->thinksitsconnected = false;
}
tv->maxents = 0; //clear these
memset(tv->modellist, 0, sizeof(tv->modellist));
@ -273,12 +278,15 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
if (!strcmp(text, "skins\n"))
{
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)
for (v = tv->cluster->viewers; v; v = v->next)
{
SendBufferToViewer(v, newcmd, sizeof(newcmd), true);
if (v->server == tv)
{
v->servercount++;
SendBufferToViewer(v, newcmd, sizeof(newcmd), true);
}
}
return;
}
@ -306,14 +314,14 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
{ //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);
snprintf(text, sizeof(text), "%s %s", tv->cluster->hostname, s);
}
else
{
if (tv->file)
snprintf(text, sizeof(text), "%s (recorded from: %s)", tv->hostname, value);
snprintf(text, sizeof(text), "%s (recorded from: %s)", tv->cluster->hostname, value);
else
snprintf(text, sizeof(text), "%s (live: %s)", tv->hostname, value);
snprintf(text, sizeof(text), "%s (live: %s)", tv->cluster->hostname, value);
}
Info_SetValueForStarKey(tv->serverinfo, "hostname", text, sizeof(tv->serverinfo));
@ -325,6 +333,22 @@ static void ParseStufftext(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 ParseSetInfo(sv_t *tv, netmsg_t *m)
{
int pnum;
char key[64];
char value[256];
pnum = ReadByte(m);
ReadString(m, key, sizeof(key));
ReadString(m, value, sizeof(value));
if (pnum < MAX_CLIENTS)
Info_SetValueForStarKey(tv->players[pnum].userinfo, key, value, sizeof(tv->players[pnum].userinfo));
if (!tv->parsingconnectiondata)
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, dem_all, (unsigned)-1);
}
static void ParseServerinfo(sv_t *tv, netmsg_t *m)
{
char key[64];
@ -366,10 +390,16 @@ static void ParsePrint(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
*t = '[';
if (*t == 17)
*t = ']';
if (*t == 29)
*t = '-';
if (*t == 30)
*t = '-';
if (*t == 31)
*t = '-';
if (*t == '\a') //doh. :D
*t = ' ';
}
printf("%s", text);
Sys_Printf(tv->cluster, "%s", text);
}
}
@ -430,7 +460,7 @@ static void ParseStaticSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
if (tv->staticsound_count == MAX_STATICSOUNDS)
{
tv->staticsound_count--; // don't be fatal.
printf("Too many static sounds\n");
Sys_Printf(tv->cluster, "Too many static sounds\n");
}
tv->staticsound[tv->staticsound_count].origin[0] = ReadShort(m);
@ -462,7 +492,7 @@ 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");
Sys_Printf(tv->cluster, "Too many static entities\n");
}
ParseEntityState(&tv->spawnstatic[tv->spawnstatic_count], m);
@ -473,16 +503,28 @@ void ParseSpawnStatic(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
}
static void ParsePlayerInfo(sv_t *tv, netmsg_t *m)
static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers)
{
int flags;
int num;
int i;
if (clearoldplayers)
{
for (i = 0; i < MAX_CLIENTS; i++)
{ //hide players
//they'll be sent after this packet.
tv->players[i].active = false;
}
}
num = ReadByte(m);
if (num >= MAX_CLIENTS)
{
num = 0; // don't be fatal.
printf("Too many svc_playerinfos, wrapping\n");
Sys_Printf(tv->cluster, "Too many svc_playerinfos, wrapping\n");
}
tv->players[num].old = tv->players[num].current;
@ -544,18 +586,13 @@ static void ParsePacketEntities(sv_t *tv, netmsg_t *m)
tv->physicstime = tv->parsetime;
if (tv->chokeonnotupdated)
for (v = tv->viewers; v; v = v->next)
if (tv->cluster->chokeonnotupdated)
for (v = tv->cluster->viewers; v; v = v->next)
{
v->chokeme = false;
if (v->server == tv)
v->chokeme = false;
}
for (entnum = 0; entnum < MAX_CLIENTS; entnum++)
{ //hide players
//they'll be sent after this packet.
tv->players[entnum].active = false;
}
//luckilly, only updated entities are here, so that keeps cpu time down a bit.
for (;;)
{
@ -630,7 +667,7 @@ static void ParseUpdatePing(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
if (pnum < MAX_CLIENTS)
tv->players[pnum].ping = ping;
else
printf("svc_updateping: invalid player number\n");
Sys_Printf(tv->cluster, "svc_updateping: invalid player number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
}
@ -645,7 +682,7 @@ static void ParseUpdateFrags(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
if (pnum < MAX_CLIENTS)
tv->players[pnum].frags = frags;
else
printf("svc_updatefrags: invalid player number\n");
Sys_Printf(tv->cluster, "svc_updatefrags: invalid player number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
}
@ -668,7 +705,7 @@ static void ParseUpdateStat(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
}
}
else
printf("svc_updatestat: invalid stat number\n");
Sys_Printf(tv->cluster, "svc_updatestat: invalid stat number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
}
@ -690,7 +727,7 @@ static void ParseUpdateStatLong(sv_t *tv, netmsg_t *m, int to, unsigned int mask
}
}
else
printf("svc_updatestatlong: invalid stat number\n");
Sys_Printf(tv->cluster, "svc_updatestatlong: invalid stat number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
}
@ -704,7 +741,7 @@ static void ParseUpdateUserinfo(sv_t *tv, netmsg_t *m)
ReadString(m, tv->players[pnum].userinfo, sizeof(tv->players[pnum].userinfo));
else
{
printf("svc_updateuserinfo: invalid player number\n");
Sys_Printf(tv->cluster, "svc_updateuserinfo: invalid player number\n");
while (ReadByte(m)) //suck out the message.
{
}
@ -722,7 +759,7 @@ static void ParsePacketloss(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
if (pnum < MAX_CLIENTS)
tv->players[pnum].packetloss = value;
else
printf("svc_updatepl: invalid player number\n");
Sys_Printf(tv->cluster, "svc_updatepl: invalid player number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
}
@ -738,7 +775,7 @@ static void ParseUpdateEnterTime(sv_t *tv, netmsg_t *m, int to, unsigned int mas
if (pnum < MAX_CLIENTS)
tv->players[pnum].entertime = value;
else
printf("svc_updateentertime: invalid player number\n");
Sys_Printf(tv->cluster, "svc_updateentertime: invalid player number\n");
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
}
@ -881,7 +918,7 @@ static void ParseTempEntity(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
ReadShort (m);
break;
default:
printf("temp entity %i not recognised\n", i);
Sys_Printf(tv->cluster, "temp entity %i not recognised\n", i);
return;
}
@ -896,7 +933,7 @@ void ParseLightstyle(sv_t *tv, netmsg_t *m)
ReadString(m, tv->lightstyle[style].name, sizeof(tv->lightstyle[style].name));
else
{
printf("svc_lightstyle: invalid lightstyle index (%i)\n", style);
Sys_Printf(tv->cluster, "svc_lightstyle: invalid lightstyle index (%i)\n", style);
while (ReadByte(m)) //suck out the message.
{
}
@ -908,6 +945,7 @@ void ParseLightstyle(sv_t *tv, netmsg_t *m)
void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
{
netmsg_t buf;
qboolean clearoldplayers = true;
buf.cursize = length;
buf.maxsize = length;
buf.readpos = 0;
@ -917,7 +955,7 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
{
if (buf.readpos > buf.cursize)
{
printf("Read past end of parse buffer\n");
Sys_Printf(tv->cluster, "Read past end of parse buffer\n");
return;
}
// printf("%i\n", buf.buffer[0]);
@ -926,10 +964,10 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
{
case svc_bad:
ParseError(&buf);
printf("ParseMessage: svc_bad\n");
Sys_Printf(tv->cluster, "ParseMessage: svc_bad\n");
return;
case svc_nop: //quakeworld isn't meant to send these.
printf("nop\n");
Sys_Printf(tv->cluster, "nop\n");
break;
case svc_disconnect:
@ -1054,7 +1092,8 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
//#define svc_download 41 // [short] size [size bytes]
case svc_playerinfo:
ParsePlayerInfo(tv, &buf);
ParsePlayerInfo(tv, &buf, clearoldplayers);
clearoldplayers = false;
break;
//#define svc_nails 43 // [qbyte] num [48 bits] xyzpy 12 12 12 4 8
case svc_chokecount:
@ -1067,10 +1106,10 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
if (tv->bsp)
BSP_Free(tv->bsp);
if (tv->nobsp)
if (tv->cluster->nobsp)
tv->bsp = NULL;
else
tv->bsp = BSP_LoadModel(tv->gamedir, tv->modellist[1].name);
tv->bsp = BSP_LoadModel(tv->cluster, tv->gamedir, tv->modellist[1].name);
tv->numinlines = 0;
for (i = 2; i < 256; i++)
@ -1094,7 +1133,9 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
break;
//#define svc_maxspeed 49 // maxspeed change, for prediction
//#define svc_entgravity 50 // gravity change, for prediction
//#define svc_setinfo 51 // setinfo on a client
case svc_setinfo:
ParseSetInfo(tv, &buf);
break;
case svc_serverinfo:
ParseServerinfo(tv, &buf);
break;
@ -1106,7 +1147,7 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
default:
buf.readpos = buf.startpos;
printf("Can't handle svc %i\n", (unsigned int)ReadByte(&buf));
Sys_Printf(tv->cluster, "Can't handle svc %i\n", (unsigned int)ReadByte(&buf));
return;
}
}

View File

@ -27,6 +27,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//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
//how does multiple servers work
//each proxy acts as a cluster of connections to servers
//when a viewer connects, they are given a list of active server connections
//if there's only one server connection, they are given that one automatically.
#ifdef _WIN32
#include <conio.h>
#include <winsock.h>
@ -42,6 +49,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#endif
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#elif defined(__CYGWIN__)
@ -221,6 +229,8 @@ typedef struct {
} packet_entities_t;
#define MAX_BACK_BUFFERS 16
typedef struct sv_s sv_t;
typedef struct cluster_s cluster_t;
typedef struct viewer_s {
qboolean drop;
netchan_t netchan;
@ -229,6 +239,8 @@ typedef struct viewer_s {
qboolean thinksitsconnected;
int delta_frame;
int servercount;
netmsg_t backbuf[MAX_BACK_BUFFERS]; //note data is malloced!
int backbuffered;
@ -245,6 +257,12 @@ typedef struct viewer_s {
int settime; //the time that we last told the client.
float origin[3];
sv_t *server;
int menunum;
int menuop;
int fwdval; //for scrolling up/down the menu using +forward/+back :)
} viewer_t;
typedef struct oproxy_s {
@ -287,7 +305,6 @@ typedef struct sv_s {
unsigned int parsetime;
int servercount;
char gamedir[MAX_QPATH];
char mapname[256];
struct {
@ -312,6 +329,7 @@ typedef struct sv_s {
filename_t lightstyle[MAX_LIGHTSTYLES];
char serverinfo[MAX_SERVERINFO_STRING];
char hostname[MAX_QPATH];
playerinfo_t players[MAX_CLIENTS];
filename_t modellist[MAX_MODELS];
@ -323,10 +341,7 @@ typedef struct sv_s {
SOCKET sourcesock;
SOCKET listenmvd; //tcp + mvd protocol
SOCKET qwdsocket; //udp + quakeworld protocols
viewer_t *viewers;
int numviewers;
oproxy_t *proxies;
int numproxies;
@ -336,35 +351,51 @@ typedef struct sv_s {
unsigned int curtime;
unsigned int oldpackettime;
unsigned int nextpackettime;
unsigned int nextconnectattemp;
int tcplistenportnum;
int qwlistenportnum;
unsigned int mastersendtime;
unsigned int mastersequence;
char commandinput[512];
int inputlength;
cluster_t *cluster;
sv_t *next; //next proxy->server connection
bsp_t *bsp;
int numinlines;
//options:
char server[MAX_QPATH];
} sv_t;
typedef struct cluster_s {
SOCKET qwdsocket; //udp + quakeworld protocols
char commandinput[512];
int inputlength;
unsigned int mastersendtime;
unsigned int mastersequence;
unsigned int curtime;
viewer_t *viewers;
int numviewers;
sv_t *servers;
int numservers;
//options
int qwlistenportnum;
char password[256];
char hostname[256];
char master[MAX_QPATH];
qboolean chokeonnotupdated;
qboolean lateforward;
qboolean notalking;
char password[256];
char hostname[256];
char server[MAX_QPATH];
char master[MAX_QPATH];
qboolean nobsp;
int maxviewers;
int maxproxies;
} sv_t;
typedef struct {
sv_t server;
} qtv_t;
boolean wanttoexit;
} cluster_t;
@ -465,7 +496,7 @@ void ReadString(netmsg_t *b, char *string, int maxlen);
#define svc_deltapacketentities 48 // [...]
//#define svc_maxspeed 49 // maxspeed change, for prediction
//#define svc_entgravity 50 // gravity change, for prediction
//#define svc_setinfo 51 // setinfo on a client
#define svc_setinfo 51 // setinfo on a client
#define svc_serverinfo 52 // serverinfo
#define svc_updatepl 53 // [qbyte] [qbyte]
@ -548,9 +579,9 @@ 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);
void BuildServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount);
SOCKET QW_InitUDPSocket(int port);
void QW_UpdateUDPStuff(sv_t *qtv);
void QW_UpdateUDPStuff(cluster_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);
@ -559,22 +590,29 @@ 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, ...);
void Netchan_OutOfBandPrint (cluster_t *cluster, SOCKET sock, netadr_t adr, char *format, ...);
void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, char *data, netadr_t adr);
qboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2);
qboolean Netchan_Process (netchan_t *chan, netmsg_t *msg);
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);
void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const unsigned char *data);
int SendList(sv_t *qtv, int first, const filename_t *list, int svc, netmsg_t *msg);
int Prespawn(sv_t *qtv, int curmsgsize, netmsg_t *msg, int bufnum);
bsp_t *BSP_LoadModel(char *gamedir, char *bspname);
bsp_t *BSP_LoadModel(cluster_t *cluster, 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 *Rcon_Command(cluster_t *cluster, 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);
void Sys_Printf(cluster_t *cluster, char *fmt, ...);
qboolean Net_FileProxy(sv_t *qtv, char *filename);
sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, qboolean force);
SOCKET Net_MVDListen(int port);
qboolean Net_StopFileProxy(sv_t *qtv);

File diff suppressed because it is too large Load Diff

View File

@ -89,7 +89,7 @@ void Info_RemoveKey (char *s, const char *key)
if (strstr (key, "\\"))
{
printf ("Key has a slash\n");
// printf ("Key has a slash\n");
return;
}
@ -139,19 +139,19 @@ void Info_SetValueForStarKey (char *s, const char *key, const char *value, int m
if (strstr (key, "\\") || strstr (value, "\\") )
{
printf ("Key has a slash\n");
// printf ("Key has a slash\n");
return;
}
if (strstr (key, "\"") || strstr (value, "\"") )
{
printf ("Key has a quote\n");
// printf ("Key has a quote\n");
return;
}
if (strlen(key) >= MAX_INFO_KEY || strlen(value) >= MAX_INFO_KEY)
{
printf ("Key or value is too long\n");
// printf ("Key or value is too long\n");
return;
}
@ -177,7 +177,7 @@ void Info_SetValueForStarKey (char *s, const char *key, const char *value, int m
if ((int)(strlen(newv) + strlen(s) + 1) > maxsize)
{
printf ("info buffer is too small\n");
// printf ("info buffer is too small\n");
return;
}
@ -293,23 +293,219 @@ skipwhite:
return data;
}
char *Rcon_Command(sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qboolean localcommand)
#define MAX_ARGS 8
#define ARG_LEN 512
char *Cluster_Rcon_Dispatch(cluster_t *cluster, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
if (!strcmp(arg[0], "hostname"))
{
strncpy(cluster->hostname, arg[1], sizeof(cluster->hostname)-1);
return "hostname will change at start of next map\n"; //I'm too lazy to alter the serverinfo here.
}
else if (!strcmp(arg[0], "master"))
{
netadr_t addr;
strncpy(cluster->master, arg[1], sizeof(cluster->master)-1);
cluster->mastersendtime = cluster->curtime;
if (NET_StringToAddr(arg[1], &addr)) //send a ping like a qw server does. this is kinda pointless of course.
NET_SendPacket (cluster, cluster->qwdsocket, 1, "k", addr);
return "Master server set.\n";
}
else if (!strcmp(arg[0], "port"))
{
int news;
int newp = atoi(arg[1]);
news = QW_InitUDPSocket(newp);
if (news != INVALID_SOCKET)
{
cluster->mastersendtime = cluster->curtime;
closesocket(cluster->qwdsocket);
cluster->qwdsocket = news;
cluster->qwlistenportnum = newp;
return "Opened udp port (all connected qw clients will time out)\n";
}
else
return "Failed to open udp port\n";
}
else if (!strcmp(arg[0], "password"))
{
if (!localcommand)
return "Rejecting rcon password change.\n";
strncpy(cluster->password, arg[1], sizeof(cluster->password)-1);
return "Password changed.\n";
}
else if (!strcmp(arg[0], "connect") || !strcmp(arg[0], "addserver"))
{
if (!*arg[1])
return "connect requires an ip:port parameter\n";
memmove(arg[1]+4, arg[1], ARG_LEN-5);
strncpy(arg[1], "tcp:", 4);
if (!QTV_NewServerConnection(cluster, arg[1], false))
return "Failed to connect to server, connection aborted\n";
return "Connection registered\n";
}
else if (!strcmp(arg[0], "demo") || !strcmp(arg[0], "adddemo") || !strcmp(arg[0], "addfile"))
{
if (!*arg[1])
return "adddemo requires an filename parameter\n";
if (!localcommand)
if (*arg[1] == '\\' || *arg[1] == '/' || strstr(arg[1], "..") || arg[1][1] == ':')
return "Absolute paths are prohibited.\n";
memmove(arg[1]+5, arg[1], ARG_LEN-6);
strncpy(arg[1], "file:", 5);
if (!QTV_NewServerConnection(cluster, arg[1], false))
return "Failed to connect to server, connection aborted\n";
return "Connection registered\n";
}
else if (!strcmp(arg[0], "exec"))
{
FILE *f;
char line[512], *res;
if (!localcommand)
if (*arg[1] == '\\' || *arg[1] == '/' || strstr(arg[1], "..") || arg[1][1] == ':')
return "Absolute paths are prohibited.\n";
f = fopen(arg[1], "rt");
if (!f)
{
snprintf(buffer, sizeofbuffer, "Couldn't exec \"%s\"\n", arg[1]);
return buffer;
}
else
{
while(fgets(line, sizeof(line)-1, f))
{
res = Rcon_Command(cluster, NULL, line, buffer, sizeofbuffer, localcommand);
Sys_Printf(cluster, "%s", res);
}
fclose(f);
return "Execed\n";
}
}
else if (!strcmp(arg[0], "status"))
{
buffer[0] = '\0';
sprintf(buffer, "%i connections\n", cluster->numservers);
strcat(buffer, "Options:\n");
if (cluster->chokeonnotupdated)
strcat(buffer, " Choke\n");
if (cluster->lateforward)
strcat(buffer, " Late forwarding\n");
if (!cluster->notalking)
strcat(buffer, " Talking allowed\n");
if (cluster->nobsp)
strcat(buffer, " No BSP loading\n");
strcat(buffer, "\n");
return buffer;
}
else if (!strcmp(arg[0], "choke"))
{
cluster->chokeonnotupdated = !!atoi(arg[1]);
return "choke-until-update set\n";
}
else if (!strcmp(arg[0], "late"))
{
cluster->lateforward = !!atoi(arg[1]);
return "late forwarding set\n";
}
else if (!strcmp(arg[0], "talking"))
{
cluster->notalking = !atoi(arg[1]);
return "talking permissions set\n";
}
else if (!strcmp(arg[0], "nobsp"))
{
cluster->nobsp = !!atoi(arg[1]);
return "nobsp will change at start of next map\n";
}
else if (!strcmp(arg[1], "maxviewers"))
{
cluster->maxviewers = atoi(arg[2]);
return "maxviewers set\n";
}
else if (!strcmp(arg[1], "maxproxies"))
{
cluster->maxproxies = atoi(arg[2]);
return "maxproxies set\n";
}
else if (!strcmp(arg[0], "ping"))
{
netadr_t addr;
if (NET_StringToAddr(arg[1], &addr))
{
NET_SendPacket (cluster, cluster->qwdsocket, 1, "k", addr);
return "pinged\n";
}
return "couldn't resolve\n";
}
else if (!strcmp(arg[0], "help"))
{
return "FTEQTV proxy\nValid commands: connect, addserver, adddemo, status, choke, late, talking, nobsp, exec, password, master, hostname, port, maxviewers, maxproxies\n";
}
else if (!strcmp(arg[0], "mvdport"))
{
return "mvdport requires a targeted server. Connect first.\n";
}
else if (!strcmp(arg[0], "record"))
{
return "record requires a targeted server\n";
}
else if (!strcmp(arg[0], "reconnect"))
{
return "reconnect requires a targeted server\n";
}
else if (!strcmp(arg[0], "stop"))
{ //fixme
return "stop requires a targeted server\n";
}
else if (!strcmp(arg[0], "echo"))
{
return "Poly wants a cracker.\n";
}
else if (!strcmp(arg[0], "quit"))
{
cluster->wanttoexit = true;
return "Shutting down.\n";
}
else
return NULL;
}
char *Server_Rcon_Dispatch(sv_t *qtv, char *arg[MAX_ARGS], char *buffer, int sizeofbuffer, qboolean localcommand)
{
#define TOKENIZE_PUNCTUATION ""
int i;
#define MAX_ARGS 8
#define ARG_LEN 512
char arg[MAX_ARGS][ARG_LEN];
for (i = 0; i < MAX_ARGS; i++)
command = COM_ParseToken(command, arg[i], ARG_LEN, TOKENIZE_PUNCTUATION);
buffer[0] = '\0';
if (!strcmp(arg[0], "status"))
{
sprintf(buffer, "%i connections\n", qtv->cluster->numservers);
strcat(buffer, "\n");
strcat(buffer, "Server: ");
strcat(buffer, "Selected server: ");
strcat(buffer, qtv->server);
strcat(buffer, "\n");
if (qtv->file)
@ -326,13 +522,6 @@ char *Rcon_Command(sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qbo
strcat(buffer, arg[0]);
strcat(buffer, ")\n");
}
if (qtv->qwdsocket != INVALID_SOCKET)
{
strcat(buffer, "Listening for qwcl (");
sprintf(arg[0], "%i", qtv->qwlistenportnum);
strcat(buffer, arg[0]);
strcat(buffer, ")\n");
}
if (qtv->bsp)
{
@ -342,55 +531,19 @@ char *Rcon_Command(sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qbo
}
strcat(buffer, "Options:\n");
if (qtv->chokeonnotupdated)
if (qtv->cluster->chokeonnotupdated)
strcat(buffer, " Choke\n");
if (qtv->lateforward)
if (qtv->cluster->lateforward)
strcat(buffer, " Late forwarding\n");
if (!qtv->notalking)
if (!qtv->cluster->notalking)
strcat(buffer, " Talking allowed\n");
if (qtv->cluster->nobsp)
strcat(buffer, " No BSP loading\n");
strcat(buffer, "\n");
return buffer;
}
else if (!strcmp(arg[0], "option"))
{
if (!*arg[1])
return "option X Y\nWhere X is choke/late/talking/hostname/nobsp/master/maxviewers/maxproxies and Y is (mostly) 0/1\n";
if (!strcmp(arg[1], "choke"))
qtv->chokeonnotupdated = !!atoi(arg[2]);
else if (!strcmp(arg[1], "late"))
qtv->lateforward = !!atoi(arg[2]);
else if (!strcmp(arg[1], "talking"))
qtv->notalking = !atoi(arg[2]);
else if (!strcmp(arg[1], "nobsp"))
{
qtv->nobsp = !!atoi(arg[2]);
return "nobsp will change at start of next map\n";
}
else if (!strcmp(arg[1], "hostname"))
{
strncpy(qtv->hostname, arg[2], sizeof(qtv->hostname)-1);
return "hostname will change at start of next map\n"; //I'm too lazy to alter the serverinfo here.
}
else if (!strcmp(arg[1], "master"))
{
netadr_t addr;
strncpy(qtv->master, arg[2], sizeof(qtv->master)-1);
qtv->mastersendtime = qtv->curtime;
if (NET_StringToAddr(arg[1], &addr))
NET_SendPacket (qtv->qwdsocket, 1, "k", addr);
}
else if (!strcmp(arg[1], "maxviewers"))
qtv->maxviewers = atoi(arg[2]);
else if (!strcmp(arg[1], "maxproxies"))
qtv->maxproxies = atoi(arg[2]);
else
return "Option not recognised\n";
return "Set\n";
}
else if (!strcmp(arg[0], "connect"))
{
if (!*arg[1])
@ -410,7 +563,7 @@ char *Rcon_Command(sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qbo
if (*arg[1] == '\\' || *arg[1] == '/' || strstr(arg[1], "..") || arg[1][1] == ':')
return "Absolute paths are prohibited.\n";
memmove(arg[1]+5, arg[1], sizeof(arg[1])-6);
memmove(arg[1]+5, arg[1], ARG_LEN-6);
strncpy(arg[1], "file:", 5);
if (QTV_Connect(qtv, arg[1]))
return "File opened successfully\n";
@ -438,10 +591,7 @@ char *Rcon_Command(sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qbo
else
return "not recording to disk\n";
}
else if (!strcmp(arg[0], "help"))
{
return "FTEQTV proxy version "VERSION"\nValid commands: connect, file, status, option, mvdport, port, reconnect\n";
}
else if (!strcmp(arg[0], "reconnect"))
{
if (QTV_Connect(qtv, qtv->server))
@ -483,64 +633,72 @@ char *Rcon_Command(sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qbo
}
}
else if (!strcmp(arg[0], "ping"))
else if (!strcmp(arg[0], "exec"))
{
netadr_t addr;
if (NET_StringToAddr(arg[1], &addr))
{
NET_SendPacket (qtv->qwdsocket, 1, "k", addr);
return "pinged\n";
}
return "couldn't resolve\n";
}
else if (!strcmp(arg[0], "port") || !strcmp(arg[0], "udpport"))
{
int news;
int newp = atoi(arg[1]);
FILE *f;
char line[512], *res;
if (!localcommand)
return "Changing udp port is not allowed via rcon\n";
if (*arg[1] == '\\' || *arg[1] == '/' || strstr(arg[1], "..") || arg[1][1] == ':')
return "Absolute paths are prohibited.\n";
if (!newp)
f = fopen(arg[1], "rt");
if (!f)
{
if (qtv->listenmvd != INVALID_SOCKET)
{
closesocket(qtv->listenmvd);
qtv->listenmvd = INVALID_SOCKET;
qtv->tcplistenportnum = 0;
return "Closed udp port\n";
}
return "udp port was already closed\n";
snprintf(buffer, sizeofbuffer, "Couldn't exec \"%s\"\n", arg[1]);
return buffer;
}
else
{
news = QW_InitUDPSocket(newp);
if (news != INVALID_SOCKET)
while(fgets(line, sizeof(line)-1, f))
{
qtv->mastersendtime = qtv->curtime;
closesocket(qtv->qwdsocket);
qtv->qwdsocket = news;
qtv->qwlistenportnum = newp;
return "Opened udp port\n";
res = Rcon_Command(qtv->cluster, qtv, line, buffer, sizeofbuffer, localcommand);
Sys_Printf(qtv->cluster, "%s", res);
}
else
return "Failed to open udp port\n";
fclose(f);
return "Execed\n";
}
}
else if (!strcmp(arg[0], "password"))
{
if (!localcommand)
return "Rejecting rcon password change.\n";
strncpy(qtv->password, arg[1], sizeof(qtv->password)-1);
return "Password changed.\n";
}
else
return "Unrecognised command.\n";
{
return NULL;
}
}
char *Rcon_Command(cluster_t *cluster, sv_t *qtv, char *command, char *buffer, int sizeofbuffer, qboolean localcommand)
{
#define TOKENIZE_PUNCTUATION ""
int i;
char arg[MAX_ARGS][ARG_LEN];
char *argptrs[MAX_ARGS];
char *result;
for (i = 0; i < MAX_ARGS; i++)
{
command = COM_ParseToken(command, arg[i], ARG_LEN, TOKENIZE_PUNCTUATION);
argptrs[i] = arg[i];
}
if (qtv)
{ //if there is a specific connection targetted
result = Server_Rcon_Dispatch(qtv, argptrs, buffer, sizeofbuffer, localcommand);
if (result)
return result;
}
else if (cluster->numservers == 1)
{ //if it's a single-connection proxy
result = Server_Rcon_Dispatch(cluster->servers, argptrs, buffer, sizeofbuffer, localcommand);
if (result)
return result;
}
result = Cluster_Rcon_Dispatch(cluster, argptrs, buffer, sizeofbuffer, localcommand);
if (result)
return result;
snprintf(buffer, sizeofbuffer, "Command \"%s\" not recognised.\n", arg[0]);
return buffer;
}

View File

@ -20,6 +20,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "qtv.h"
#define RECONNECT_TIME (1000*30)
qboolean NET_StringToAddr (char *s, netadr_t *sadr)
{
@ -216,13 +218,21 @@ qboolean Net_ConnectToServer(sv_t *qtv, char *ip)
fseek(qtv->file, 0, SEEK_SET);
return true;
}
printf("Unable to open file %s\n", ip+5);
Sys_Printf(qtv->cluster, "Unable to open file %s\n", ip+5);
return false;
}
if (!NET_StringToAddr(ip, &qtv->serveraddress))
qtv->nextconnectattemp = qtv->curtime + RECONNECT_TIME; //wait half a minuite before trying to reconnect
if (strncmp(ip, "tcp:", 4))
{
printf("Unable to resolve %s\n", ip);
Sys_Printf(qtv->cluster, "Unknown source type %s\n", ip);
return false;
}
if (!NET_StringToAddr(ip+4, &qtv->serveraddress))
{
Sys_Printf(qtv->cluster, "Unable to resolve %s\n", ip);
return false;
}
qtv->sourcesock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
@ -238,18 +248,21 @@ qboolean Net_ConnectToServer(sv_t *qtv, char *ip)
return false;
}
if (connect(qtv->sourcesock, (struct sockaddr *)&qtv->serveraddress, sizeof(qtv->serveraddress)) == INVALID_SOCKET)
if (ioctlsocket (qtv->sourcesock, FIONBIO, &nonblocking) == -1)
{
closesocket(qtv->sourcesock);
qtv->sourcesock = INVALID_SOCKET;
return false;
}
if (ioctlsocket (qtv->sourcesock, FIONBIO, &nonblocking) == -1)
if (connect(qtv->sourcesock, (struct sockaddr *)&qtv->serveraddress, sizeof(qtv->serveraddress)) == INVALID_SOCKET)
{
closesocket(qtv->sourcesock);
qtv->sourcesock = INVALID_SOCKET;
return false;
if (qerrno != EWOULDBLOCK)
{
closesocket(qtv->sourcesock);
qtv->sourcesock = INVALID_SOCKET;
return false;
}
}
return true;
@ -264,7 +277,7 @@ void Net_FindProxies(sv_t *qtv)
if (sock == INVALID_SOCKET)
return;
if (qtv->numproxies >= qtv->maxproxies && qtv->maxproxies)
if (qtv->numproxies >= qtv->cluster->maxproxies && qtv->cluster->maxproxies)
{
const char buffer[] = {dem_all, 1, 'P','r','o','x','y',' ','i','s',' ','f','u','l','l','.'};
send(sock, buffer, strlen(buffer), 0);
@ -353,7 +366,7 @@ void CheckMVDConsistancy(unsigned char *buffer, int pos, int size)
*/
}
void Net_TryFlushProxyBuffer(oproxy_t *prox)
void Net_TryFlushProxyBuffer(cluster_t *cluster, oproxy_t *prox)
{
char *buffer;
int length;
@ -376,7 +389,7 @@ void Net_TryFlushProxyBuffer(oproxy_t *prox)
// CheckMVDConsistancy(prox->buffer, prox->bufferpos, prox->buffersize);
if (bufpos+length > MAX_PROXY_BUFFER)
printf("oversize flush\n");
Sys_Printf(cluster, "oversize flush\n");
if (prox->file)
length = fwrite(buffer, 1, length, prox->file);
@ -398,13 +411,13 @@ void Net_TryFlushProxyBuffer(oproxy_t *prox)
}
}
void Net_ProxySend(oproxy_t *prox, char *buffer, int length)
void Net_ProxySend(cluster_t *cluster, oproxy_t *prox, char *buffer, int length)
{
int wrap;
if (prox->buffersize-prox->bufferpos + length > MAX_PROXY_BUFFER)
{
Net_TryFlushProxyBuffer(prox); //try flushing
Net_TryFlushProxyBuffer(cluster, prox); //try flushing
if (prox->buffersize-prox->bufferpos + length > MAX_PROXY_BUFFER) //damn, still too big.
{ //they're too slow. hopefully it was just momentary lag
prox->flushing = true;
@ -438,7 +451,7 @@ void Net_ProxySend(oproxy_t *prox, char *buffer, int length)
#endif
}
void Prox_SendMessage(oproxy_t *prox, char *buf, int length, int dem_type, unsigned int playermask)
void Prox_SendMessage(cluster_t *cluster, oproxy_t *prox, char *buf, int length, int dem_type, unsigned int playermask)
{
netmsg_t msg;
char tbuf[16];
@ -450,9 +463,9 @@ void Prox_SendMessage(oproxy_t *prox, char *buf, int length, int dem_type, unsig
WriteLong(&msg, playermask);
Net_ProxySend(prox, msg.data, msg.cursize);
Net_ProxySend(cluster, prox, msg.data, msg.cursize);
Net_ProxySend(prox, buf, length);
Net_ProxySend(cluster, prox, buf, length);
}
void Prox_SendPlayerStats(sv_t *qtv, oproxy_t *prox)
@ -502,31 +515,31 @@ void Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox)
prox->flushing = false;
BuildServerData(qtv, &msg, true);
Prox_SendMessage(prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
BuildServerData(qtv, &msg, true, 0);
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0;
for (prespawn = 0;prespawn >= 0;)
{
prespawn = SendList(qtv, prespawn, qtv->soundlist, svc_soundlist, &msg);
Prox_SendMessage(prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0;
}
for (prespawn = 0;prespawn >= 0;)
{
prespawn = SendList(qtv, prespawn, qtv->modellist, svc_modellist, &msg);
Prox_SendMessage(prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0;
}
Net_TryFlushProxyBuffer(prox); //that should be enough data to fill a packet.
Net_TryFlushProxyBuffer(qtv->cluster, prox); //that should be enough data to fill a packet.
for(prespawn = 0;prespawn>=0;)
{
prespawn = Prespawn(qtv, 0, &msg, prespawn);
Prox_SendMessage(prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0;
}
@ -534,30 +547,30 @@ void Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox)
//we do need to send entity states.
Prox_SendInitialEnts(qtv, prox, &msg);
Prox_SendMessage(prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0;
WriteByte(&msg, svc_stufftext);
WriteString(&msg, "skins\n");
Prox_SendMessage(prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);
msg.cursize = 0;
Net_TryFlushProxyBuffer(prox);
Net_TryFlushProxyBuffer(qtv->cluster, prox);
Prox_SendPlayerStats(qtv, prox);
Net_TryFlushProxyBuffer(prox);
Net_TryFlushProxyBuffer(qtv->cluster, prox);
if (!qtv->lateforward)
Net_ProxySend(prox, qtv->buffer, qtv->buffersize); //send all the info we've not yet processed.
if (!qtv->cluster->lateforward)
Net_ProxySend(qtv->cluster, prox, qtv->buffer, qtv->buffersize); //send all the info we've not yet processed.
if (prox->flushing)
{
printf("Connection data is too big, dropping proxy client\n");
Sys_Printf(qtv->cluster, "Connection data is too big, dropping proxy client\n");
prox->drop = true; //this is unfortunate...
}
else
Net_TryFlushProxyBuffer(prox);
Net_TryFlushProxyBuffer(qtv->cluster, prox);
}
void Net_ForwardStream(sv_t *qtv, char *buffer, int length)
@ -604,7 +617,7 @@ void Net_ForwardStream(sv_t *qtv, char *buffer, int length)
}
else
{
Net_TryFlushProxyBuffer(prox); //try and flush it.
Net_TryFlushProxyBuffer(qtv->cluster, prox); //try and flush it.
continue;
}
}
@ -613,12 +626,11 @@ void Net_ForwardStream(sv_t *qtv, char *buffer, int length)
continue;
//add the new data
Net_ProxySend(prox, buffer, length);
Net_ProxySend(qtv->cluster, prox, buffer, length);
//and try to send it.
Net_TryFlushProxyBuffer(prox);
Net_TryFlushProxyBuffer(prox);
Net_TryFlushProxyBuffer(prox);
Net_TryFlushProxyBuffer(qtv->cluster, prox);
// Net_TryFlushProxyBuffer(qtv->cluster, prox);
// Net_TryFlushProxyBuffer(qtv->cluster, prox);
}
}
@ -648,7 +660,7 @@ qboolean Net_ReadStream(sv_t *qtv)
if (read > 0)
{
qtv->buffersize += read;
if (!qtv->lateforward)
if (!qtv->cluster->lateforward)
Net_ForwardStream(qtv, buffer, read);
}
else
@ -657,7 +669,12 @@ qboolean Net_ReadStream(sv_t *qtv)
{
if (qtv->sourcesock != INVALID_SOCKET)
{
printf("Error: source socket error %i\n", qerrno);
int err;
err = qerrno;
if (qerrno)
Sys_Printf(qtv->cluster, "Error: source socket error %i\n", qerrno);
else
Sys_Printf(qtv->cluster, "Error: server disconnected\n");
closesocket(qtv->sourcesock);
qtv->sourcesock = INVALID_SOCKET;
}
@ -666,7 +683,7 @@ qboolean Net_ReadStream(sv_t *qtv)
fclose(qtv->file);
qtv->file = NULL;
}
printf("Read error or eof\n");
Sys_Printf(qtv->cluster, "Read error or eof to %s\n", qtv->server);
return false;
}
}
@ -688,7 +705,7 @@ unsigned int Sys_Milliseconds(void)
return ((unsigned)tv.tv_sec)*1000 + (((unsigned)tv.tv_usec)/1000);
#endif
}
/*
void NetSleep(sv_t *tv)
{
int m;
@ -741,7 +758,7 @@ void NetSleep(sv_t *tv)
if (tv->inputlength)
{
tv->commandinput[tv->inputlength] = '\0';
result = Rcon_Command(tv, tv->commandinput, buffer, sizeof(buffer), true);
result = Rcon_Command(tv->cluster, tv, tv->commandinput, buffer, sizeof(buffer), true);
printf("%s", result);
tv->inputlength = 0;
tv->commandinput[0] = '\0';
@ -787,6 +804,7 @@ void NetSleep(sv_t *tv)
}
#endif
}
*/
void Trim(char *s)
{
@ -816,7 +834,7 @@ qboolean QTV_Connect(sv_t *qtv, char *serverurl)
*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, "hostname", qtv->cluster->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));
@ -827,24 +845,24 @@ qboolean QTV_Connect(sv_t *qtv, char *serverurl)
if (!Net_ConnectToServer(qtv, qtv->server))
{
printf("Couldn't connect (%s)\n", qtv->server);
Sys_Printf(qtv->cluster, "Couldn't connect (%s)\n", qtv->server);
return false;
}
printf("Connected\n");
Sys_Printf(qtv->cluster, "Connected\n");
if (qtv->sourcesock == INVALID_SOCKET)
{
qtv->parsetime = Sys_Milliseconds();
printf("Playing from file\n");
Sys_Printf(qtv->cluster, "Playing from file\n");
}
else
{
qtv->parsetime = Sys_Milliseconds() + BUFFERTIME*1000;
printf("Buffering for %i seconds\n", BUFFERTIME);
Sys_Printf(qtv->cluster, "Buffering for %i seconds\n", BUFFERTIME);
}
return true;
}
/*
void QTV_Run(sv_t *qtv)
{
int lengthofs;
@ -1112,3 +1130,424 @@ int main(int argc, char **argv)
return 0;
}
*/
void QTV_Run(sv_t *qtv)
{
int lengthofs;
unsigned int length;
unsigned char *buffer;
int oldcurtime;
int packettime;
//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
//each new packet comes with a leading msec byte (msecs from last packet)
//then a type, an optional destination mask, and a 4byte size.
//the 4 byte size is probably excessive, a short would do.
//some of the types have thier destination mask encoded inside the type byte, yielding 8 types, and 32 max players.
//if we've no got enough data to read a new packet, we print a message and wait an extra two seconds. this will add a pause, connected clients will get the same pause, and we'll just try to buffer more of the game before playing.
//we'll stay 2 secs behind when the tcp stream catches up, however. This could be bad especially with long up-time.
//All timings are in msecs, which is in keeping with the mvd times, but means we might have issues after 72 or so days.
//the following if statement will reset the parse timer. It might cause the game to play too soon, the buffersize checks in the rest of the function will hopefully put it back to something sensible.
oldcurtime = qtv->curtime;
qtv->curtime = Sys_Milliseconds();
if (oldcurtime > qtv->curtime)
{
Sys_Printf(qtv->cluster, "Time wrapped\n");
qtv->parsetime = qtv->curtime;
}
if (qtv->sourcesock == INVALID_SOCKET && !qtv->file)
{
if (qtv->curtime >= qtv->nextconnectattemp || qtv->curtime < qtv->nextconnectattemp - RECONNECT_TIME*2)
if (!QTV_Connect(qtv, qtv->server))
{
return;
}
}
Net_FindProxies(qtv); //look for any other proxies wanting to muscle in on the action.
if (qtv->file || qtv->sourcesock != INVALID_SOCKET)
{
if (!Net_ReadStream(qtv))
{ //if we have an error reading it
//if it's valid, give up
//what should we do here?
//obviously, we need to keep reading the stream to keep things smooth
}
}
while (qtv->curtime >= qtv->parsetime)
{
if (qtv->buffersize < 2)
{ //not enough stuff to play.
if (qtv->parsetime < qtv->curtime)
{
qtv->parsetime = qtv->curtime + 2*1000; //add two seconds
if (qtv->file || qtv->sourcesock != INVALID_SOCKET)
Sys_Printf(qtv->cluster, "Not enough buffered\n");
}
break;
}
buffer = qtv->buffer;
switch (qtv->buffer[1]&dem_mask)
{
case dem_set:
if (qtv->buffersize < 10)
{ //not enough stuff to play.
qtv->parsetime = qtv->curtime + 2*1000; //add two seconds
if (qtv->file || qtv->sourcesock != INVALID_SOCKET)
Sys_Printf(qtv->cluster, "Not enough buffered\n");
continue;
}
qtv->parsetime += buffer[0]; //well this was pointless
memmove(qtv->buffer, qtv->buffer+10, qtv->buffersize-(10));
qtv->buffersize -= 10;
continue;
case dem_multiple:
lengthofs = 6;
break;
default:
lengthofs = 2;
break;
}
if (qtv->buffersize < lengthofs+4)
{ //the size parameter doesn't fit.
if (qtv->file || qtv->sourcesock != INVALID_SOCKET)
Sys_Printf(qtv->cluster, "Not enough buffered\n");
qtv->parsetime = qtv->curtime + 2*1000; //add two seconds
break;
}
length = (buffer[lengthofs]<<0) + (buffer[lengthofs+1]<<8) + (buffer[lengthofs+2]<<16) + (buffer[lengthofs+3]<<24);
if (length > 1450)
{ //FIXME: THIS SHOULDN'T HAPPEN!
//Blame the upstream proxy!
Sys_Printf(qtv->cluster, "Warning: corrupt input packet (%i) too big! Flushing and reconnecting!\n", length);
if (qtv->file)
{
fclose(qtv->file);
qtv->file = NULL;
}
else
{
closesocket(qtv->sourcesock);
qtv->sourcesock = INVALID_SOCKET;
}
qtv->buffersize = 0;
break;
}
if (length+lengthofs+4 > qtv->buffersize)
{
if (qtv->file || qtv->sourcesock != INVALID_SOCKET)
Sys_Printf(qtv->cluster, "Not enough buffered\n");
qtv->parsetime = qtv->curtime + 2*1000; //add two seconds
break; //can't parse it yet.
}
qtv->nextpackettime = qtv->parsetime+buffer[0];
if (qtv->nextpackettime < qtv->curtime)
{
if (qtv->cluster->lateforward)
Net_ForwardStream(qtv, qtv->buffer, lengthofs+4+length);
switch(qtv->buffer[1]&dem_mask)
{
case dem_multiple:
ParseMessage(qtv, buffer+lengthofs+4, length, qtv->buffer[1]&dem_mask, (buffer[lengthofs-4]<<0) + (buffer[lengthofs+3]<<8) + (buffer[lengthofs-2]<<16) + (buffer[lengthofs-1]<<24));
break;
case dem_single:
case dem_stats:
ParseMessage(qtv, buffer+lengthofs+4, length, qtv->buffer[1]&dem_mask, 1<<(qtv->buffer[1]>>3));
break;
case dem_read:
case dem_all:
ParseMessage(qtv, buffer+lengthofs+4, length, qtv->buffer[1]&dem_mask, 0xffffffff);
break;
default:
Sys_Printf(qtv->cluster, "Message type %i\n", qtv->buffer[1]&dem_mask);
break;
}
qtv->oldpackettime = qtv->curtime;
packettime = buffer[0];
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;
}
}
void Cluster_Run(cluster_t *cluster)
{
sv_t *sv;
int m;
struct timeval timeout;
fd_set socketset;
FD_ZERO(&socketset);
m = 0;
if (cluster->qwdsocket != INVALID_SOCKET)
{
FD_SET(cluster->qwdsocket, &socketset);
if (cluster->qwdsocket >= m)
m = cluster->qwdsocket+1;
}
/*
for (sv = cluster->servers; sv; sv = sv->next)
{
if (sv->sourcesock != INVALID_SOCKET)
{
FD_SET(sv->sourcesock, &socketset);
if (sv->sourcesock >= m)
m = sv->sourcesock+1;
}
}
*/
#ifndef _WIN32
#ifndef STDIN
#define STDIN 0
#endif
FD_SET(STDIN, &socketset);
if (STDIN >= m)
m = STDIN+1;
#endif
timeout.tv_sec = 100/1000;
timeout.tv_usec = (100%1000)*1000;
m = select(m, &socketset, NULL, NULL, &timeout);
#ifdef _WIN32
for (;;)
{
char buffer[8192];
char *result;
char c;
if (!kbhit())
break;
c = getch();
if (c == '\n' || c == '\r')
{
Sys_Printf(cluster, "\n");
if (cluster->inputlength)
{
cluster->commandinput[cluster->inputlength] = '\0';
result = Rcon_Command(cluster, NULL, cluster->commandinput, buffer, sizeof(buffer), true);
Sys_Printf(cluster, "%s", result);
cluster->inputlength = 0;
cluster->commandinput[0] = '\0';
}
}
else if (c == '\b')
{
if (cluster->inputlength > 0)
{
Sys_Printf(cluster, "%c", c);
Sys_Printf(cluster, " ", c);
Sys_Printf(cluster, "%c", c);
cluster->inputlength--;
cluster->commandinput[cluster->inputlength] = '\0';
}
}
else
{
Sys_Printf(cluster, "%c", c);
if (cluster->inputlength < sizeof(cluster->commandinput)-1)
{
cluster->commandinput[cluster->inputlength++] = c;
cluster->commandinput[cluster->inputlength] = '\0';
}
}
}
#else
if (FD_ISSET(STDIN, &socketset))
{
char buffer[8192];
char *result;
cluster->inputlength = read (0, cluster->commandinput, sizeof(cluster->commandinput));
if (cluster->inputlength >= 1)
{
cluster->commandinput[cluster->inputlength-1] = 0; // rip off the /n and terminate
if (cluster->inputlength)
{
cluster->commandinput[cluster->inputlength] = '\0';
result = Rcon_Command(cluster, NULL, cluster->commandinput, buffer, sizeof(buffer), true);
printf("%s", result);
cluster->inputlength = 0;
cluster->commandinput[0] = '\0';
}
}
}
#endif
for (sv = cluster->servers; sv; sv = sv->next)
{
QTV_Run(sv);
}
QW_UpdateUDPStuff(cluster);
}
sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, qboolean force)
{
sv_t *qtv = malloc(sizeof(sv_t));
memset(qtv, 0, sizeof(*qtv));
//set up a default config
qtv->tcplistenportnum = PROX_DEFAULTLISTENPORT;
strcpy(qtv->server, PROX_DEFAULTSERVER);
qtv->listenmvd = INVALID_SOCKET;
qtv->sourcesock = INVALID_SOCKET;
qtv->cluster = cluster;
qtv->next = cluster->servers;
if (!QTV_Connect(qtv, server) && !force)
{
free(qtv);
return NULL;
}
cluster->servers = qtv;
cluster->numservers++;
return qtv;
}
void DoCommandLine(cluster_t *cluster, int argc, char **argv)
{
int i;
char commandline[8192];
char *start, *end, *result;
char buffer[8192];
commandline[0] = '\0';
//build a block of strings.
for (i = 1; i < argc; i++)
{
strcat(commandline, argv[i]);
strcat(commandline, " ");
}
strcat(commandline, "+");
start = commandline;
while(start)
{
end = strchr(start+1, '+');
if (end)
*end = '\0';
if (start[1])
{
result = Rcon_Command(cluster, NULL, start+1, buffer, sizeof(buffer), true);
Sys_Printf(cluster, "%s", result);
}
start = end;
}
}
int main(int argc, char **argv)
{
cluster_t cluster;
#ifdef _WIN32
{
WSADATA discard;
WSAStartup(MAKEWORD(2,0), &discard);
}
#endif
memset(&cluster, 0, sizeof(cluster));
cluster.qwdsocket = INVALID_SOCKET;
cluster.qwlistenportnum = 0;
strcpy(cluster.hostname, DEFAULT_HOSTNAME);
DoCommandLine(&cluster, argc, argv);
if (!cluster.numservers)
{ //probably running on a home user's computer
if (cluster.qwdsocket == INVALID_SOCKET && !cluster.qwlistenportnum)
{
cluster.qwdsocket = QW_InitUDPSocket(cluster.qwlistenportnum = 27599);
if (cluster.qwdsocket != INVALID_SOCKET)
Sys_Printf(&cluster, "opened port %i\n", cluster.qwlistenportnum);
}
Sys_Printf(&cluster, "\nWelcome to FTEQTV\nPlease type \nconnect server:ip\nto connect to a server.\n\n");
}
while (!cluster.wanttoexit)
Cluster_Run(&cluster);
return 0;
}
void Sys_Printf(cluster_t *cluster, char *fmt, ...)
{
va_list argptr;
char string[2024];
va_start (argptr, fmt);
vsnprintf (string, sizeof(string), fmt,argptr);
va_end (argptr);
printf("%s", string);
}