123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659 |
- /*
- 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 GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
- #include "qwsvdef.h"
- quakeparms_t host_parms;
- qboolean host_initialized; // true if into command execution (compatability)
- double host_frametime;
- double realtime; // without any filtering or bounding
- int host_hunklevel;
- netadr_t master_adr[MAX_MASTERS]; // address of group servers
- client_t *host_client; // current client
- cvar_t sv_mintic = {"sv_mintic","0.03"}; // bound the size of the
- cvar_t sv_maxtic = {"sv_maxtic","0.1"}; // physics time tic
- cvar_t developer = {"developer","0"}; // show extra messages
- cvar_t timeout = {"timeout","65"}; // seconds without any message
- cvar_t zombietime = {"zombietime", "2"}; // seconds to sink messages
- // after disconnect
- cvar_t rcon_password = {"rcon_password", ""}; // password for remote server commands
- cvar_t password = {"password", ""}; // password for entering the game
- cvar_t spectator_password = {"spectator_password", ""}; // password for entering as a sepctator
- cvar_t allow_download = {"allow_download", "1"};
- cvar_t allow_download_skins = {"allow_download_skins", "1"};
- cvar_t allow_download_models = {"allow_download_models", "1"};
- cvar_t allow_download_sounds = {"allow_download_sounds", "1"};
- cvar_t allow_download_maps = {"allow_download_maps", "1"};
- cvar_t sv_highchars = {"sv_highchars", "1"};
- cvar_t sv_phs = {"sv_phs", "1"};
- cvar_t pausable = {"pausable", "1"};
- //
- // game rules mirrored in svs.info
- //
- cvar_t fraglimit = {"fraglimit","0",false,true};
- cvar_t timelimit = {"timelimit","0",false,true};
- cvar_t teamplay = {"teamplay","0",false,true};
- cvar_t samelevel = {"samelevel","0", false, true};
- cvar_t maxclients = {"maxclients","8", false, true};
- cvar_t maxspectators = {"maxspectators","8", false, true};
- cvar_t deathmatch = {"deathmatch","1", false, true}; // 0, 1, or 2
- cvar_t spawn = {"spawn","0", false, true};
- cvar_t watervis = {"watervis", "0", false, true};
- cvar_t hostname = {"hostname","unnamed", false, true};
- FILE *sv_logfile;
- FILE *sv_fraglogfile;
- void SV_AcceptClient (netadr_t adr, int userid, char *userinfo);
- void Master_Shutdown (void);
- //============================================================================
- qboolean ServerPaused(void)
- {
- return sv.paused;
- }
- /*
- ================
- SV_Shutdown
- Quake calls this before calling Sys_Quit or Sys_Error
- ================
- */
- void SV_Shutdown (void)
- {
- Master_Shutdown ();
- if (sv_logfile)
- {
- fclose (sv_logfile);
- sv_logfile = NULL;
- }
- if (sv_fraglogfile)
- {
- fclose (sv_fraglogfile);
- sv_logfile = NULL;
- }
- NET_Shutdown ();
- }
- /*
- ================
- SV_Error
- Sends a datagram to all the clients informing them of the server crash,
- then exits
- ================
- */
- void SV_Error (char *error, ...)
- {
- va_list argptr;
- static char string[1024];
- static qboolean inerror = false;
- if (inerror)
- Sys_Error ("SV_Error: recursively entered (%s)", string);
- inerror = true;
- va_start (argptr,error);
- vsprintf (string,error,argptr);
- va_end (argptr);
- Con_Printf ("SV_Error: %s\n",string);
- SV_FinalMessage (va("server crashed: %s\n", string));
-
- SV_Shutdown ();
- Sys_Error ("SV_Error: %s\n",string);
- }
- /*
- ==================
- SV_FinalMessage
- Used by SV_Error and SV_Quit_f to send a final message to all connected
- clients before the server goes down. The messages are sent immediately,
- not just stuck on the outgoing message list, because the server is going
- to totally exit after returning from this function.
- ==================
- */
- void SV_FinalMessage (char *message)
- {
- int i;
- client_t *cl;
-
- SZ_Clear (&net_message);
- MSG_WriteByte (&net_message, svc_print);
- MSG_WriteByte (&net_message, PRINT_HIGH);
- MSG_WriteString (&net_message, message);
- MSG_WriteByte (&net_message, svc_disconnect);
- for (i=0, cl = svs.clients ; i<MAX_CLIENTS ; i++, cl++)
- if (cl->state >= cs_spawned)
- Netchan_Transmit (&cl->netchan, net_message.cursize
- , net_message.data);
- }
- /*
- =====================
- SV_DropClient
- Called when the player is totally leaving the server, either willingly
- or unwillingly. This is NOT called if the entire server is quiting
- or crashing.
- =====================
- */
- void SV_DropClient (client_t *drop)
- {
- // add the disconnect
- MSG_WriteByte (&drop->netchan.message, svc_disconnect);
- if (drop->state == cs_spawned)
- if (!drop->spectator)
- {
- // call the prog function for removing a client
- // this will set the body to a dead frame, among other things
- pr_global_struct->self = EDICT_TO_PROG(drop->edict);
- PR_ExecuteProgram (pr_global_struct->ClientDisconnect);
- }
- else if (SpectatorDisconnect)
- {
- // call the prog function for removing a client
- // this will set the body to a dead frame, among other things
- pr_global_struct->self = EDICT_TO_PROG(drop->edict);
- PR_ExecuteProgram (SpectatorDisconnect);
- }
- if (drop->spectator)
- Con_Printf ("Spectator %s removed\n",drop->name);
- else
- Con_Printf ("Client %s removed\n",drop->name);
- if (drop->download)
- {
- fclose (drop->download);
- drop->download = NULL;
- }
- if (drop->upload)
- {
- fclose (drop->upload);
- drop->upload = NULL;
- }
- *drop->uploadfn = 0;
- drop->state = cs_zombie; // become free in a few seconds
- drop->connection_started = realtime; // for zombie timeout
- drop->old_frags = 0;
- drop->edict->v.frags = 0;
- drop->name[0] = 0;
- memset (drop->userinfo, 0, sizeof(drop->userinfo));
- // send notification to all remaining clients
- SV_FullClientUpdate (drop, &sv.reliable_datagram);
- }
- //====================================================================
- /*
- ===================
- SV_CalcPing
- ===================
- */
- int SV_CalcPing (client_t *cl)
- {
- float ping;
- int i;
- int count;
- register client_frame_t *frame;
- ping = 0;
- count = 0;
- for (frame = cl->frames, i=0 ; i<UPDATE_BACKUP ; i++, frame++)
- {
- if (frame->ping_time > 0)
- {
- ping += frame->ping_time;
- count++;
- }
- }
- if (!count)
- return 9999;
- ping /= count;
- return ping*1000;
- }
- /*
- ===================
- SV_FullClientUpdate
- Writes all update values to a sizebuf
- ===================
- */
- void SV_FullClientUpdate (client_t *client, sizebuf_t *buf)
- {
- int i;
- char info[MAX_INFO_STRING];
- i = client - svs.clients;
- //Sys_Printf("SV_FullClientUpdate: Updated frags for client %d\n", i);
- MSG_WriteByte (buf, svc_updatefrags);
- MSG_WriteByte (buf, i);
- MSG_WriteShort (buf, client->old_frags);
-
- MSG_WriteByte (buf, svc_updateping);
- MSG_WriteByte (buf, i);
- MSG_WriteShort (buf, SV_CalcPing (client));
-
- MSG_WriteByte (buf, svc_updatepl);
- MSG_WriteByte (buf, i);
- MSG_WriteByte (buf, client->lossage);
-
- MSG_WriteByte (buf, svc_updateentertime);
- MSG_WriteByte (buf, i);
- MSG_WriteFloat (buf, realtime - client->connection_started);
- strcpy (info, client->userinfo);
- Info_RemovePrefixedKeys (info, '_'); // server passwords, etc
- MSG_WriteByte (buf, svc_updateuserinfo);
- MSG_WriteByte (buf, i);
- MSG_WriteLong (buf, client->userid);
- MSG_WriteString (buf, info);
- }
- /*
- ===================
- SV_FullClientUpdateToClient
- Writes all update values to a client's reliable stream
- ===================
- */
- void SV_FullClientUpdateToClient (client_t *client, client_t *cl)
- {
- ClientReliableCheckBlock(cl, 24 + strlen(client->userinfo));
- if (cl->num_backbuf) {
- SV_FullClientUpdate (client, &cl->backbuf);
- ClientReliable_FinishWrite(cl);
- } else
- SV_FullClientUpdate (client, &cl->netchan.message);
- }
- /*
- ==============================================================================
- CONNECTIONLESS COMMANDS
- ==============================================================================
- */
- /*
- ================
- SVC_Status
- Responds with all the info that qplug or qspy can see
- This message can be up to around 5k with worst case string lengths.
- ================
- */
- void SVC_Status (void)
- {
- int i;
- client_t *cl;
- int ping;
- int top, bottom;
- Cmd_TokenizeString ("status");
- SV_BeginRedirect (RD_PACKET);
- Con_Printf ("%s\n", svs.info);
- for (i=0 ; i<MAX_CLIENTS ; i++)
- {
- cl = &svs.clients[i];
- if ((cl->state == cs_connected || cl->state == cs_spawned ) && !cl->spectator)
- {
- top = atoi(Info_ValueForKey (cl->userinfo, "topcolor"));
- bottom = atoi(Info_ValueForKey (cl->userinfo, "bottomcolor"));
- top = (top < 0) ? 0 : ((top > 13) ? 13 : top);
- bottom = (bottom < 0) ? 0 : ((bottom > 13) ? 13 : bottom);
- ping = SV_CalcPing (cl);
- Con_Printf ("%i %i %i %i \"%s\" \"%s\" %i %i\n", cl->userid,
- cl->old_frags, (int)(realtime - cl->connection_started)/60,
- ping, cl->name, Info_ValueForKey (cl->userinfo, "skin"), top, bottom);
- }
- }
- SV_EndRedirect ();
- }
- /*
- ===================
- SV_CheckLog
- ===================
- */
- #define LOG_HIGHWATER 4096
- #define LOG_FLUSH 10*60
- void SV_CheckLog (void)
- {
- sizebuf_t *sz;
- sz = &svs.log[svs.logsequence&1];
- // bump sequence if allmost full, or ten minutes have passed and
- // there is something still sitting there
- if (sz->cursize > LOG_HIGHWATER
- || (realtime - svs.logtime > LOG_FLUSH && sz->cursize) )
- {
- // swap buffers and bump sequence
- svs.logtime = realtime;
- svs.logsequence++;
- sz = &svs.log[svs.logsequence&1];
- sz->cursize = 0;
- Con_Printf ("beginning fraglog sequence %i\n", svs.logsequence);
- }
- }
- /*
- ================
- SVC_Log
- Responds with all the logged frags for ranking programs.
- If a sequence number is passed as a parameter and it is
- the same as the current sequence, an A2A_NACK will be returned
- instead of the data.
- ================
- */
- void SVC_Log (void)
- {
- int seq;
- char data[MAX_DATAGRAM+64];
- if (Cmd_Argc() == 2)
- seq = atoi(Cmd_Argv(1));
- else
- seq = -1;
- if (seq == svs.logsequence-1 || !sv_fraglogfile)
- { // they allready have this data, or we aren't logging frags
- data[0] = A2A_NACK;
- NET_SendPacket (1, data, net_from);
- return;
- }
- Con_DPrintf ("sending log %i to %s\n", svs.logsequence-1, NET_AdrToString(net_from));
- sprintf (data, "stdlog %i\n", svs.logsequence-1);
- strcat (data, (char *)svs.log_buf[((svs.logsequence-1)&1)]);
- NET_SendPacket (strlen(data)+1, data, net_from);
- }
- /*
- ================
- SVC_Ping
- Just responds with an acknowledgement
- ================
- */
- void SVC_Ping (void)
- {
- char data;
- data = A2A_ACK;
- NET_SendPacket (1, &data, net_from);
- }
- /*
- =================
- SVC_GetChallenge
- Returns a challenge number that can be used
- in a subsequent client_connect command.
- We do this to prevent denial of service attacks that
- flood the server with invalid connection IPs. With a
- challenge, they must give a valid IP address.
- =================
- */
- void SVC_GetChallenge (void)
- {
- int i;
- int oldest;
- int oldestTime;
- oldest = 0;
- oldestTime = 0x7fffffff;
- // see if we already have a challenge for this ip
- for (i = 0 ; i < MAX_CHALLENGES ; i++)
- {
- if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr))
- break;
- if (svs.challenges[i].time < oldestTime)
- {
- oldestTime = svs.challenges[i].time;
- oldest = i;
- }
- }
- if (i == MAX_CHALLENGES)
- {
- // overwrite the oldest
- svs.challenges[oldest].challenge = (rand() << 16) ^ rand();
- svs.challenges[oldest].adr = net_from;
- svs.challenges[oldest].time = realtime;
- i = oldest;
- }
- // send it back
- Netchan_OutOfBandPrint (net_from, "%c%i", S2C_CHALLENGE,
- svs.challenges[i].challenge);
- }
- /*
- ==================
- SVC_DirectConnect
- A connection request that did not come from the master
- ==================
- */
- void SVC_DirectConnect (void)
- {
- char userinfo[1024];
- static int userid;
- netadr_t adr;
- int i;
- client_t *cl, *newcl;
- client_t temp;
- edict_t *ent;
- int edictnum;
- char *s;
- int clients, spectators;
- qboolean spectator;
- int qport;
- int version;
- int challenge;
- version = atoi(Cmd_Argv(1));
- if (version != PROTOCOL_VERSION)
- {
- Netchan_OutOfBandPrint (net_from, "%c\nServer is version %4.2f.\n", A2C_PRINT, VERSION);
- Con_Printf ("* rejected connect from version %i\n", version);
- return;
- }
- qport = atoi(Cmd_Argv(2));
- challenge = atoi(Cmd_Argv(3));
- // note an extra byte is needed to replace spectator key
- strncpy (userinfo, Cmd_Argv(4), sizeof(userinfo)-2);
- userinfo[sizeof(userinfo) - 2] = 0;
- // see if the challenge is valid
- for (i=0 ; i<MAX_CHALLENGES ; i++)
- {
- if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr))
- {
- if (challenge == svs.challenges[i].challenge)
- break; // good
- Netchan_OutOfBandPrint (net_from, "%c\nBad challenge.\n", A2C_PRINT);
- return;
- }
- }
- if (i == MAX_CHALLENGES)
- {
- Netchan_OutOfBandPrint (net_from, "%c\nNo challenge for address.\n", A2C_PRINT);
- return;
- }
- // check for password or spectator_password
- s = Info_ValueForKey (userinfo, "spectator");
- if (s[0] && strcmp(s, "0"))
- {
- if (spectator_password.string[0] &&
- stricmp(spectator_password.string, "none") &&
- strcmp(spectator_password.string, s) )
- { // failed
- Con_Printf ("%s:spectator password failed\n", NET_AdrToString (net_from));
- Netchan_OutOfBandPrint (net_from, "%c\nrequires a spectator password\n\n", A2C_PRINT);
- return;
- }
- Info_RemoveKey (userinfo, "spectator"); // remove passwd
- Info_SetValueForStarKey (userinfo, "*spectator", "1", MAX_INFO_STRING);
- spectator = true;
- }
- else
- {
- s = Info_ValueForKey (userinfo, "password");
- if (password.string[0] &&
- stricmp(password.string, "none") &&
- strcmp(password.string, s) )
- {
- Con_Printf ("%s:password failed\n", NET_AdrToString (net_from));
- Netchan_OutOfBandPrint (net_from, "%c\nserver requires a password\n\n", A2C_PRINT);
- return;
- }
- spectator = false;
- Info_RemoveKey (userinfo, "password"); // remove passwd
- }
- adr = net_from;
- userid++; // so every client gets a unique id
- newcl = &temp;
- memset (newcl, 0, sizeof(client_t));
- newcl->userid = userid;
- // works properly
- if (!sv_highchars.value) {
- byte *p, *q;
- for (p = (byte *)newcl->userinfo, q = (byte *)userinfo;
- *q && p < (byte *)newcl->userinfo + sizeof(newcl->userinfo)-1; q++)
- if (*q > 31 && *q <= 127)
- *p++ = *q;
- } else
- strncpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)-1);
- // if there is allready a slot for this ip, drop it
- for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
- {
- if (cl->state == cs_free)
- continue;
- if (NET_CompareBaseAdr (adr, cl->netchan.remote_address)
- && ( cl->netchan.qport == qport
- || adr.port == cl->netchan.remote_address.port ))
- {
- if (cl->state == cs_connected) {
- Con_Printf("%s:dup connect\n", NET_AdrToString (adr));
- userid--;
- return;
- }
- Con_Printf ("%s:reconnect\n", NET_AdrToString (adr));
- SV_DropClient (cl);
- break;
- }
- }
- // count up the clients and spectators
- clients = 0;
- spectators = 0;
- for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
- {
- if (cl->state == cs_free)
- continue;
- if (cl->spectator)
- spectators++;
- else
- clients++;
- }
- // if at server limits, refuse connection
- if ( maxclients.value > MAX_CLIENTS )
- Cvar_SetValue ("maxclients", MAX_CLIENTS);
- if (maxspectators.value > MAX_CLIENTS)
- Cvar_SetValue ("maxspectators", MAX_CLIENTS);
- if (maxspectators.value + maxclients.value > MAX_CLIENTS)
- Cvar_SetValue ("maxspectators", MAX_CLIENTS - maxspectators.value + maxclients.value);
- if ( (spectator && spectators >= (int)maxspectators.value)
- || (!spectator && clients >= (int)maxclients.value) )
- {
- Con_Printf ("%s:full connect\n", NET_AdrToString (adr));
- Netchan_OutOfBandPrint (adr, "%c\nserver is full\n\n", A2C_PRINT);
- return;
- }
- // find a client slot
- newcl = NULL;
- for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
- {
- if (cl->state == cs_free)
- {
- newcl = cl;
- break;
- }
- }
- if (!newcl)
- {
- Con_Printf ("WARNING: miscounted available clients\n");
- return;
- }
-
- // build a new connection
- // accept the new client
- // this is the only place a client_t is ever initialized
- *newcl = temp;
- Netchan_OutOfBandPrint (adr, "%c", S2C_CONNECTION );
- edictnum = (newcl-svs.clients)+1;
-
- Netchan_Setup (&newcl->netchan , adr, qport);
- newcl->state = cs_connected;
- newcl->datagram.allowoverflow = true;
- newcl->datagram.data = newcl->datagram_buf;
- newcl->datagram.maxsize = sizeof(newcl->datagram_buf);
- // spectator mode can ONLY be set at join time
- newcl->spectator = spectator;
- ent = EDICT_NUM(edictnum);
- newcl->edict = ent;
-
- // parse some info from the info strings
- SV_ExtractFromUserinfo (newcl);
- // JACK: Init the floodprot stuff.
- for (i=0; i<10; i++)
- newcl->whensaid[i] = 0.0;
- newcl->whensaidhead = 0;
- newcl->lockedtill = 0;
- // call the progs to get default spawn parms for the new client
- PR_ExecuteProgram (pr_global_struct->SetNewParms);
- for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
- newcl->spawn_parms[i] = (&pr_global_struct->parm1)[i];
- if (newcl->spectator)
- Con_Printf ("Spectator %s connected\n", newcl->name);
- else
- Con_DPrintf ("Client %s connected\n", newcl->name);
- newcl->sendinfo = true;
- }
- int Rcon_Validate (void)
- {
- if (!strlen (rcon_password.string))
- return 0;
- if (strcmp (Cmd_Argv(1), rcon_password.string) )
- return 0;
- return 1;
- }
- /*
- ===============
- SVC_RemoteCommand
- A client issued an rcon command.
- Shift down the remaining args
- Redirect all printfs
- ===============
- */
- void SVC_RemoteCommand (void)
- {
- int i;
- char remaining[1024];
- if (!Rcon_Validate ()) {
- Con_Printf ("Bad rcon from %s:\n%s\n"
- , NET_AdrToString (net_from), net_message.data+4);
- SV_BeginRedirect (RD_PACKET);
- Con_Printf ("Bad rcon_password.\n");
- } else {
- Con_Printf ("Rcon from %s:\n%s\n"
- , NET_AdrToString (net_from), net_message.data+4);
- SV_BeginRedirect (RD_PACKET);
- remaining[0] = 0;
- for (i=2 ; i<Cmd_Argc() ; i++)
- {
- strcat (remaining, Cmd_Argv(i) );
- strcat (remaining, " ");
- }
- Cmd_ExecuteString (remaining);
- }
- SV_EndRedirect ();
- }
- /*
- =================
- SV_ConnectionlessPacket
- A connectionless packet has four leading 0xff
- characters to distinguish it from a game channel.
- Clients that are in the game can still send
- connectionless packets.
- =================
- */
- void SV_ConnectionlessPacket (void)
- {
- char *s;
- char *c;
- MSG_BeginReading ();
- MSG_ReadLong (); // skip the -1 marker
- s = MSG_ReadStringLine ();
- Cmd_TokenizeString (s);
- c = Cmd_Argv(0);
- if (!strcmp(c, "ping") || ( c[0] == A2A_PING && (c[1] == 0 || c[1] == '\n')) )
- {
- SVC_Ping ();
- return;
- }
- if (c[0] == A2A_ACK && (c[1] == 0 || c[1] == '\n') )
- {
- Con_Printf ("A2A_ACK from %s\n", NET_AdrToString (net_from));
- return;
- }
- else if (!strcmp(c,"status"))
- {
- SVC_Status ();
- return;
- }
- else if (!strcmp(c,"log"))
- {
- SVC_Log ();
- return;
- }
- else if (!strcmp(c,"connect"))
- {
- SVC_DirectConnect ();
- return;
- }
- else if (!strcmp(c,"getchallenge"))
- {
- SVC_GetChallenge ();
- return;
- }
- else if (!strcmp(c, "rcon"))
- SVC_RemoteCommand ();
- else
- Con_Printf ("bad connectionless packet from %s:\n%s\n"
- , NET_AdrToString (net_from), s);
- }
- /*
- ==============================================================================
- PACKET FILTERING
-
- You can add or remove addresses from the filter list with:
- addip <ip>
- removeip <ip>
- The ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire class C network with "addip 192.246.40".
- Removeip will only remove an address specified exactly the same way. You cannot addip a subnet, then removeip a single host.
- listip
- Prints the current list of filters.
- writeip
- Dumps "addip <ip>" commands to listip.cfg so it can be execed at a later date. The filter lists are not saved and restored by default, because I beleive it would cause too much confusion.
- filterban <0 or 1>
- If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game. This is the default setting.
- If 0, then only addresses matching the list will be allowed. This lets you easily set up a private game, or a game that only allows players from your local network.
- ==============================================================================
- */
- typedef struct
- {
- unsigned mask;
- unsigned compare;
- } ipfilter_t;
- #define MAX_IPFILTERS 1024
- ipfilter_t ipfilters[MAX_IPFILTERS];
- int numipfilters;
- cvar_t filterban = {"filterban", "1"};
- /*
- =================
- StringToFilter
- =================
- */
- qboolean StringToFilter (char *s, ipfilter_t *f)
- {
- char num[128];
- int i, j;
- byte b[4];
- byte m[4];
-
- for (i=0 ; i<4 ; i++)
- {
- b[i] = 0;
- m[i] = 0;
- }
-
- for (i=0 ; i<4 ; i++)
- {
- if (*s < '0' || *s > '9')
- {
- Con_Printf ("Bad filter address: %s\n", s);
- return false;
- }
-
- j = 0;
- while (*s >= '0' && *s <= '9')
- {
- num[j++] = *s++;
- }
- num[j] = 0;
- b[i] = atoi(num);
- if (b[i] != 0)
- m[i] = 255;
- if (!*s)
- break;
- s++;
- }
-
- f->mask = *(unsigned *)m;
- f->compare = *(unsigned *)b;
-
- return true;
- }
- /*
- =================
- SV_AddIP_f
- =================
- */
- void SV_AddIP_f (void)
- {
- int i;
-
- for (i=0 ; i<numipfilters ; i++)
- if (ipfilters[i].compare == 0xffffffff)
- break; // free spot
- if (i == numipfilters)
- {
- if (numipfilters == MAX_IPFILTERS)
- {
- Con_Printf ("IP filter list is full\n");
- return;
- }
- numipfilters++;
- }
-
- if (!StringToFilter (Cmd_Argv(1), &ipfilters[i]))
- ipfilters[i].compare = 0xffffffff;
- }
- /*
- =================
- SV_RemoveIP_f
- =================
- */
- void SV_RemoveIP_f (void)
- {
- ipfilter_t f;
- int i, j;
- if (!StringToFilter (Cmd_Argv(1), &f))
- return;
- for (i=0 ; i<numipfilters ; i++)
- if (ipfilters[i].mask == f.mask
- && ipfilters[i].compare == f.compare)
- {
- for (j=i+1 ; j<numipfilters ; j++)
- ipfilters[j-1] = ipfilters[j];
- numipfilters--;
- Con_Printf ("Removed.\n");
- return;
- }
- Con_Printf ("Didn't find %s.\n", Cmd_Argv(1));
- }
- /*
- =================
- SV_ListIP_f
- =================
- */
- void SV_ListIP_f (void)
- {
- int i;
- byte b[4];
- Con_Printf ("Filter list:\n");
- for (i=0 ; i<numipfilters ; i++)
- {
- *(unsigned *)b = ipfilters[i].compare;
- Con_Printf ("%3i.%3i.%3i.%3i\n", b[0], b[1], b[2], b[3]);
- }
- }
- /*
- =================
- SV_WriteIP_f
- =================
- */
- void SV_WriteIP_f (void)
- {
- FILE *f;
- char name[MAX_OSPATH];
- byte b[4];
- int i;
- sprintf (name, "%s/listip.cfg", com_gamedir);
- Con_Printf ("Writing %s.\n", name);
- f = fopen (name, "wb");
- if (!f)
- {
- Con_Printf ("Couldn't open %s\n", name);
- return;
- }
-
- for (i=0 ; i<numipfilters ; i++)
- {
- *(unsigned *)b = ipfilters[i].compare;
- fprintf (f, "addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]);
- }
-
- fclose (f);
- }
- /*
- =================
- SV_SendBan
- =================
- */
- void SV_SendBan (void)
- {
- char data[128];
- data[0] = data[1] = data[2] = data[3] = 0xff;
- data[4] = A2C_PRINT;
- data[5] = 0;
- strcat (data, "\nbanned.\n");
-
- NET_SendPacket (strlen(data), data, net_from);
- }
- /*
- =================
- SV_FilterPacket
- =================
- */
- qboolean SV_FilterPacket (void)
- {
- int i;
- unsigned in;
-
- in = *(unsigned *)net_from.ip;
- for (i=0 ; i<numipfilters ; i++)
- if ( (in & ipfilters[i].mask) == ipfilters[i].compare)
- return filterban.value;
- return !filterban.value;
- }
- //============================================================================
- /*
- =================
- SV_ReadPackets
- =================
- */
- void SV_ReadPackets (void)
- {
- int i;
- client_t *cl;
- qboolean good;
- int qport;
- good = false;
- while (NET_GetPacket ())
- {
- if (SV_FilterPacket ())
- {
- SV_SendBan (); // tell them we aren't listening...
- continue;
- }
- // check for connectionless packet (0xffffffff) first
- if (*(int *)net_message.data == -1)
- {
- SV_ConnectionlessPacket ();
- continue;
- }
-
- // read the qport out of the message so we can fix up
- // stupid address translating routers
- MSG_BeginReading ();
- MSG_ReadLong (); // sequence number
- MSG_ReadLong (); // sequence number
- qport = MSG_ReadShort () & 0xffff;
- // check for packets from connected clients
- for (i=0, cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
- {
- if (cl->state == cs_free)
- continue;
- if (!NET_CompareBaseAdr (net_from, cl->netchan.remote_address))
- continue;
- if (cl->netchan.qport != qport)
- continue;
- if (cl->netchan.remote_address.port != net_from.port)
- {
- Con_DPrintf ("SV_ReadPackets: fixing up a translated port\n");
- cl->netchan.remote_address.port = net_from.port;
- }
- if (Netchan_Process(&cl->netchan))
- { // this is a valid, sequenced packet, so process it
- svs.stats.packets++;
- good = true;
- cl->send_message = true; // reply at end of frame
- if (cl->state != cs_zombie)
- SV_ExecuteClientMessage (cl);
- }
- break;
- }
-
- if (i != MAX_CLIENTS)
- continue;
-
- // packet is not from a known client
- // Con_Printf ("%s:sequenced packet without connection\n"
- // ,NET_AdrToString(net_from));
- }
- }
- /*
- ==================
- SV_CheckTimeouts
- If a packet has not been received from a client in timeout.value
- seconds, drop the conneciton.
- When a client is normally dropped, the client_t goes into a zombie state
- for a few seconds to make sure any final reliable message gets resent
- if necessary
- ==================
- */
- void SV_CheckTimeouts (void)
- {
- int i;
- client_t *cl;
- float droptime;
- int nclients;
-
- droptime = realtime - timeout.value;
- nclients = 0;
- for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
- {
- if (cl->state == cs_connected || cl->state == cs_spawned) {
- if (!cl->spectator)
- nclients++;
- if (cl->netchan.last_received < droptime) {
- SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name);
- SV_DropClient (cl);
- cl->state = cs_free; // don't bother with zombie state
- }
- }
- if (cl->state == cs_zombie &&
- realtime - cl->connection_started > zombietime.value)
- {
- cl->state = cs_free; // can now be reused
- }
- }
- if (sv.paused && !nclients) {
- // nobody left, unpause the server
- SV_TogglePause("Pause released since no players are left.\n");
- }
- }
- /*
- ===================
- SV_GetConsoleCommands
- Add them exactly as if they had been typed at the console
- ===================
- */
- void SV_GetConsoleCommands (void)
- {
- char *cmd;
- while (1)
- {
- cmd = Sys_ConsoleInput ();
- if (!cmd)
- break;
- Cbuf_AddText (cmd);
- }
- }
- /*
- ===================
- SV_CheckVars
- ===================
- */
- void SV_CheckVars (void)
- {
- static char *pw, *spw;
- int v;
- if (password.string == pw && spectator_password.string == spw)
- return;
- pw = password.string;
- spw = spectator_password.string;
- v = 0;
- if (pw && pw[0] && strcmp(pw, "none"))
- v |= 1;
- if (spw && spw[0] && strcmp(spw, "none"))
- v |= 2;
- Con_Printf ("Updated needpass.\n");
- if (!v)
- Info_SetValueForKey (svs.info, "needpass", "", MAX_SERVERINFO_STRING);
- else
- Info_SetValueForKey (svs.info, "needpass", va("%i",v), MAX_SERVERINFO_STRING);
- }
- /*
- ==================
- SV_Frame
- ==================
- */
- void SV_Frame (float time)
- {
- static double start, end;
-
- start = Sys_DoubleTime ();
- svs.stats.idle += start - end;
-
- // keep the random time dependent
- rand ();
- // decide the simulation time
- if (!sv.paused) {
- realtime += time;
- sv.time += time;
- }
- // check timeouts
- SV_CheckTimeouts ();
- // toggle the log buffer if full
- SV_CheckLog ();
- // move autonomous things around if enough time has passed
- if (!sv.paused)
- SV_Physics ();
- // get packets
- SV_ReadPackets ();
- // check for commands typed to the host
- SV_GetConsoleCommands ();
-
- // process console commands
- Cbuf_Execute ();
- SV_CheckVars ();
- // send messages back to the clients that had packets read this frame
- SV_SendClientMessages ();
- // send a heartbeat to the master if needed
- Master_Heartbeat ();
- // collect timing statistics
- end = Sys_DoubleTime ();
- svs.stats.active += end-start;
- if (++svs.stats.count == STATFRAMES)
- {
- svs.stats.latched_active = svs.stats.active;
- svs.stats.latched_idle = svs.stats.idle;
- svs.stats.latched_packets = svs.stats.packets;
- svs.stats.active = 0;
- svs.stats.idle = 0;
- svs.stats.packets = 0;
- svs.stats.count = 0;
- }
- }
- /*
- ===============
- SV_InitLocal
- ===============
- */
- void SV_InitLocal (void)
- {
- int i;
- extern cvar_t sv_maxvelocity;
- extern cvar_t sv_gravity;
- extern cvar_t sv_aim;
- extern cvar_t sv_stopspeed;
- extern cvar_t sv_spectatormaxspeed;
- extern cvar_t sv_accelerate;
- extern cvar_t sv_airaccelerate;
- extern cvar_t sv_wateraccelerate;
- extern cvar_t sv_friction;
- extern cvar_t sv_waterfriction;
- SV_InitOperatorCommands ();
- SV_UserInit ();
-
- Cvar_RegisterVariable (&rcon_password);
- Cvar_RegisterVariable (&password);
- Cvar_RegisterVariable (&spectator_password);
- Cvar_RegisterVariable (&sv_mintic);
- Cvar_RegisterVariable (&sv_maxtic);
- Cvar_RegisterVariable (&fraglimit);
- Cvar_RegisterVariable (&timelimit);
- Cvar_RegisterVariable (&teamplay);
- Cvar_RegisterVariable (&samelevel);
- Cvar_RegisterVariable (&maxclients);
- Cvar_RegisterVariable (&maxspectators);
- Cvar_RegisterVariable (&hostname);
- Cvar_RegisterVariable (&deathmatch);
- Cvar_RegisterVariable (&spawn);
- Cvar_RegisterVariable (&watervis);
- Cvar_RegisterVariable (&developer);
- Cvar_RegisterVariable (&timeout);
- Cvar_RegisterVariable (&zombietime);
- Cvar_RegisterVariable (&sv_maxvelocity);
- Cvar_RegisterVariable (&sv_gravity);
- Cvar_RegisterVariable (&sv_stopspeed);
- Cvar_RegisterVariable (&sv_maxspeed);
- Cvar_RegisterVariable (&sv_spectatormaxspeed);
- Cvar_RegisterVariable (&sv_accelerate);
- Cvar_RegisterVariable (&sv_airaccelerate);
- Cvar_RegisterVariable (&sv_wateraccelerate);
- Cvar_RegisterVariable (&sv_friction);
- Cvar_RegisterVariable (&sv_waterfriction);
- Cvar_RegisterVariable (&sv_aim);
- Cvar_RegisterVariable (&filterban);
-
- Cvar_RegisterVariable (&allow_download);
- Cvar_RegisterVariable (&allow_download_skins);
- Cvar_RegisterVariable (&allow_download_models);
- Cvar_RegisterVariable (&allow_download_sounds);
- Cvar_RegisterVariable (&allow_download_maps);
- Cvar_RegisterVariable (&sv_highchars);
- Cvar_RegisterVariable (&sv_phs);
- Cvar_RegisterVariable (&pausable);
- Cmd_AddCommand ("addip", SV_AddIP_f);
- Cmd_AddCommand ("removeip", SV_RemoveIP_f);
- Cmd_AddCommand ("listip", SV_ListIP_f);
- Cmd_AddCommand ("writeip", SV_WriteIP_f);
- for (i=0 ; i<MAX_MODELS ; i++)
- sprintf (localmodels[i], "*%i", i);
- Info_SetValueForStarKey (svs.info, "*version", va("%4.2f", VERSION), MAX_SERVERINFO_STRING);
- // init fraglog stuff
- svs.logsequence = 1;
- svs.logtime = realtime;
- svs.log[0].data = svs.log_buf[0];
- svs.log[0].maxsize = sizeof(svs.log_buf[0]);
- svs.log[0].cursize = 0;
- svs.log[0].allowoverflow = true;
- svs.log[1].data = svs.log_buf[1];
- svs.log[1].maxsize = sizeof(svs.log_buf[1]);
- svs.log[1].cursize = 0;
- svs.log[1].allowoverflow = true;
- }
- //============================================================================
- /*
- ================
- Master_Heartbeat
- Send a message to the master every few minutes to
- let it know we are alive, and log information
- ================
- */
- #define HEARTBEAT_SECONDS 300
- void Master_Heartbeat (void)
- {
- char string[2048];
- int active;
- int i;
- if (realtime - svs.last_heartbeat < HEARTBEAT_SECONDS)
- return; // not time to send yet
- svs.last_heartbeat = realtime;
- //
- // count active users
- //
- active = 0;
- for (i=0 ; i<MAX_CLIENTS ; i++)
- if (svs.clients[i].state == cs_connected ||
- svs.clients[i].state == cs_spawned )
- active++;
- svs.heartbeat_sequence++;
- sprintf (string, "%c\n%i\n%i\n", S2M_HEARTBEAT,
- svs.heartbeat_sequence, active);
- // send to group master
- for (i=0 ; i<MAX_MASTERS ; i++)
- if (master_adr[i].port)
- {
- Con_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i]));
- NET_SendPacket (strlen(string), string, master_adr[i]);
- }
- }
- /*
- =================
- Master_Shutdown
- Informs all masters that this server is going down
- =================
- */
- void Master_Shutdown (void)
- {
- char string[2048];
- int i;
- sprintf (string, "%c\n", S2M_SHUTDOWN);
- // send to group master
- for (i=0 ; i<MAX_MASTERS ; i++)
- if (master_adr[i].port)
- {
- Con_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i]));
- NET_SendPacket (strlen(string), string, master_adr[i]);
- }
- }
- /*
- =================
- SV_ExtractFromUserinfo
- Pull specific info from a newly changed userinfo string
- into a more C freindly form.
- =================
- */
- void SV_ExtractFromUserinfo (client_t *cl)
- {
- char *val, *p, *q;
- int i;
- client_t *client;
- int dupc = 1;
- char newname[80];
- // name for C code
- val = Info_ValueForKey (cl->userinfo, "name");
- // trim user name
- strncpy(newname, val, sizeof(newname) - 1);
- newname[sizeof(newname) - 1] = 0;
- for (p = newname; (*p == ' ' || *p == '\r' || *p == '\n') && *p; p++)
- ;
- if (p != newname && !*p) {
- //white space only
- strcpy(newname, "unnamed");
- p = newname;
- }
- if (p != newname && *p) {
- for (q = newname; *p; *q++ = *p++)
- ;
- *q = 0;
- }
- for (p = newname + strlen(newname) - 1; p != newname && (*p == ' ' || *p == '\r' || *p == '\n') ; p--)
- ;
- p[1] = 0;
- if (strcmp(val, newname)) {
- Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING);
- val = Info_ValueForKey (cl->userinfo, "name");
- }
- if (!val[0] || !stricmp(val, "console")) {
- Info_SetValueForKey (cl->userinfo, "name", "unnamed", MAX_INFO_STRING);
- val = Info_ValueForKey (cl->userinfo, "name");
- }
- // check to see if another user by the same name exists
- while (1) {
- for (i=0, client = svs.clients ; i<MAX_CLIENTS ; i++, client++) {
- if (client->state != cs_spawned || client == cl)
- continue;
- if (!stricmp(client->name, val))
- break;
- }
- if (i != MAX_CLIENTS) { // dup name
- if (strlen(val) > sizeof(cl->name) - 1)
- val[sizeof(cl->name) - 4] = 0;
- p = val;
- if (val[0] == '(')
- if (val[2] == ')')
- p = val + 3;
- else if (val[3] == ')')
- p = val + 4;
- sprintf(newname, "(%d)%-.40s", dupc++, p);
- Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING);
- val = Info_ValueForKey (cl->userinfo, "name");
- } else
- break;
- }
-
- if (strncmp(val, cl->name, strlen(cl->name))) {
- if (!sv.paused) {
- if (!cl->lastnametime || realtime - cl->lastnametime > 5) {
- cl->lastnamecount = 0;
- cl->lastnametime = realtime;
- } else if (cl->lastnamecount++ > 4) {
- SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked for name spam\n", cl->name);
- SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the game for name spamming\n");
- SV_DropClient (cl);
- return;
- }
- }
-
- if (cl->state >= cs_spawned && !cl->spectator)
- SV_BroadcastPrintf (PRINT_HIGH, "%s changed name to %s\n", cl->name, val);
- }
- strncpy (cl->name, val, sizeof(cl->name)-1);
- // rate command
- val = Info_ValueForKey (cl->userinfo, "rate");
- if (strlen(val))
- {
- i = atoi(val);
- if (i < 500)
- i = 500;
- if (i > 10000)
- i = 10000;
- cl->netchan.rate = 1.0/i;
- }
- // msg command
- val = Info_ValueForKey (cl->userinfo, "msg");
- if (strlen(val))
- {
- cl->messagelevel = atoi(val);
- }
- }
- //============================================================================
- /*
- ====================
- SV_InitNet
- ====================
- */
- void SV_InitNet (void)
- {
- int port;
- int p;
- port = PORT_SERVER;
- p = COM_CheckParm ("-port");
- if (p && p < com_argc)
- {
- port = atoi(com_argv[p+1]);
- Con_Printf ("Port: %i\n", port);
- }
- NET_Init (port);
- Netchan_Init ();
- // heartbeats will allways be sent to the id master
- svs.last_heartbeat = -99999; // send immediately
- // NET_StringToAdr ("192.246.40.70:27000", &idmaster_adr);
- }
- /*
- ====================
- SV_Init
- ====================
- */
- void SV_Init (quakeparms_t *parms)
- {
- COM_InitArgv (parms->argc, parms->argv);
- COM_AddParm ("-game");
- COM_AddParm ("qw");
- if (COM_CheckParm ("-minmemory"))
- parms->memsize = MINIMUM_MEMORY;
- host_parms = *parms;
- if (parms->memsize < MINIMUM_MEMORY)
- SV_Error ("Only %4.1f megs of memory reported, can't execute game", parms->memsize / (float)0x100000);
- Memory_Init (parms->membase, parms->memsize);
- Cbuf_Init ();
- Cmd_Init ();
- COM_Init ();
-
- PR_Init ();
- Mod_Init ();
- SV_InitNet ();
- SV_InitLocal ();
- Sys_Init ();
- Pmove_Init ();
- Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
- host_hunklevel = Hunk_LowMark ();
- Cbuf_InsertText ("exec server.cfg\n");
- host_initialized = true;
-
- Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
- Con_Printf ("%4.1f megabyte heap\n",parms->memsize/ (1024*1024.0));
- Con_Printf ("\nServer Version %4.2f (Build %04d)\n\n", VERSION, build_number());
- Con_Printf ("======== QuakeWorld Initialized ========\n");
-
- // process command line arguments
- Cmd_StuffCmds_f ();
- Cbuf_Execute ();
- // if a map wasn't specified on the command line, spawn start.map
- if (sv.state == ss_dead)
- Cmd_ExecuteString ("map start");
- if (sv.state == ss_dead)
- SV_Error ("Couldn't spawn a server");
- }
|