diff --git a/fteqtv/bsp.c b/fteqtv/bsp.c index 249b2cf9a..a2925a8b4 100644 --- a/fteqtv/bsp.c +++ b/fteqtv/bsp.c @@ -231,13 +231,23 @@ bsp_t *BSP_LoadModel(cluster_t *cluster, char *gamedir, char *bspname) header = (dheader_t*)data; - if (data[0] != 29) + if (size < sizeof(dheader_t) || data[0] != 29) { free(data); Sys_Printf(cluster, "BSP not version 29\n", bspname, gamedir); return NULL; } + for (i = 0; i < HEADER_LUMPS; i++) + { + if (LittleLong(header->lumps[i].fileofs) + LittleLong(header->lumps[i].filelen) > size) + { + free(data); + Sys_Printf(cluster, "BSP appears truncated\n", bspname, gamedir); + return NULL; + } + } + planes = (dplane_t*)(data+LittleLong(header->lumps[LUMP_PLANES].fileofs)); nodes = (dnode_t*)(data+LittleLong(header->lumps[LUMP_NODES].fileofs)); leaf = (dleaf_t*)(data+LittleLong(header->lumps[LUMP_LEAFS].fileofs)); diff --git a/fteqtv/netchan.c b/fteqtv/netchan.c index 261de6f0b..da285a75f 100644 --- a/fteqtv/netchan.c +++ b/fteqtv/netchan.c @@ -31,7 +31,7 @@ void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, char *data, net ret = sendto(sock, data, length, 0, (struct sockaddr *)adr, sizeof(struct sockaddr_in)); if (ret < 0) { - Sys_Printf(cluster, "udp send error\n"); + Sys_Printf(cluster, "udp send error %i\n", ret); } } diff --git a/fteqtv/parse.c b/fteqtv/parse.c index c2b7ec534..64f926a3b 100644 --- a/fteqtv/parse.c +++ b/fteqtv/parse.c @@ -218,6 +218,8 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma tv->clservercount = ReadLong(m); //we don't care about server's servercount, it's all reliable data anyway. + tv->trackplayer = -1; + ReadString(m, tv->gamedir, sizeof(tv->gamedir)); if (tv->usequkeworldprotocols) @@ -265,8 +267,10 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma if (tv->usequkeworldprotocols) { + tv->netchan.message.cursize = 0; //mvdsv sucks SendClientCommand(tv, "soundlist %i 0\n", tv->clservercount); } + strcpy(tv->status, "Receiving soundlist\n"); } static void ParseCDTrack(sv_t *tv, netmsg_t *m, int to, unsigned int mask) @@ -284,13 +288,15 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) qboolean fromproxy; ReadString(m, text, sizeof(text)); - Sys_Printf(tv->cluster, "stuffcmd: %s", text); +// Sys_Printf(tv->cluster, "stuffcmd: %s", text); if (!strcmp(text, "skins\n")) { const char newcmd[10] = {svc_stufftext, 'c', 'm', 'd', ' ', 'n','e','w','\n','\0'}; tv->parsingconnectiondata = false; + strcpy(tv->status, "On server\n"); + for (v = tv->cluster->viewers; v; v = v->next) { if (v->server == tv) @@ -353,8 +359,39 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask) SendClientCommand(tv, "new\n"); return; } + else if (!strncmp(text, "packet ", 7)) + { + if(tv->usequkeworldprotocols) + {//eeeevil hack - Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); +#define ARG_LEN 256 + char *ptr; + char arg[3][ARG_LEN]; + netadr_t adr; + ptr = text; + ptr = COM_ParseToken(ptr, arg[0], ARG_LEN, ""); + ptr = COM_ParseToken(ptr, arg[1], ARG_LEN, ""); + ptr = COM_ParseToken(ptr, arg[2], ARG_LEN, ""); + NET_StringToAddr(arg[1], &adr, 27500); + Netchan_OutOfBand(tv->cluster, tv->sourcesock, adr, strlen(arg[2]), arg[2]); + + //this is an evil hack + SendClientCommand(tv, "new\n"); + return; + } + tv->drop = true; //this shouldn't ever happen + return; + } + else if (tv->usequkeworldprotocols && !strncmp(text, "setinfo ", 8)) + { + Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); + SendClientCommand(tv, text); + } + else + { + Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); + return; + } } static void ParseSetInfo(sv_t *tv, netmsg_t *m) @@ -577,7 +614,9 @@ static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers) for (i=0 ; i<3 ; i++) { if (flags & (PF_VELOCITY1<players[num].current.velocity[i] = ReadShort(m); + else + tv->players[num].current.velocity[i] = 0; } if (flags & PF_MODEL) @@ -600,7 +639,7 @@ static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers) else tv->players[num].current.weaponframe = 0; - tv->players[num].active = (num != tv->thisplayer); + tv->players[num].active = true; } else { @@ -660,6 +699,8 @@ static void ParsePacketEntities(sv_t *tv, netmsg_t *m) viewer_t *v; + tv->nailcount = 0; + tv->physicstime = tv->parsetime; if (tv->cluster->chokeonnotupdated) @@ -750,12 +791,10 @@ static void ParseUpdatePing(sv_t *tv, netmsg_t *m, int to, unsigned int mask) static void ParseUpdateFrags(sv_t *tv, netmsg_t *m, int to, unsigned int mask) { - int best; int pnum; int frags; - char buffer[64]; pnum = ReadByte(m); - frags = ReadShort(m); + frags = (signed short)ReadShort(m); if (pnum < MAX_CLIENTS) tv->players[pnum].frags = frags; @@ -763,26 +802,6 @@ static void ParseUpdateFrags(sv_t *tv, netmsg_t *m, int to, unsigned int mask) Sys_Printf(tv->cluster, "svc_updatefrags: invalid player number\n"); Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask); - - if (!tv->usequkeworldprotocols) - return; - - frags = -10000; - best = -1; - for (pnum = 0; pnum < MAX_CLIENTS; pnum++) - { - if (*tv->players[pnum].userinfo && !atoi(Info_ValueForKey(tv->players[pnum].userinfo, "*spectator", buffer, sizeof(buffer)))) - if (frags < tv->players[pnum].frags) - { - best = pnum; - frags = tv->players[pnum].frags; - } - } - if (best != tv->trackplayer) - { - SendClientCommand (tv, "ptrack %i\n", best); - tv->trackplayer = best; - } } static void ParseUpdateStat(sv_t *tv, netmsg_t *m, int to, unsigned int mask) @@ -1045,20 +1064,79 @@ void ParseLightstyle(sv_t *tv, netmsg_t *m) void ParseNails(sv_t *tv, netmsg_t *m, qboolean nails2) { int count; - int nailnum; int i; - unsigned char bits[6]; count = (unsigned char)ReadByte(m); + while(count > sizeof(tv->nails) / sizeof(tv->nails[0])) + { + count--; + if (nails2) + ReadByte(m); + for (i = 0; i < 6; i++) + ReadByte(m); + } + + tv->nailcount = count; while(count-- > 0) { if (nails2) - nailnum = ReadByte(m); + tv->nails[count].number = ReadByte(m); else - nailnum = count; + tv->nails[count].number = count; for (i = 0; i < 6; i++) - bits[i] = ReadByte(m); + tv->nails[count].bits[i] = ReadByte(m); + } +} + +void ParseDownload(sv_t *tv, netmsg_t *m) +{ + int size, b; + unsigned int percent; + char buffer[2048]; + + size = ReadShort(m); + percent = ReadByte(m); + + if (size < 0) + { + Sys_Printf(tv->cluster, "Downloading failed\n"); + if (tv->file) + fclose(tv->file); + tv->file = NULL; + tv->drop = true; + return; + } + + for (b = 0; b < size; b++) + buffer[b] = ReadByte(m); + + if (!tv->file) + { + Sys_Printf(tv->cluster, "Not downloading anything\n"); + tv->drop = true; + return; + } + fwrite(buffer, 1, size, tv->file); + + if (percent == 100) + { + fclose(tv->file); + tv->file = NULL; + Sys_Printf(tv->cluster, "Download complete\n"); + + tv->bsp = BSP_LoadModel(tv->cluster, tv->gamedir, tv->modellist[1].name); + if (!tv->bsp) + tv->drop = true; + else + { + SendClientCommand(tv, "prespawn %i 0 %i\n", tv->clservercount, LittleLong(BSP_Checksum(tv->bsp))); + strcpy(tv->status, "Prespawning\n"); + } + } + else + { + snprintf(tv->status, sizeof(tv->status), "Downloading map, %i%%\n", percent); + SendClientCommand(tv, "nextdl\n"); } -//qwe - [qbyte] num [52 bits] nxyzpy 8 12 12 12 4 8 } void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) @@ -1118,10 +1196,14 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) break; case svc_setangle: + if (!tv->usequkeworldprotocols) + ReadByte(&buf); ReadByte(&buf); ReadByte(&buf); ReadByte(&buf); - ReadByte(&buf); + + if (tv->usequkeworldprotocols && tv->controller) + SendBufferToViewer(tv->controller, buf.data+buf.startpos, buf.readpos - buf.startpos, true); break; case svc_serverdata: @@ -1139,7 +1221,18 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) //#define svc_clientdata 15 // //#define svc_stopsound 16 // //#define svc_updatecolors 17 // [qbyte] [qbyte] [qbyte] -//#define svc_particle 18 // [vec3] + + case svc_particle: + ReadShort(&buf); + ReadShort(&buf); + ReadShort(&buf); + ReadByte(&buf); + ReadByte(&buf); + ReadByte(&buf); + ReadByte(&buf); + ReadByte(&buf); + Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, dem_read, (unsigned)-1); + break; case svc_damage: ParseDamage(tv, &buf, to, mask); break; @@ -1214,7 +1307,9 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) ParseUpdateUserinfo(tv, &buf, to, mask); break; -//#define svc_download 41 // [short] size [size bytes] + case svc_download: // [short] size [size bytes] + ParseDownload(tv, &buf); + break; case svc_playerinfo: ParsePlayerInfo(tv, &buf, clearoldplayers); clearoldplayers = false; @@ -1233,7 +1328,7 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) if (tv->bsp) BSP_Free(tv->bsp); - if (tv->cluster->nobsp) + if (tv->cluster->nobsp && !tv->usequkeworldprotocols) tv->bsp = NULL; else tv->bsp = BSP_LoadModel(tv->cluster, tv->gamedir, tv->modellist[1].name); @@ -1245,17 +1340,42 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) break; tv->numinlines = j; } + strcpy(tv->status, "Prespawning\n"); } if (tv->usequkeworldprotocols) { if (i) SendClientCommand(tv, "modellist %i %i\n", tv->clservercount, i); + else if (!tv->bsp) + { + if (tv->file) + { + fclose(tv->file); + unlink(tv->downloadname); + Sys_Printf(tv->cluster, "Was already downloading %s\nOld download canceled\n"); + tv->file = NULL; + } + snprintf(tv->downloadname, sizeof(tv->downloadname), "%s/%s", (tv->gamedir&&*tv->gamedir)?tv->gamedir:"qw", tv->modellist[1].name, sizeof(tv->downloadname)); + tv->file = fopen(tv->downloadname, "wb"); + if (!tv->file) + tv->drop = true; + else + { + strcpy(tv->status, "Downloading map\n"); + Sys_Printf(tv->cluster, "Attempting download of %s\n", tv->downloadname); + SendClientCommand(tv, "download %s\n", tv->modellist[1].name); + } + } else + { SendClientCommand(tv, "prespawn %i 0 %i\n", tv->clservercount, LittleLong(BSP_Checksum(tv->bsp))); + } } break; case svc_soundlist: i = ParseList(tv, &buf, tv->soundlist, to, mask); + if (!i) + strcpy(tv->status, "Receiving modellist\n"); if (tv->usequkeworldprotocols) { if (i) @@ -1274,8 +1394,13 @@ void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask) break; //#define svc_maxspeed 49 // maxspeed change, for prediction case svc_entgravity: // gravity change, for prediction -ReadFloat(&buf); -break; + ReadFloat(&buf); + Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask); + break; +case svc_maxspeed: + ReadFloat(&buf); + Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask); + break; case svc_setinfo: ParseSetInfo(tv, &buf); break; diff --git a/fteqtv/qtv.h b/fteqtv/qtv.h index 114850f3b..ebb4a1b7d 100644 --- a/fteqtv/qtv.h +++ b/fteqtv/qtv.h @@ -46,6 +46,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #pragma comment (lib, "wsock32.lib") #define qerrno WSAGetLastError() #define EWOULDBLOCK WSAEWOULDBLOCK + #define EINPROGRESS WSAEINPROGRESS #ifdef _MSC_VER //okay, so warnings are here to help... they're ugly though. @@ -205,6 +206,7 @@ typedef struct { unsigned char modelindex; unsigned char skinnum; short origin[3]; + short velocity[3]; unsigned short angles[3]; unsigned char effects; unsigned char weaponframe; @@ -234,6 +236,15 @@ typedef struct { int numents; } packet_entities_t; +typedef struct { + unsigned char msec; + unsigned short angles[3]; + short forwardmove, sidemove, upmove; + unsigned char buttons; + unsigned char impulse; +} usercmd_t; +extern const usercmd_t nullcmd; + #define MAX_BACK_BUFFERS 16 typedef struct sv_s sv_t; typedef struct cluster_s cluster_t; @@ -251,13 +262,18 @@ typedef struct viewer_s { int backbuffered; unsigned int currentstats[MAX_STATS]; - unsigned int trackplayer; + int trackplayer; + int thisplayer; packet_entities_t frame[ENTITY_FRAMES]; struct viewer_s *next; char name[32]; + char userinfo[256]; + + int lost; //packets + usercmd_t ucmds[3]; int settime; //the time that we last told the client. @@ -307,12 +323,9 @@ typedef struct { } entity_t; typedef struct { - unsigned char msec; - unsigned short angles[3]; - short forwardmove, sidemove, upmove; - unsigned char buttons; - unsigned char impulse; -} usercmd_t; + unsigned char number; + char bits[6]; +} nail_t; struct sv_s { netadr_t serveraddress; @@ -355,17 +368,27 @@ struct sv_s { int modelindex_spike; // qw is wierd. int modelindex_player; // qw is wierd. + FILE *downloadfile; + char downloadname[256]; + + char status[64]; + + nail_t nails[32]; + int nailcount; + qboolean usequkeworldprotocols; int challenge; unsigned short qport; int isconnected; int clservercount; unsigned int nextsendpings; - unsigned int trackplayer; + int trackplayer; int thisplayer; unsigned int timeout; qboolean ispaused; unsigned int packetratelimiter; + viewer_t *controller; + boolean maysend; FILE *file; unsigned int filelength; @@ -388,11 +411,10 @@ struct sv_s { + qboolean drop; qboolean disconnectwhennooneiswatching; unsigned int numviewers; - - cluster_t *cluster; sv_t *next; //next proxy->server connection @@ -484,7 +506,7 @@ void ReadString(netmsg_t *b, char *string, int maxlen); //#define svc_clientdata 15 // //#define svc_stopsound 16 // //#define svc_updatecolors 17 // [qbyte] [qbyte] [qbyte] -//#define svc_particle 18 // [vec3] +#define svc_particle 18 // [vec3] #define svc_damage 19 #define svc_spawnstatic 20 @@ -531,7 +553,7 @@ void ReadString(netmsg_t *b, char *string, int maxlen); #define svc_soundlist 46 // [strings] #define svc_packetentities 47 // [...] #define svc_deltapacketentities 48 // [...] -//#define svc_maxspeed 49 // maxspeed change, for prediction +#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_serverinfo 52 // serverinfo @@ -623,7 +645,7 @@ 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); void QTV_Shutdown(sv_t *qtv); -qboolean NET_StringToAddr (char *s, netadr_t *sadr); +qboolean NET_StringToAddr (char *s, netadr_t *sadr, int defaultport); void SendBufferToViewer(viewer_t *v, const char *buffer, int length, qboolean reliable); void QW_PrintfToViewer(viewer_t *v, char *format, ...); @@ -637,7 +659,7 @@ 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 (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); +int Prespawn(sv_t *qtv, int curmsgsize, netmsg_t *msg, int bufnum, int thisplayer); bsp_t *BSP_LoadModel(cluster_t *cluster, char *gamedir, char *bspname); void BSP_Free(bsp_t *bsp); @@ -659,7 +681,7 @@ int vsnprintf(char *buffer, int buffersize, char *format, va_list argptr); #endif qboolean Net_FileProxy(sv_t *qtv, char *filename); -sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, qboolean force, qboolean autoclose); +sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, qboolean force, qboolean autoclose, qboolean noduplicates); SOCKET Net_MVDListen(int port); qboolean Net_StopFileProxy(sv_t *qtv); diff --git a/fteqtv/qtvprox.dsp b/fteqtv/qtvprox.dsp index 791b592dc..0887fd437 100644 --- a/fteqtv/qtvprox.dsp +++ b/fteqtv/qtvprox.dsp @@ -90,6 +90,10 @@ SOURCE=.\bsp.c # End Source File # Begin Source File +SOURCE=.\crc.c +# End Source File +# Begin Source File + SOURCE=.\mdfour.c # End Source File # Begin Source File diff --git a/fteqtv/qw.c b/fteqtv/qw.c index 07b84bf68..2039c36f5 100644 --- a/fteqtv/qw.c +++ b/fteqtv/qw.c @@ -20,7 +20,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "qtv.h" +#define MENU_NONE 0 +#define MENU_SERVERS 1 +#define MENU_ADMIN 2 +#define MENU_ADMINSERVER 3 + void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum); +void QW_SetMenu(viewer_t *v, int menunum); #ifdef _WIN32 int snprintf(char *buffer, int buffersize, char *format, ...) @@ -230,7 +236,7 @@ void BuildServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount) if (mvd) WriteFloat(msg, 0); else - WriteByte(msg, MAX_CLIENTS-1); + WriteByte(msg, tv->thisplayer); WriteString(msg, tv->mapname); @@ -261,6 +267,10 @@ void SendServerData(sv_t *tv, viewer_t *viewer) char buffer[1024]; InitNetMsg(&msg, buffer, sizeof(buffer)); + if (tv && (tv->controller == viewer || !tv->controller)) + viewer->thisplayer = tv->thisplayer; + else + viewer->thisplayer = MAX_CLIENTS-1; BuildServerData(tv, &msg, false, viewer->servercount); SendBufferToViewer(viewer, msg.data, msg.cursize, true); @@ -270,20 +280,42 @@ void SendServerData(sv_t *tv, viewer_t *viewer) memset(viewer->currentstats, 0, sizeof(viewer->currentstats)); } -int SendCurrentUserinfos(sv_t *tv, int cursize, netmsg_t *msg, int i) +int SendCurrentUserinfos(sv_t *tv, int cursize, netmsg_t *msg, int i, int thisplayer) { if (i < 0) return i; if (i >= MAX_CLIENTS) return i; - WriteByte(msg, svc_updateuserinfo); - WriteByte(msg, MAX_CLIENTS-1); - WriteLong(msg, MAX_CLIENTS-1); - WriteString(msg, "\\*spectator\\1\\name\\YOU!"); - - for (; i < MAX_CLIENTS-1; i++) + for (; i < MAX_CLIENTS; i++) { + if (i == thisplayer && !tv->controller) + { + WriteByte(msg, svc_updateuserinfo); + WriteByte(msg, i); + WriteLong(msg, i); + WriteString2(msg, "\\*spectator\\1\\name\\"); + if (!tv || !*tv->hostname) + WriteString(msg, "FTEQTV"); + else + WriteString(msg, tv->hostname); + + WriteByte(msg, svc_updatefrags); + WriteByte(msg, i); + WriteShort(msg, 9999); + + WriteByte(msg, svc_updateping); + WriteByte(msg, i); + WriteShort(msg, 0); + + WriteByte(msg, svc_updatepl); + WriteByte(msg, i); + WriteByte(msg, 0); + + continue; + } + if (!tv) + continue; if (msg->cursize+cursize+strlen(tv->players[i].userinfo) > 768) { return i; @@ -440,7 +472,10 @@ void QW_SetViewersServer(viewer_t *viewer, sv_t *sv) viewer->server = sv; if (viewer->server) viewer->server->numviewers++; - QW_StuffcmdToViewer(viewer, "reconnect\n"); + if (!sv || !sv->parsingconnectiondata) + { + QW_StuffcmdToViewer(viewer, "cmd new\n"); + } viewer->servercount++; viewer->origin[0] = 0; viewer->origin[1] = 0; @@ -509,19 +544,74 @@ void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage) viewer->server->numviewers++; if (!initialserver) - viewer->menunum = 1; + { + QW_SetMenu(viewer, MENU_SERVERS); + } + else + { + viewer->menunum = -1; + QW_SetMenu(viewer, MENU_NONE); + } cluster->numviewers++; Info_ValueForKey(infostring, "name", viewer->name, sizeof(viewer->name)); + strncpy(viewer->userinfo, infostring, sizeof(viewer->userinfo)-1); Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *addr, "j"); QW_PrintfToViewer(viewer, "Welcome to FTEQTV\n"); QW_StuffcmdToViewer(viewer, "alias admin \"cmd admin\"\n"); + + QW_StuffcmdToViewer(viewer, "alias \"proxy:up\" \"say proxy:menu up\"\n"); + QW_StuffcmdToViewer(viewer, "alias \"proxy:down\" \"say proxy:menu down\"\n"); + QW_StuffcmdToViewer(viewer, "alias \"proxy:right\" \"say proxy:menu right\"\n"); + QW_StuffcmdToViewer(viewer, "alias \"proxy:left\" \"say proxy:menu left\"\n"); + + QW_StuffcmdToViewer(viewer, "alias \"proxy:select\" \"say proxy:menu select\"\n"); + + QW_StuffcmdToViewer(viewer, "alias \"proxy:home\" \"say proxy:menu home\"\n"); + QW_StuffcmdToViewer(viewer, "alias \"proxy:end\" \"say proxy:menu end\"\n"); + QW_StuffcmdToViewer(viewer, "alias \"proxy:menu\" \"say proxy:menu\"\n"); + QW_StuffcmdToViewer(viewer, "alias \"proxy:backspace\" \"say proxy:menu backspace\"\n"); + QW_PrintfToViewer(viewer, "Type admin for the admin menu\n"); +// if +} +void QW_SetMenu(viewer_t *v, int menunum) +{ + if ((v->menunum==MENU_NONE) != (menunum==MENU_NONE)) + { + if (menunum != MENU_NONE) + { + QW_StuffcmdToViewer(v, "alias \"+proxfwd\" \"proxy:up\"\n"); + QW_StuffcmdToViewer(v, "alias \"+proxback\" \"proxy:down\"\n"); + QW_StuffcmdToViewer(v, "alias \"+proxleft\" \"proxy:left\"\n"); + QW_StuffcmdToViewer(v, "alias \"+proxright\" \"proxy:right\"\n"); + + QW_StuffcmdToViewer(v, "alias \"-proxfwd\" \" \"\n"); + QW_StuffcmdToViewer(v, "alias \"-proxback\" \" \"\n"); + QW_StuffcmdToViewer(v, "alias \"-proxleft\" \" \"\n"); + QW_StuffcmdToViewer(v, "alias \"-proxright\" \" \"\n"); + } + else + { + QW_StuffcmdToViewer(v, "alias \"+proxfwd\" \"+forward\"\n"); + QW_StuffcmdToViewer(v, "alias \"+proxback\" \"+back\"\n"); + QW_StuffcmdToViewer(v, "alias \"+proxleft\" \"+moveleft\"\n"); + QW_StuffcmdToViewer(v, "alias \"+proxright\" \"+moveright\"\n"); + + QW_StuffcmdToViewer(v, "alias \"-proxfwd\" \"-forward\"\n"); + QW_StuffcmdToViewer(v, "alias \"-proxback\" \"-back\"\n"); + QW_StuffcmdToViewer(v, "alias \"-proxleft\" \"-moveleft\"\n"); + QW_StuffcmdToViewer(v, "alias \"-proxright\" \"-moveright\"\n"); + } + } + + v->menunum = menunum; + v->menuop = 0; } void QTV_Rcon(cluster_t *cluster, char *message, netadr_t *from) @@ -570,8 +660,10 @@ void QTV_Status(cluster_t *cluster, netadr_t *from) WriteString2(&msg, sv->serverinfo); WriteString2(&msg, "\n"); - for (i = 0;i < MAX_CLIENTS-1; i++) + for (i = 0;i < MAX_CLIENTS; i++) { + if (i == sv->thisplayer) + continue; if (!sv->players[i].active) continue; //userid @@ -852,10 +944,59 @@ static float InterpolateAngle(float current, float ideal, float fraction) return current + fraction * move; } +void SendLocalPlayerState(sv_t *tv, viewer_t *v, int playernum, netmsg_t *msg) +{ + int flags; + int j; + + WriteByte(msg, svc_playerinfo); + WriteByte(msg, playernum); + + if (tv && tv->controller == v) + { + v->trackplayer = tv->thisplayer; + flags = 0; + if (tv->players[tv->thisplayer].current.weaponframe) + flags |= PF_WEAPONFRAME; + if (tv->players[tv->thisplayer].current.effects) + flags |= PF_EFFECTS; + for (j=0 ; j<3 ; j++) + if (tv->players[tv->thisplayer].current.velocity[j]) + flags |= (PF_VELOCITY1<players[tv->thisplayer].current.origin[0]); + WriteShort(msg, tv->players[tv->thisplayer].current.origin[1]); + WriteShort(msg, tv->players[tv->thisplayer].current.origin[2]); + WriteByte(msg, tv->players[tv->thisplayer].current.frame); + + for (j=0 ; j<3 ; j++) + if (flags & (PF_VELOCITY1<players[tv->thisplayer].current.velocity[j]); + + if (flags & PF_MODEL) + WriteByte(msg, tv->players[tv->thisplayer].current.modelindex); + if (flags & PF_SKINNUM) + WriteByte(msg, tv->players[tv->thisplayer].current.skinnum); + if (flags & PF_EFFECTS) + WriteByte(msg, tv->players[tv->thisplayer].current.effects); + if (flags & PF_WEAPONFRAME) + WriteByte(msg, tv->players[tv->thisplayer].current.weaponframe); + } + else + { + WriteShort(msg, 0); + WriteShort(msg, v->origin[0]*8); + WriteShort(msg, v->origin[1]*8); + WriteShort(msg, v->origin[2]*8); + WriteByte(msg, 0); + } +} + void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) { packet_entities_t *e; - int i; + int i,j; usercmd_t to; unsigned short flags; short interp; @@ -864,7 +1005,7 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) memset(&to, 0, sizeof(to)); - if (v->server) + if (tv) { if (tv->physicstime != v->settime && tv->cluster->chokeonnotupdated) { @@ -883,8 +1024,16 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) if (lerp > 1) lerp = 1; - for (i = 0; i < MAX_CLIENTS-1; i++) + if (tv->controller == v) + lerp = 1; + + for (i = 0; i < MAX_CLIENTS; i++) { + if (i == v->thisplayer) + { + SendLocalPlayerState(tv, v, i, msg); + continue; + } if (!tv->players[i].active) continue; @@ -935,25 +1084,14 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) else { lerp = 1; + + SendLocalPlayerState(tv, v, v->thisplayer, msg); } - WriteByte(msg, svc_playerinfo); - WriteByte(msg, MAX_CLIENTS-1); - WriteShort(msg, 0); - - WriteShort(msg, v->origin[0]*8); - WriteShort(msg, v->origin[1]*8); - WriteShort(msg, v->origin[2]*8); - - WriteByte(msg, 0); - - - - e = &v->frame[v->netchan.outgoing_sequence&(ENTITY_FRAMES-1)]; e->numents = 0; - if (v->server) + if (tv) for (i = 0; i < tv->maxents; i++) { if (!tv->entity[i].current.modelindex) @@ -979,6 +1117,21 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) } SV_EmitPacketEntities(tv, v, e, msg); + + if (tv && tv->nailcount) + { + WriteByte(msg, svc_nails); + WriteByte(msg, tv->nailcount); + for (i = 0; i < tv->nailcount; i++) + { + WriteByte(msg, tv->nails[i].bits[0]); + WriteByte(msg, tv->nails[i].bits[1]); + WriteByte(msg, tv->nails[i].bits[2]); + WriteByte(msg, tv->nails[i].bits[3]); + WriteByte(msg, tv->nails[i].bits[4]); + WriteByte(msg, tv->nails[i].bits[5]); + } + } } void UpdateStats(sv_t *qtv, viewer_t *v) @@ -992,7 +1145,9 @@ void UpdateStats(sv_t *qtv, viewer_t *v) InitNetMsg(&msg, buf, sizeof(buf)); - if (v->trackplayer < 0 || !qtv) + if (qtv && qtv->controller == v) + stats = qtv->players[qtv->thisplayer].stats; + else if (v->trackplayer < 0 || !qtv) stats = nullstats; else stats = qtv->players[v->trackplayer].stats; @@ -1021,12 +1176,12 @@ void UpdateStats(sv_t *qtv, viewer_t *v) } //returns the next prespawn 'buffer' number to use, or -1 if no more -int Prespawn(sv_t *qtv, int curmsgsize, netmsg_t *msg, int bufnum) +int Prespawn(sv_t *qtv, int curmsgsize, netmsg_t *msg, int bufnum, int thisplayer) { int r, ni; r = bufnum; - ni = SendCurrentUserinfos(qtv, curmsgsize, msg, bufnum); + ni = SendCurrentUserinfos(qtv, curmsgsize, msg, bufnum, thisplayer); r += ni - bufnum; bufnum = ni; bufnum -= MAX_CLIENTS; @@ -1089,13 +1244,20 @@ void PMove(viewer_t *v, usercmd_t *cmd) { int i; float fwd[3], rgt[3], up[3]; + if (v->server && v->server->controller == v) + { + v->origin[0] = v->server->players[v->server->thisplayer].current.origin[0]/8.0f; + v->origin[1] = v->server->players[v->server->thisplayer].current.origin[1]/8.0f; + v->origin[2] = v->server->players[v->server->thisplayer].current.origin[2]/8.0f; + return; + } AngleVectors(cmd->angles, fwd, rgt, up); for (i = 0; i < 3; i++) v->origin[i] += (cmd->forwardmove*fwd[i] + cmd->sidemove*rgt[i] + cmd->upmove*up[i])*(cmd->msec/1000.0f); } -void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message) +void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean noupwards) { char buf[1024]; netmsg_t msg; @@ -1103,7 +1265,7 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message) if (message[strlen(message)-1] == '\"') message[strlen(message)-1] = '\0'; - if (*v->expectcommand && v->menunum) + if (*v->expectcommand) { buf[sizeof(buf)-1] = '\0'; if (!strcmp(v->expectcommand, "hostname")) @@ -1122,7 +1284,7 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message) else if (!strcmp(v->expectcommand, "addserver")) { snprintf(buf, sizeof(buf), "tcp:%s", message); - qtv = QTV_NewServerConnection(cluster, buf, false, false); + qtv = QTV_NewServerConnection(cluster, buf, false, false, false); if (qtv) { QW_SetViewersServer(v, qtv); @@ -1135,7 +1297,7 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message) { if (!strcmp(message, cluster->password)) { - v->menunum = 2; + QW_SetMenu(v, MENU_ADMIN); v->isadmin = true; Sys_Printf(cluster, "Player %s logs in as admin\n", v->name); } @@ -1148,7 +1310,7 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message) else if (!strcmp(v->expectcommand, "adddemo")) { snprintf(buf, sizeof(buf), "file:%s", message); - qtv = QTV_NewServerConnection(cluster, buf, false, false); + qtv = QTV_NewServerConnection(cluster, buf, false, false, false); if (!qtv) QW_PrintfToViewer(v, "Failed to play demo \"%s\"\n", message); else @@ -1211,10 +1373,26 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message) else message += 9; snprintf(buf, sizeof(buf), "udp:%s", message); - qtv = QTV_NewServerConnection(cluster, buf, false, false); + qtv = QTV_NewServerConnection(cluster, buf, false, true, true); if (qtv) { -// QW_SetViewersServer(v, qtv); + QW_SetMenu(v, MENU_NONE); + QW_SetViewersServer(v, qtv); + QW_PrintfToViewer(v, "Connected\n", message); + } + else + QW_PrintfToViewer(v, "Failed to connect to server \"%s\", connection aborted\n", message); + } + else if (!strncmp(message, ".join ", 6)) + { + message += 6; + snprintf(buf, sizeof(buf), "udp:%s", message); + qtv = QTV_NewServerConnection(cluster, buf, false, true, false); + if (qtv) + { + QW_SetMenu(v, MENU_NONE); + QW_SetViewersServer(v, qtv); + qtv->controller = v; QW_PrintfToViewer(v, "Connected\n", message); } else @@ -1224,9 +1402,10 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message) { message += 5; snprintf(buf, sizeof(buf), "tcp:%s", message); - qtv = QTV_NewServerConnection(cluster, buf, false, true); + qtv = QTV_NewServerConnection(cluster, buf, false, true, true); if (qtv) { + QW_SetMenu(v, MENU_NONE); QW_SetViewersServer(v, qtv); QW_PrintfToViewer(v, "Connected\n", message); } @@ -1237,9 +1416,10 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message) { message += 6; snprintf(buf, sizeof(buf), "file:%s", message); - qtv = QTV_NewServerConnection(cluster, buf, false, true); + qtv = QTV_NewServerConnection(cluster, buf, false, true, true); if (qtv) { + QW_SetMenu(v, MENU_NONE); QW_SetViewersServer(v, qtv); QW_PrintfToViewer(v, "Connected\n", message); } @@ -1248,6 +1428,7 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message) } else if (!strncmp(message, ".disconnect", 11)) { + QW_SetMenu(v, MENU_SERVERS); QW_SetViewersServer(v, NULL); QW_PrintfToViewer(v, "Connected\n", message); } @@ -1288,7 +1469,24 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message) } else if (!strncmp(message, "proxy:menu", 10)) { - Menu_Enter(cluster, v, 0); + if (v->menunum) + Menu_Enter(cluster, v, 0); + else + QW_SetMenu(v, MENU_SERVERS); + } + else if (!strncmp(message, ".menu bind", 10) || !strncmp(message, "proxy:menu bindstd", 18)) + { + QW_StuffcmdToViewer(v, "bind uparrow proxy:menu up\n"); + QW_StuffcmdToViewer(v, "bind downarrow proxy:menu down\n"); + QW_StuffcmdToViewer(v, "bind rightarrow proxy:menu right\n"); + QW_StuffcmdToViewer(v, "bind leftarrow proxy:menu left\n"); + + QW_StuffcmdToViewer(v, "bind enter proxy:menu select\n"); + + QW_StuffcmdToViewer(v, "bind home proxy:menu home\n"); + QW_StuffcmdToViewer(v, "bind end proxy:menu end\n"); + QW_StuffcmdToViewer(v, "bind pause proxy:menu\n"); + QW_StuffcmdToViewer(v, "bind backspace proxy:back up\n"); } else { @@ -1297,8 +1495,13 @@ void QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message) if (cluster->notalking) return; - if (qtv && qtv->usequkeworldprotocols) - SendClientCommand(qtv, "say %s: %s\n", v->name, message); + if (qtv && qtv->usequkeworldprotocols && !noupwards) + { + if (qtv->controller == v || !*v->name) + SendClientCommand(qtv, "say %s\n", message); + else + SendClientCommand(qtv, "say %s: %s\n", v->name, message); + } else { @@ -1352,7 +1555,6 @@ void QW_StuffcmdToViewer(viewer_t *v, char *format, ...) va_end (argptr); buf[0] = svc_stufftext; -printf("\"%s\"", buf); SendBufferToViewer(v, buf, strlen(buf)+1, true); } @@ -1362,7 +1564,7 @@ static const filename_t ConnectionlessSoundList[] = {{""}, {""}}; void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) { - usercmd_t oldest, oldcmd, newcmd; +// usercmd_t oldest, oldcmd, newcmd; char buf[1024]; netmsg_t msg; @@ -1379,14 +1581,16 @@ void ParseQWC(cluster_t *cluster, 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")) { - if (qtv->parsingconnectiondata) + if (qtv && qtv->parsingconnectiondata) QW_StuffcmdToViewer(v, "cmd new\n"); else + { SendServerData(qtv, v); + } } else if (!strncmp(buf, "modellist ", 10)) { @@ -1444,6 +1648,7 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) SendServerData(qtv, v); //we're old. else { + int crc; int r; char *s; s = buf+9; @@ -1453,14 +1658,43 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) s++; r = atoi(s); + if (r == 0) + { + while((*s >= '0' && *s <= '9') || *s == '-') + s++; + while(*s == ' ') + s++; + crc = atoi(s); + + if (qtv && qtv->controller == v) + { + if (!qtv->bsp) + { + QW_PrintfToViewer(v, "Proxy was unable to check your map version\n"); + qtv->drop = true; + } + else if (crc != BSP_Checksum(qtv->bsp)) + { + QW_PrintfToViewer(v, "Your map (%s) does not match the servers\n", qtv->modellist[1].name); + qtv->drop = true; + } + } + } + InitNetMsg(&msg, buf, sizeof(buf)); - if (v->server) + if (qtv) { - r = Prespawn(qtv, v->netchan.message.cursize, &msg, r); + r = Prespawn(qtv, v->netchan.message.cursize, &msg, r, v->thisplayer); + SendBufferToViewer(v, msg.data, msg.cursize, true); + } + else + { + r = SendCurrentUserinfos(qtv, v->netchan.message.cursize, &msg, r, v->thisplayer); + if (r > MAX_CLIENTS) + r = -1; SendBufferToViewer(v, msg.data, msg.cursize, true); } - else r = -1; if (r < 0) sprintf(skin, "%ccmd spawn\n", svc_stufftext); @@ -1483,7 +1717,7 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) else { v->thinksitsconnected = true; - if (v->server && v->server->ispaused) + if (qtv && qtv->ispaused) { char msgb[] = {svc_setpause, 1}; SendBufferToViewer(v, msgb, sizeof(msgb), true); @@ -1522,18 +1756,23 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) { } else if (!strncmp(buf, "say \"", 5)) - QTV_Say(cluster, qtv, v, buf+5); + QTV_Say(cluster, qtv, v, buf+5, false); else if (!strncmp(buf, "say ", 4)) - QTV_Say(cluster, qtv, v, buf+4); + QTV_Say(cluster, qtv, v, buf+4, false); + + else if (!strncmp(buf, "say_team \"", 10)) + QTV_Say(cluster, qtv, v, buf+10, true); + else if (!strncmp(buf, "say_team ", 9)) + QTV_Say(cluster, qtv, v, buf+9, true); else if (!strncmp(buf, "servers", 7)) { - v->menunum = 1; + QW_SetMenu(v, MENU_SERVERS); } else if (!strncmp(buf, "reset", 5)) { QW_SetViewersServer(v, NULL); - v->menunum = 1; + QW_SetMenu(v, MENU_SERVERS); } else if (!strncmp(buf, "admin", 5)) { @@ -1542,14 +1781,14 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) if (Netchan_IsLocal(v->netchan.remote_address)) { Sys_Printf(cluster, "Local player %s logs in as admin\n", v->name); - v->menunum = 2; + QW_SetMenu(v, MENU_ADMIN); v->isadmin = true; } else QW_PrintfToViewer(v, "There is no admin password set\nYou may not log in.\n"); } else if (v->isadmin) - v->menunum = 2; + QW_SetMenu(v, MENU_ADMIN); else { strcpy(v->expectcommand, "admin"); @@ -1557,6 +1796,30 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) } } + else if (!strncmp(buf, "setinfo", 5)) + { + #define TOKENIZE_PUNCTUATION "" + #define MAX_ARGS 3 + #define ARG_LEN 256 + + int i; + char arg[MAX_ARGS][ARG_LEN]; + char *argptrs[MAX_ARGS]; + char *command = buf; + + for (i = 0; i < MAX_ARGS; i++) + { + command = COM_ParseToken(command, arg[i], ARG_LEN, TOKENIZE_PUNCTUATION); + argptrs[i] = arg[i]; + } + + Info_SetValueForStarKey(v->userinfo, arg[1], arg[2], sizeof(v->userinfo)); + Info_ValueForKey(v->userinfo, "name", v->name, sizeof(v->name)); + + if (v->server && v->server->controller == v) + SendClientCommand(v->server, "%s", buf); + } + else if (!qtv) { //all the other things need an active server. @@ -1604,19 +1867,27 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) } else { - Sys_Printf(cluster, "Client sent unknown string command: %s\n", buf); + if (v->server && v->server->controller == v) + SendClientCommand(v->server, "%s", buf); + else + Sys_Printf(cluster, "Client sent unknown string command: %s\n", buf); } break; case clc_move: + v->lost = ReadByte(m); ReadByte(m); - ReadByte(m); - ReadDeltaUsercmd(m, &nullcmd, &oldest); - ReadDeltaUsercmd(m, &oldest, &oldcmd); - ReadDeltaUsercmd(m, &oldcmd, &newcmd); - - if (v->menunum) + ReadDeltaUsercmd(m, &nullcmd, &v->ucmds[0]); + ReadDeltaUsercmd(m, &v->ucmds[0], &v->ucmds[1]); + ReadDeltaUsercmd(m, &v->ucmds[1], &v->ucmds[2]); +/* + if (v->server && v->server->controller) + { + v-> + } +*/ +/* if (v->menunum) { if (newcmd.buttons&1 && !(oldcmd.buttons&1)) Menu_Enter(cluster, v, 0); @@ -1637,8 +1908,8 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m) } } } - else - PMove(v, &newcmd); +*/ + PMove(v, &v->ucmds[2]); break; case clc_tmove: v->origin[0] = ((signed short)ReadShort(m))/8.0f; @@ -1663,25 +1934,20 @@ void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum) sv_t *sv; int i, min; - char buffer[2048]; - netmsg_t m; - InitNetMsg(&m, buffer, sizeof(buffer)); - WriteByte(&m, svc_stufftext); switch(viewer->menunum) { default: break; - case 3: + case MENU_ADMINSERVER: if (viewer->server) { i = 0; sv = viewer->server; if (i++ == viewer->menuop) { //mvd port - WriteString(&m, "echo Please enter a new tcp port number\nmessagemode\n"); - SendBufferToViewer(viewer, m.data, m.cursize, true); + QW_StuffcmdToViewer(viewer, "echo Please enter a new tcp port number\nmessagemode\n"); strcpy(viewer->expectcommand, "setmvdport"); } if (i++ == viewer->menuop) @@ -1690,17 +1956,15 @@ void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum) } if (i++ == viewer->menuop) { //back - viewer->menunum = 2; - viewer->menuop = 0; + QW_SetMenu(viewer, MENU_ADMIN); } break; } //fallthrough - case 1: + case MENU_SERVERS: if (!cluster->servers) { - WriteString(&m, "echo Please enter a server ip\nmessagemode\n"); - SendBufferToViewer(viewer, m.data, m.cursize, true); + QW_StuffcmdToViewer(viewer, "echo Please enter a server ip\nmessagemode\n"); strcpy(viewer->expectcommand, "insecadddemo"); } else @@ -1719,16 +1983,14 @@ void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum) { if (i == viewer->menuop) { - if (sv->parsingconnectiondata || !sv->modellist[1].name[0]) + /*if (sv->parsingconnectiondata || !sv->modellist[1].name[0]) { - WriteString(&m, "echo But that stream isn't connected\n"); - SendBufferToViewer(viewer, m.data, m.cursize, true); + QW_PrintfToViewer(viewer, "But that stream isn't connected\n"); } - else + else*/ { QW_SetViewersServer(viewer, sv); - SendBufferToViewer(viewer, m.data, m.cursize, true); - viewer->menunum = 0; + QW_SetMenu(viewer, MENU_NONE); viewer->thinksitsconnected = false; } break; @@ -1736,12 +1998,11 @@ void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum) } } break; - case 2: + case MENU_ADMIN: i = 0; if (i++ == viewer->menuop) { //connection stuff - viewer->menunum = 3; - viewer->menuop = 0; + QW_SetMenu(viewer, MENU_ADMINSERVER); } if (i++ == viewer->menuop) { //qw port @@ -1791,8 +2052,7 @@ void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum) } if (i++ == viewer->menuop) { //back - viewer->menunum = 0; - viewer->menuop = 0; + QW_SetMenu(viewer, MENU_NONE); } @@ -2012,6 +2272,8 @@ void QW_FreeViewer(cluster_t *cluster, viewer_t *viewer) free(viewer->backbuf[i].data); } + if (viewer->server->controller == viewer) + viewer->server->drop = true; if (viewer->server) viewer->server->numviewers--; @@ -2029,11 +2291,12 @@ void QW_UpdateUDPStuff(cluster_t *cluster) int qport; netmsg_t m; + sv_t *useserver; viewer_t *v, *f; if (*cluster->master && (cluster->curtime > cluster->mastersendtime || cluster->mastersendtime > cluster->curtime + 4*1000*60)) //urm... time wrapped? { - if (NET_StringToAddr(cluster->master, &from)) + if (NET_StringToAddr(cluster->master, &from, 27000)) { sprintf(buffer, "a\n%i\n0\n", cluster->mastersequence++); //fill buffer with a heartbeat //why is there no \xff\xff\xff\xff ?.. @@ -2083,16 +2346,34 @@ void QW_UpdateUDPStuff(cluster_t *cluster) { if (Netchan_Process(&v->netchan, &m)) { + useserver = v->server; + if (useserver && useserver->parsingconnectiondata) + useserver = NULL; + v->netchan.outgoing_sequence = v->netchan.incoming_sequence; //compensate for client->server packetloss. - if (!v->server) - v->maysend = true; - else if (!v->chokeme || !cluster->chokeonnotupdated) + if (v->server && v->server->controller == v) { - v->maysend = true; - v->chokeme = cluster->chokeonnotupdated; +// v->maysend = true; + v->server->maysend = true; +// v->server->netchan.outgoing_sequence = v->netchan.incoming_sequence; + } + else + { + if (!v->server) + v->maysend = true; + else if (!v->chokeme || !cluster->chokeonnotupdated) + { + v->maysend = true; + v->chokeme = cluster->chokeonnotupdated; + } } - ParseQWC(cluster, v->server, v, &m); + ParseQWC(cluster, useserver, v, &m); + + if (v->server && v->server->controller == v) + { + QTV_Run(v->server); + } } break; } @@ -2120,17 +2401,26 @@ void QW_UpdateUDPStuff(cluster_t *cluster) QW_FreeViewer(cluster, f); } - if (v->maysend && (!v->server || !v->server->parsingconnectiondata)) //don't send incompleate connection data. + if (v->maysend) //don't send incompleate connection data. { + useserver = v->server; + if (useserver && useserver->parsingconnectiondata) + useserver = NULL; + v->maysend = false; m.cursize = 0; if (v->thinksitsconnected) { - SendPlayerStates(v->server, v, &m); - UpdateStats(v->server, v); + SendPlayerStates(useserver, v, &m); + UpdateStats(useserver, v); if (v->menunum) Menu_Draw(cluster, v); + else if (v->server && v->server->parsingconnectiondata) + { + WriteByte(&m, svc_centerprint); + WriteString(&m, v->server->status); + } } Netchan_Transmit(cluster, &v->netchan, m.cursize, m.data); diff --git a/fteqtv/rcon.c b/fteqtv/rcon.c index 1ec35e922..aed8134cf 100644 --- a/fteqtv/rcon.c +++ b/fteqtv/rcon.c @@ -308,7 +308,7 @@ char *Cluster_Rcon_Dispatch(cluster_t *cluster, char *arg[MAX_ARGS], char *buffe 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. + if (NET_StringToAddr(arg[1], &addr, 27000)) //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"; @@ -346,7 +346,7 @@ char *Cluster_Rcon_Dispatch(cluster_t *cluster, char *arg[MAX_ARGS], char *buffe memmove(arg[1]+4, arg[1], ARG_LEN-5); strncpy(arg[1], "tcp:", 4); - if (!QTV_NewServerConnection(cluster, arg[1], false, !strcmp(arg[0], "connect"))) + if (!QTV_NewServerConnection(cluster, arg[1], false, !strcmp(arg[0], "connect"), false)) return "Failed to connect to server, connection aborted\n"; return "Connection registered\n"; } @@ -358,7 +358,7 @@ char *Cluster_Rcon_Dispatch(cluster_t *cluster, char *arg[MAX_ARGS], char *buffe memmove(arg[1]+4, arg[1], ARG_LEN-5); strncpy(arg[1], "udp:", 4); - if (!QTV_NewServerConnection(cluster, arg[1], false, false)) + if (!QTV_NewServerConnection(cluster, arg[1], false, false, false)) return "Failed to connect to server, connection aborted\n"; return "Connection registered\n"; } @@ -374,7 +374,7 @@ char *Cluster_Rcon_Dispatch(cluster_t *cluster, char *arg[MAX_ARGS], char *buffe memmove(arg[1]+5, arg[1], ARG_LEN-6); strncpy(arg[1], "file:", 5); - if (!QTV_NewServerConnection(cluster, arg[1], false, false)) + if (!QTV_NewServerConnection(cluster, arg[1], false, false, false)) return "Failed to connect to server, connection aborted\n"; return "Connection registered\n"; } @@ -459,7 +459,7 @@ char *Cluster_Rcon_Dispatch(cluster_t *cluster, char *arg[MAX_ARGS], char *buffe else if (!strcmp(arg[0], "ping")) { netadr_t addr; - if (NET_StringToAddr(arg[1], &addr)) + if (NET_StringToAddr(arg[1], &addr, 27500)) { NET_SendPacket (cluster, cluster->qwdsocket, 1, "k", addr); return "pinged\n"; diff --git a/fteqtv/source.c b/fteqtv/source.c index da89d5cc5..b3365b96f 100644 --- a/fteqtv/source.c +++ b/fteqtv/source.c @@ -30,8 +30,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define UDPTIMEOUT_LENGTH (1000*20) #define UDPPACKETINTERVAL (1000/72) +void Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox); -qboolean NET_StringToAddr (char *s, netadr_t *sadr) + +qboolean NET_StringToAddr (char *s, netadr_t *sadr, int defaultport) { struct hostent *h; char *colon; @@ -129,7 +131,7 @@ dblbreak: { ((struct sockaddr_in *)sadr)->sin_family = AF_INET; - ((struct sockaddr_in *)sadr)->sin_port = 0; + ((struct sockaddr_in *)sadr)->sin_port = htons(defaultport); strcpy (copy, s); // strip off a trailing :port if present @@ -215,7 +217,7 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip) netadr_t from; unsigned long nonblocking = true; - if (!NET_StringToAddr(ip+4, &qtv->serveraddress)) + if (!NET_StringToAddr(ip+4, &qtv->serveraddress, 27500)) { Sys_Printf(qtv->cluster, "Unable to resolve %s\n", ip); return false; @@ -242,11 +244,7 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip) if (connect(qtv->sourcesock, (struct sockaddr *)&qtv->serveraddress, sizeof(qtv->serveraddress)) == INVALID_SOCKET) { -#ifdef _WIN32 - if (qerrno != WSAEINPROGRESS) -#else if (qerrno != EINPROGRESS) -#endif { closesocket(qtv->sourcesock); qtv->sourcesock = INVALID_SOCKET; @@ -260,7 +258,7 @@ qboolean Net_ConnectToUDPServer(sv_t *qtv, char *ip) netadr_t from; unsigned long nonblocking = true; - if (!NET_StringToAddr(ip+4, &qtv->serveraddress)) + if (!NET_StringToAddr(ip+4, &qtv->serveraddress, 27500)) { Sys_Printf(qtv->cluster, "Unable to resolve %s\n", ip); return false; @@ -349,7 +347,6 @@ void Net_FindProxies(sv_t *qtv) return; } memset(prox, 0, sizeof(*prox)); - prox->flushing = true; //allow the buffer overflow resumption code to send the connection info. prox->sock = sock; prox->file = NULL; @@ -357,6 +354,8 @@ void Net_FindProxies(sv_t *qtv) qtv->proxies = prox; qtv->numproxies++; + + Net_SendConnectionMVD(qtv, prox); } qboolean Net_FileProxy(sv_t *qtv, char *filename) @@ -374,7 +373,7 @@ qboolean Net_FileProxy(sv_t *qtv, char *filename) if (!prox) return false; memset(prox, 0, sizeof(*prox)); - prox->flushing = true; //allow the buffer overflow resumption code to send the connection info. + prox->sock = INVALID_SOCKET; prox->file = f; @@ -383,6 +382,8 @@ qboolean Net_FileProxy(sv_t *qtv, char *filename) qtv->numproxies++; + Net_SendConnectionMVD(qtv, prox); + return true; } @@ -435,7 +436,9 @@ void Net_TryFlushProxyBuffer(cluster_t *cluster, oproxy_t *prox) char *buffer; int length; int bufpos; - fd_set wfds; + + if (prox->drop) + return; while (prox->bufferpos >= MAX_PROXY_BUFFER) { //so we never get any issues with wrapping.. @@ -602,7 +605,7 @@ void Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox) for(prespawn = 0;prespawn>=0;) { - prespawn = Prespawn(qtv, 0, &msg, prespawn); + prespawn = Prespawn(qtv, 0, &msg, prespawn, MAX_CLIENTS-1); Prox_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1); msg.cursize = 0; @@ -923,7 +926,8 @@ qboolean QTV_Connect(sv_t *qtv, char *serverurl) else { qtv->parsetime = Sys_Milliseconds() + BUFFERTIME*1000; - Sys_Printf(qtv->cluster, "Buffering for %i seconds\n", BUFFERTIME); + if (!qtv->usequkeworldprotocols) + Sys_Printf(qtv->cluster, "Buffering for %i seconds\n", BUFFERTIME); } return true; } @@ -1296,6 +1300,123 @@ void SendClientCommand(sv_t *qtv, char *fmt, ...) WriteString(&qtv->netchan.message, buf); } + + + + + +void ChooseFavoriteTrack(sv_t *tv) +{ + int frags, best, pnum; + char buffer[64]; + + frags = -10000; + best = -1; + if (tv->controller) + best = tv->trackplayer; + else + { + for (pnum = 0; pnum < MAX_CLIENTS; pnum++) + { + if (*tv->players[pnum].userinfo && !atoi(Info_ValueForKey(tv->players[pnum].userinfo, "*spectator", buffer, sizeof(buffer)))) + { + if (tv->thisplayer == pnum) + continue; + if (frags < tv->players[pnum].frags) + { + best = pnum; + frags = tv->players[pnum].frags; + } + } + } + } + if (best != tv->trackplayer) + { + SendClientCommand (tv, "ptrack %i\n", best); + tv->trackplayer = best; + } +} + + + + + + +static const unsigned char chktbl[1024] = { +0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01, +0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a, +0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58, +0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3, +0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b, +0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36, +0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8, +0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27, +0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd, +0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63, +0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2, +0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d, +0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65, +0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f, +0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f, +0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42, +0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59, +0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9, +0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69, +0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba, +0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85, +0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4, +0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8, +0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa, +0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05, +0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7, +0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a, +0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb, +0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b, +0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d, +0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce, +0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3 +}; + + +unsigned char COM_BlockSequenceCRCByte (unsigned char *base, int length, int sequence) +{ + unsigned short crc; + const unsigned char *p; + unsigned char chkb[60 + 4]; + + p = chktbl + (sequence % (sizeof(chktbl) - 4)); + + if (length > 60) + length = 60; + memcpy (chkb, base, length); + + chkb[length] = (sequence & 0xff) ^ p[0]; + chkb[length+1] = p[1]; + chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2]; + chkb[length+3] = p[3]; + + length += 4; + + crc = QCRC_Block(chkb, length); + + crc &= 0xff; + + return crc; +} +void SetMoveCRC(sv_t *qtv, netmsg_t *msg) +{ + char *outbyte; + outbyte = msg->data + msg->startpos+1; + + *outbyte = COM_BlockSequenceCRCByte( + outbyte+1, msg->cursize - (msg->startpos+2), + qtv->netchan.outgoing_sequence); +} + + + + + void QTV_ParseQWStream(sv_t *qtv) { char buffer[1500]; @@ -1318,18 +1439,25 @@ void QTV_ParseQWStream(sv_t *qtv) { if (buffer[4] == 'c') { //got a challenge + strcpy(qtv->status, "Attemping connection\n"); qtv->challenge = atoi(buffer+5); - sprintf(buffer, "connect %i %i %i \"%s\"", 28, qtv->qport, qtv->challenge, "\\*ver\\fteqtv\\name\\fteqtv\\spectator\\1\\rate\\10000"); + if (qtv->controller) + sprintf(buffer, "connect %i %i %i \"%s\"", 28, qtv->qport, qtv->challenge, qtv->controller->userinfo); + else + sprintf(buffer, "connect %i %i %i \"%s\\name\\%s\"", 28, qtv->qport, qtv->challenge, "\\*ver\\fteqtv\\spectator\\1\\rate\\10000", qtv->cluster->hostname); Netchan_OutOfBand(qtv->cluster, qtv->sourcesock, qtv->serveraddress, strlen(buffer), buffer); continue; } if (buffer[4] == 'n') { + strncpy(qtv->status, buffer+5, sizeof(qtv->status)); + qtv->status[sizeof(qtv->status)-1] = 0; Sys_Printf(qtv->cluster, "%s: %s", qtv->server, buffer+5); continue; } if (buffer[4] == 'j') { + strcpy(qtv->status, "Waiting for gamestate\n"); Netchan_Setup(qtv->sourcesock, &qtv->netchan, qtv->serveraddress, qtv->qport, true); qtv->trackplayer = -1; @@ -1358,10 +1486,16 @@ void QTV_ParseQWStream(sv_t *qtv) if (qtv->simtime < qtv->oldpackettime) qtv->simtime = qtv->oldpackettime; //too old + + if (qtv->controller) + { + qtv->controller->maysend = true; + qtv->controller->netchan.outgoing_sequence = qtv->netchan.incoming_sequence; + qtv->controller->netchan.incoming_sequence = qtv->netchan.incoming_acknowledged; + } } } - void QTV_Run(sv_t *qtv) { int lengthofs; @@ -1370,7 +1504,7 @@ void QTV_Run(sv_t *qtv) int oldcurtime; int packettime; - if (qtv->disconnectwhennooneiswatching && qtv->numviewers == 0) + if (qtv->drop || (qtv->disconnectwhennooneiswatching && qtv->numviewers == 0)) { QTV_Shutdown(qtv); return; @@ -1410,6 +1544,7 @@ void QTV_Run(sv_t *qtv) if (!qtv->isconnected && (qtv->curtime >= qtv->nextconnectattempt || qtv->curtime < qtv->nextconnectattempt - UDPRECONNECT_TIME*2)) { + strcpy(qtv->status, "Attemping challenge\n"); Netchan_OutOfBand(qtv->cluster, qtv->sourcesock, qtv->serveraddress, 13, "getchallenge\n"); qtv->nextconnectattempt = qtv->curtime + UDPRECONNECT_TIME; } @@ -1417,7 +1552,7 @@ void QTV_Run(sv_t *qtv) if (qtv->isconnected) { - char buffer[64]; + char buffer[128]; netmsg_t msg; memset(&msg, 0, sizeof(msg)); msg.data = buffer; @@ -1430,8 +1565,23 @@ void QTV_Run(sv_t *qtv) return; } - if (qtv->curtime < qtv->packetratelimiter - UDPPACKETINTERVAL*2) - qtv->packetratelimiter = qtv->curtime; + if (qtv->controller) + { + qtv->netchan.outgoing_sequence = qtv->controller->netchan.incoming_sequence; + qtv->netchan.incoming_sequence = qtv->controller->netchan.incoming_acknowledged; + if (qtv->maysend) + { + qtv->maysend = false; + qtv->curtime = qtv->packetratelimiter; + } + else + qtv->curtime = qtv->packetratelimiter - 1; + } + else + { + if (qtv->curtime < qtv->packetratelimiter - UDPPACKETINTERVAL*2) + qtv->packetratelimiter = qtv->curtime; + } if (qtv->curtime >= qtv->packetratelimiter) { qtv->packetratelimiter += UDPPACKETINTERVAL; @@ -1440,11 +1590,38 @@ void QTV_Run(sv_t *qtv) { qtv->nextsendpings = qtv->curtime + PINGSINTERVAL_TIME; SendClientCommand(qtv, "pings\n"); + + } + ChooseFavoriteTrack(qtv); + + if (qtv->trackplayer >= 0) + { + WriteByte(&msg, clc_tmove); + WriteShort(&msg, qtv->players[qtv->trackplayer].current.origin[0]); + WriteShort(&msg, qtv->players[qtv->trackplayer].current.origin[1]); + WriteShort(&msg, qtv->players[qtv->trackplayer].current.origin[2]); + } + else if (qtv->controller) + { + WriteByte(&msg, clc_tmove); + WriteShort(&msg, qtv->controller->origin[0]); + WriteShort(&msg, qtv->controller->origin[1]); + WriteShort(&msg, qtv->controller->origin[2]); + +/* qtv->controller->ucmds[0].angles[1] = qtv->curtime*120; + qtv->controller->ucmds[1].angles[1] = qtv->curtime*120; + qtv->controller->ucmds[2].angles[1] = qtv->curtime*120; +*/ + msg.startpos = msg.cursize; + WriteByte(&msg, clc_move); + WriteByte(&msg, 0); + WriteByte(&msg, 0); + WriteDeltaUsercmd(&msg, &nullcmd, &qtv->controller->ucmds[0]); + WriteDeltaUsercmd(&msg, &qtv->controller->ucmds[0], &qtv->controller->ucmds[1]); + WriteDeltaUsercmd(&msg, &qtv->controller->ucmds[1], &qtv->controller->ucmds[2]); + + SetMoveCRC(qtv, &msg); } - WriteByte(&msg, clc_tmove); - WriteShort(&msg, qtv->players[qtv->trackplayer].current.origin[0]); - WriteShort(&msg, qtv->players[qtv->trackplayer].current.origin[1]); - WriteShort(&msg, qtv->players[qtv->trackplayer].current.origin[2]); Netchan_Transmit(qtv->cluster, &qtv->netchan, msg.cursize, msg.data); } @@ -1719,9 +1896,20 @@ void Cluster_Run(cluster_t *cluster) QW_UpdateUDPStuff(cluster); } -sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, qboolean force, qboolean autoclose) +sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, qboolean force, qboolean autoclose, qboolean noduplicates) { - sv_t *qtv = malloc(sizeof(sv_t)); + sv_t *qtv; + + if (noduplicates) + { + for (qtv = cluster->servers; qtv; qtv = qtv->next) + { + if (!strcmp(qtv->server, server)) + return qtv; + } + } + + qtv = malloc(sizeof(sv_t)); if (!qtv) return NULL; @@ -1787,8 +1975,10 @@ int main(int argc, char **argv) cluster_t cluster; #ifndef _WIN32 +#ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif +#endif #ifdef _WIN32 {