/* Copyright (C) 1996-1997 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the included (GNU.txt) GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef __GNUC__ #define LittleLong(x) ({ typeof(x) _x = (x); _x = (((unsigned char *)&_x)[0]|(((unsigned char *)&_x)[1]<<8)|(((unsigned char *)&_x)[2]<<16)|(((unsigned char *)&_x)[3]<<24)); _x; }) #define LittleShort(x) ({ typeof(x) _x = (x); _x = (((unsigned char *)&_x)[0]|(((unsigned char *)&_x)[1]<<8)); _x; }) #else #define LittleLong(x) (x) #define LittleShort(x) (x) #endif //this is for a future version //#define COMMENTARY //each server that we are connected to has it's own state. //it should be easy enough to use one thread per server. //mvd info is forwarded to other proxies instantly //qwd stuff is buffered and delayed. :( //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 #include //this includes windows.h and is the reason for much compiling slowness with windows builds. #ifdef _MSC_VER #pragma comment (lib, "wsock32.lib") #endif #define qerrno WSAGetLastError() #define EWOULDBLOCK WSAEWOULDBLOCK #define EINPROGRESS WSAEINPROGRESS #define ECONNREFUSED WSAECONNREFUSED #define ENOTCONN WSAENOTCONN //we have special functions to properly terminate sprintf buffers in windows. //we assume other systems are designed with even a minor thought to security. #if !defined(__MINGW32_VERSION) #define unlink _unlink //why do MS have to be so awkward? int snprintf(char *buffer, int buffersize, char *format, ...); #if !defined(_VC80_UPGRADE) int vsnprintf(char *buffer, int buffersize, char *format, va_list argptr); #endif #else #define unlink remove //seems mingw misses something #endif #ifdef _MSC_VER //okay, so warnings are here to help... they're ugly though. #pragma warning(disable: 4761) //integral size mismatch in argument #pragma warning(disable: 4244) //conversion from float to short #pragma warning(disable: 4018) //signed/unsigned mismatch #endif #elif defined(__CYGWIN__) #include #include #include #include #include #include #include #include #include #include #define ioctlsocket ioctl #define closesocket close #elif defined(linux) || defined(ixemul) #include #include #include #include #include #include #include #include #include #include #include #include #define ioctlsocket ioctl #define closesocket close #else #error "Please insert required headers here" //try the cygwin ones #endif #ifndef EAGAIN #define EAGAIN EWOULDBLOCK #endif #ifndef pgetaddrinfo #ifndef _WIN32 #define pgetaddrinfo getaddrinfo #define pfreeaddrinfo freeaddrinfo #endif #endif #ifndef SOCKET #define SOCKET int #endif #ifndef INVALID_SOCKET #define INVALID_SOCKET -1 #endif #ifndef qerrno #define qerrno errno #endif #include #include #ifndef _WIN32 //stricmp is ansi, strcasecmp is unix. #define stricmp strcasecmp #define strnicmp strncasecmp #endif #define ustrlen(s) strlen((char*)(s)) #define ustrcmp(s1,s2) strcmp((char*)(s1),(char*)(s2)) #define ustrncmp(s1,s2,l) strncmp((char*)(s1),(char*)(s2),l) //some systems have strlcat / strlcpy //we support windows and can't use those #define Q_strncatz(dest, src, sizeofdest) \ do { \ strncat(dest, src, sizeofdest - strlen(dest) - 1); \ dest[sizeofdest - 1] = 0; \ } while (0) #define Q_strncpyz(dest, src, sizeofdest) \ do { \ strncpy(dest, src, sizeofdest - 1); \ dest[sizeofdest - 1] = 0; \ } while (0) #define VERSION "0.01" //this will be added to the serverinfo #define PROX_DEFAULTSERVERPORT 27500 #define PROX_DEFAULTLISTENPORT 27501 #define PROX_DEFAULTSERVER "localhost:27500" #define DEFAULT_HOSTNAME "FTEQTV" #define MAX_ENTITY_LEAFS 32 #include "protocol.h" #ifndef __cplusplus typedef enum {false, true} qboolean; #else typedef int qboolean; extern "C" { #endif typedef unsigned char netadr_t[64]; #ifdef COMMENTARY typedef struct soundcapt_s { int (*update)(struct soundcapt_s *ghnd, int samplechunks, char *buffer); void (*close)(struct soundcapt_s *ptr); } soundcapt_t; typedef struct soundplay_s { int (*update)(struct soundplay_s *ghnd, int samplechunks, char *buffer); void (*close)(struct soundplay_s *ptr); } soundplay_t; #endif typedef struct { unsigned int readpos; unsigned int cursize; unsigned int maxsize; void *data; unsigned int startpos; qboolean overflowed; qboolean allowoverflow; } netmsg_t; typedef struct { SOCKET sock; netadr_t remote_address; unsigned short qport; unsigned int last_received; unsigned int cleartime; int reliable_length; qboolean drop; qboolean isclient; qboolean isnqprotocol; netmsg_t message; char message_buf[MAX_NQMSGLEN]; //reliable message being built char reliable_buf[MAX_NQMSGLEN]; //reliable message that we're making sure arrives. float rate; unsigned int incoming_acknowledged; unsigned int last_reliable_sequence; unsigned int incoming_reliable_acknowledged; unsigned int incoming_reliable_sequence; unsigned int reliable_sequence; unsigned int incoming_sequence; unsigned int outgoing_sequence; unsigned int reliable_start; unsigned int outgoing_unreliable; unsigned int incoming_unreliable; unsigned int in_fragment_length; char in_fragment_buf[MAX_NQMSGLEN]; } netchan_t; typedef struct { #define MAX_QPATH 64 char name[MAX_QPATH]; } filename_t; typedef struct { unsigned char frame; unsigned char modelindex; unsigned char colormap; unsigned char skinnum; short origin[3]; char angles[3]; unsigned char effects; } entity_state_t; typedef struct { unsigned char frame; unsigned char modelindex; unsigned char skinnum; short origin[3]; short velocity[3]; short angles[3]; unsigned char effects; unsigned char weaponframe; } player_state_t; typedef struct { unsigned int stats[MAX_STATS]; char userinfo[MAX_USERINFO]; int ping; int packetloss; int frags; float entertime; int leafcount; unsigned short leafs[MAX_ENTITY_LEAFS]; qboolean active:1; qboolean gibbed:1; qboolean dead:1; player_state_t current; player_state_t old; } playerinfo_t; typedef struct { entity_state_t ents[ENTS_PER_FRAME]; //ouchie ouchie! unsigned short entnum[ENTS_PER_FRAME]; 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; typedef float vec3_t[3]; typedef struct { float gravity; float maxspeed; float spectatormaxspeed; float accelerate; float airaccelerate; float waterfriction; float entgrav; float stopspeed; float wateraccelerate; float friction; } movevars_t; typedef struct { //in / out vec3_t origin; vec3_t velocity; //in usercmd_t cmd; movevars_t movevars; //internal vec3_t angles; float frametime; vec3_t forward, right, up; } pmove_t; #define MAX_BACK_BUFFERS 16 typedef struct sv_s sv_t; typedef struct cluster_s cluster_t; typedef struct viewer_s { qboolean drop; unsigned int timeout; unsigned int nextpacket; //for nq clients netchan_t netchan; qboolean maysend; qboolean chokeme; qboolean thinksitsconnected; qboolean conmenussupported; qboolean isproxy; int delta_frame; int servercount; netmsg_t backbuf[MAX_BACK_BUFFERS]; //note data is malloced! int backbuffered; unsigned int currentstats[MAX_STATS]; int trackplayer; int thisplayer; int userid; packet_entities_t frame[ENTITY_FRAMES]; struct viewer_s *next; struct viewer_s *commentator; char name[32]; char userinfo[1024]; int lost; //packets usercmd_t ucmds[3]; int settime; //the time that we last told the client. vec3_t velocity; vec3_t origin; int isadmin; char expectcommand[16]; sv_t *server; int menuspamtime; int menunum; int menuop; int fwdval; //for scrolling up/down the menu using +forward/+back :) } viewer_t; //'other proxy', these are mvd stream clients. typedef struct oproxy_s { int authkey; unsigned int droptime; qboolean flushing; qboolean drop; sv_t *defaultstream; sv_t *stream; FILE *srcfile; //buffer is padded with data from this file when its empty FILE *file; //recording a demo (written to) SOCKET sock; //playing to a proxy unsigned char inbuffer[MAX_PROXY_INBUFFER]; unsigned int inbuffersize; //amount of data available. unsigned char buffer[MAX_PROXY_BUFFER]; unsigned int buffersize; //use cyclic buffering. unsigned int bufferpos; struct oproxy_s *next; } oproxy_t; typedef struct { short origin[3]; unsigned char soundindex; unsigned char volume; unsigned char attenuation; } staticsound_t; typedef struct bsp_s bsp_t; typedef struct { entity_state_t baseline; // entity_state_t current; // entity_state_t old; // unsigned int updatetime; //to stop lerping when it's an old entity (bodies, stationary grenades, ...) int leafcount; unsigned short leafs[MAX_ENTITY_LEAFS]; } entity_t; #define MAX_ENTITY_FRAMES 64 typedef struct { int oldframe; int numents; int maxents; entity_state_t *ents; //dynamically allocated unsigned short *entnums; //dynamically allocated } frame_t; typedef struct { unsigned char number; char bits[6]; } nail_t; typedef struct { float pos[3]; float angle[3]; } intermission_t; typedef enum { SRC_BAD, SRC_DEMO, SRC_UDP, SRC_TCP } sourcetype_t; struct sv_s { //details about a server connection (also known as stream) char connectpassword[64]; //password given to server netadr_t serveraddress; netchan_t netchan; qboolean serverquery; sourcetype_t sourcetype; //proxy chaining qboolean serverisproxy; qboolean proxyisselected; qboolean upstreamacceptschat; qboolean upstreamacceptsdownload; // unsigned char buffer[MAX_PROXY_BUFFER]; //this doesn't cycle. int buffersize; //it memmoves down int forwardpoint; //the point in the stream that we've forwarded up to. qboolean parsingqtvheader; unsigned char upstreambuffer[2048]; int upstreambuffersize; unsigned int parsetime; unsigned int parsespeed; char gamedir[MAX_QPATH]; char mapname[256]; movevars_t movevars; int cdtrack; entity_t entity[MAX_ENTITIES]; frame_t frame[MAX_ENTITY_FRAMES]; // int maxents; staticsound_t staticsound[MAX_STATICSOUNDS]; int staticsound_count; entity_state_t spawnstatic[MAX_STATICENTITIES]; int spawnstatic_count; filename_t lightstyle[MAX_LIGHTSTYLES]; char serverinfo[MAX_SERVERINFO_STRING]; char hostname[MAX_QPATH]; playerinfo_t players[MAX_CLIENTS]; filename_t modellist[MAX_MODELS]; filename_t soundlist[MAX_SOUNDS]; 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 silentstream; qboolean usequakeworldprotocols; int challenge; unsigned short qport; int isconnected; int clservercount; unsigned int nextsendpings; int trackplayer; int thisplayer; unsigned int timeout; qboolean ispaused; unsigned int packetratelimiter; viewer_t *controller; int controllersquencebias; qboolean proxyplayer; //a player is actually playing on the proxy. usercmd_t proxyplayerucmds[3]; int proxyplayerucmdnum; int proxyplayerbuttons; float proxyplayerangles[3]; float proxyplayerimpulse; qboolean maysend; FILE *sourcefile; unsigned int filelength; SOCKET sourcesock; // SOCKET tcpsocket; //tcp + mvd protocol // int tcplistenportnum; oproxy_t *proxies; qboolean parsingconnectiondata; //so reject any new connects for now unsigned int physicstime; //the last time all the ents moved. unsigned int simtime; unsigned int curtime; unsigned int oldpackettime; unsigned int nextpackettime; unsigned int nextconnectattempt; qboolean drop; qboolean disconnectwhennooneiswatching; unsigned int numviewers; cluster_t *cluster; sv_t *next; //next proxy->server connection bsp_t *bsp; int numinlines; #ifdef COMMENTARY //audio stuff soundcapt_t *comentrycapture; #endif //options: char server[MAX_QPATH]; int streamid; }; typedef struct { char name[64]; int size; int time, smalltime; } availdemo_t; struct cluster_s { SOCKET qwdsocket; //udp + quakeworld protocols SOCKET tcpsocket; //tcp listening socket (for mvd and listings and stuff) char commandinput[512]; int inputlength; unsigned int mastersendtime; unsigned int mastersequence; unsigned int curtime; viewer_t *viewers; int numviewers; sv_t *servers; int numservers; int nextstreamid; int nextuserid; sv_t *viewserver; //options int qwlistenportnum; int tcplistenportnum; char adminpassword[256];//password required for rcon etc char qtvpassword[256]; //password required to connect a proxy char hostname[256]; char master[MAX_QPATH]; char demodir[MAX_QPATH]; qboolean chokeonnotupdated; qboolean lateforward; qboolean notalking; qboolean nobsp; qboolean allownqclients; //nq clients require no challenge qboolean nouserconnects; //prohibit users from connecting to new streams. int maxviewers; int buildnumber; int numproxies; int maxproxies; qboolean wanttoexit; oproxy_t *pendingproxies; availdemo_t availdemos[2048]; int availdemoscount; }; #define MENU_NONE 0 #define MENU_MAIN 1//MENU_SERVERS #define MENU_SERVERS 2 #define MENU_CLIENTS 3 #define MENU_ADMIN 4 #define MENU_ADMINSERVER 5 #define MENU_DEMOS 6 #define MENU_FORWARDING 7 #define MENU_SERVERBROWSER 8 #define MENU_HELP 9 #define MENU_DEFAULT MENU_MAIN enum { MENU_MAIN_STREAMS, MENU_MAIN_NEWSTREAM, MENU_MAIN_SERVERBROWSER, MENU_MAIN_PREVPROX, MENU_MAIN_HELP, MENU_MAIN_CLIENTLIST, MENU_MAIN_DEMOS, MENU_MAIN_ADMIN, MENU_MAIN_NEXTPROX, MENU_MAIN_ITEMCOUNT }; unsigned char ReadByte(netmsg_t *b); unsigned short ReadShort(netmsg_t *b); unsigned int ReadLong(netmsg_t *b); float ReadFloat(netmsg_t *b); void ReadString(netmsg_t *b, char *string, int maxlen); unsigned int SwapLong(unsigned int val); unsigned int BigLong(unsigned int val); //flags for where a message can be sent, for easy broadcasting #define Q1 (NQ|QW) #define QW 1 #define NQ 2 #define CONNECTING 4 #include "cmd.h" void InitNetMsg(netmsg_t *b, void *buffer, int bufferlength); unsigned char ReadByte(netmsg_t *b); unsigned short ReadShort(netmsg_t *b); unsigned int ReadLong(netmsg_t *b); float ReadFloat(netmsg_t *b); void ReadString(netmsg_t *b, char *string, int maxlen); void WriteByte(netmsg_t *b, unsigned char c); void WriteShort(netmsg_t *b, unsigned short l); void WriteLong(netmsg_t *b, unsigned int l); void WriteFloat(netmsg_t *b, float f); void WriteString2(netmsg_t *b, const char *str); void WriteString(netmsg_t *b, const char *str); void WriteData(netmsg_t *b, const void *data, int length); void Multicast(sv_t *tv, void *buffer, int length, int to, unsigned int playermask,int suitablefor); void Broadcast(cluster_t *cluster, void *buffer, int length, int suitablefor); void ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask); void BuildServerData(sv_t *tv, netmsg_t *msg, int servercount, viewer_t *spectatorflag); void BuildNQServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount); SOCKET QW_InitUDPSocket(int port); 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); void QTV_Shutdown(sv_t *qtv); qboolean NET_StringToAddr (char *s, netadr_t *sadr, int defaultport); void QTV_Printf(sv_t *qtv, char *format, ...); void SendBufferToViewer(viewer_t *v, const char *buffer, int length, qboolean reliable); void QW_PrintfToViewer(viewer_t *v, char *format, ...); void QW_StuffcmdToViewer(viewer_t *v, char *format, ...); void QW_StreamPrint(cluster_t *cluster, sv_t *server, viewer_t *allbut, char *message); void QW_StreamStuffcmd(cluster_t *cluster, sv_t *server, char *fmt, ...); void QTV_SayCommand(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *fullcommand); //execute a command from a view void PM_PlayerMove (pmove_t *pmove); void Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport, qboolean isclient); void Netchan_OutOfBandPrint (cluster_t *cluster, SOCKET sock, netadr_t adr, char *format, ...); int Netchan_IsLocal (netadr_t adr); void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, void *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); qboolean NQNetchan_Process(cluster_t *cluster, netchan_t *chan, netmsg_t *msg); void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const void *data); void Netchan_OutOfBand (cluster_t *cluster, SOCKET sock, netadr_t adr, int length, void *data); qboolean Netchan_CanPacket (netchan_t *chan); 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 thisplayer); 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); unsigned int BSP_Checksum(bsp_t *bsp); 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); const intermission_t *BSP_IntermissionSpot(bsp_t *bsp); void QW_SetViewersServer(cluster_t *cluster, viewer_t *viewer, sv_t *sv); unsigned short QCRC_Block (void *start, int count); unsigned short QCRC_Value(unsigned short crcvalue); void WriteDeltaUsercmd (netmsg_t *m, const usercmd_t *from, usercmd_t *move); void SendClientCommand(sv_t *qtv, char *fmt, ...); void QTV_Run(sv_t *qtv); void QW_FreeViewer(cluster_t *cluster, viewer_t *viewer); void QW_SetMenu(viewer_t *v, int menunum); 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 ReadDeltaUsercmd (netmsg_t *m, const usercmd_t *from, usercmd_t *move); unsigned Com_BlockChecksum (void *buffer, int length); void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf); void Cluster_BuildAvailableDemoList(cluster_t *cluster); void Sys_Printf(cluster_t *cluster, char *fmt, ...); void Net_ProxySend(cluster_t *cluster, oproxy_t *prox, void *buffer, int length); oproxy_t *Net_FileProxy(sv_t *qtv, char *filename); sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, char *password, qboolean force, qboolean autoclose, qboolean noduplicates, qboolean query); SOCKET Net_MVDListen(int port); qboolean Net_StopFileProxy(sv_t *qtv); void SV_FindProxies(SOCKET sock, cluster_t *cluster, sv_t *defaultqtv); qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend); void SV_ForwardStream(sv_t *qtv, void *buffer, int length); unsigned char *FS_ReadFile(char *gamedir, char *filename, unsigned int *size); void ChooseFavoriteTrack(sv_t *tv); void DemoViewer_Update(sv_t *svtest); //httpsv.c void HTTPSV_GetMethod(cluster_t *cluster, oproxy_t *pend); void HTTPSV_PostMethod(cluster_t *cluster, oproxy_t *pend, char *postdata); #ifdef __cplusplus } #endif