123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665 |
- /*
- Copyright (C) 1997-2001 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.
- */
- // sv_user.c -- server code for moving users
- #include "server.h"
- edict_t *sv_player;
- /*
- ============================================================
- USER STRINGCMD EXECUTION
- sv_client and sv_player will be valid.
- ============================================================
- */
- /*
- ==================
- SV_BeginDemoServer
- ==================
- */
- void SV_BeginDemoserver (void)
- {
- char name[MAX_OSPATH];
- Com_sprintf (name, sizeof(name), "demos/%s", sv.name);
- FS_FOpenFile (name, &sv.demofile);
- if (!sv.demofile)
- Com_Error (ERR_DROP, "Couldn't open %s\n", name);
- }
- /*
- ================
- SV_New_f
- Sends the first message from the server to a connected client.
- This will be sent on the initial connection and upon each server load.
- ================
- */
- void SV_New_f (void)
- {
- char *gamedir;
- int playernum;
- edict_t *ent;
- Com_DPrintf ("New() from %s\n", sv_client->name);
- if (sv_client->state != cs_connected)
- {
- Com_Printf ("New not valid -- already spawned\n");
- return;
- }
- // demo servers just dump the file message
- if (sv.state == ss_demo)
- {
- SV_BeginDemoserver ();
- return;
- }
- //
- // serverdata needs to go over for all types of servers
- // to make sure the protocol is right, and to set the gamedir
- //
- gamedir = Cvar_VariableString ("gamedir");
- // send the serverdata
- MSG_WriteByte (&sv_client->netchan.message, svc_serverdata);
- MSG_WriteLong (&sv_client->netchan.message, PROTOCOL_VERSION);
- MSG_WriteLong (&sv_client->netchan.message, svs.spawncount);
- MSG_WriteByte (&sv_client->netchan.message, sv.attractloop);
- MSG_WriteString (&sv_client->netchan.message, gamedir);
- if (sv.state == ss_cinematic || sv.state == ss_pic)
- playernum = -1;
- else
- playernum = sv_client - svs.clients;
- MSG_WriteShort (&sv_client->netchan.message, playernum);
- // send full levelname
- MSG_WriteString (&sv_client->netchan.message, sv.configstrings[CS_NAME]);
- //
- // game server
- //
- if (sv.state == ss_game)
- {
- // set up the entity for the client
- ent = EDICT_NUM(playernum+1);
- ent->s.number = playernum+1;
- sv_client->edict = ent;
- memset (&sv_client->lastcmd, 0, sizeof(sv_client->lastcmd));
- // begin fetching configstrings
- MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
- MSG_WriteString (&sv_client->netchan.message, va("cmd configstrings %i 0\n",svs.spawncount) );
- }
- }
- /*
- ==================
- SV_Configstrings_f
- ==================
- */
- void SV_Configstrings_f (void)
- {
- int start;
- Com_DPrintf ("Configstrings() from %s\n", sv_client->name);
- if (sv_client->state != cs_connected)
- {
- Com_Printf ("configstrings not valid -- already spawned\n");
- return;
- }
- // handle the case of a level changing while a client was connecting
- if ( atoi(Cmd_Argv(1)) != svs.spawncount )
- {
- Com_Printf ("SV_Configstrings_f from different level\n");
- SV_New_f ();
- return;
- }
-
- start = atoi(Cmd_Argv(2));
- // write a packet full of data
- while ( sv_client->netchan.message.cursize < MAX_MSGLEN/2
- && start < MAX_CONFIGSTRINGS)
- {
- if (sv.configstrings[start][0])
- {
- MSG_WriteByte (&sv_client->netchan.message, svc_configstring);
- MSG_WriteShort (&sv_client->netchan.message, start);
- MSG_WriteString (&sv_client->netchan.message, sv.configstrings[start]);
- }
- start++;
- }
- // send next command
- if (start == MAX_CONFIGSTRINGS)
- {
- MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
- MSG_WriteString (&sv_client->netchan.message, va("cmd baselines %i 0\n",svs.spawncount) );
- }
- else
- {
- MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
- MSG_WriteString (&sv_client->netchan.message, va("cmd configstrings %i %i\n",svs.spawncount, start) );
- }
- }
- /*
- ==================
- SV_Baselines_f
- ==================
- */
- void SV_Baselines_f (void)
- {
- int start;
- entity_state_t nullstate;
- entity_state_t *base;
- Com_DPrintf ("Baselines() from %s\n", sv_client->name);
- if (sv_client->state != cs_connected)
- {
- Com_Printf ("baselines not valid -- already spawned\n");
- return;
- }
-
- // handle the case of a level changing while a client was connecting
- if ( atoi(Cmd_Argv(1)) != svs.spawncount )
- {
- Com_Printf ("SV_Baselines_f from different level\n");
- SV_New_f ();
- return;
- }
-
- start = atoi(Cmd_Argv(2));
- memset (&nullstate, 0, sizeof(nullstate));
- // write a packet full of data
- while ( sv_client->netchan.message.cursize < MAX_MSGLEN/2
- && start < MAX_EDICTS)
- {
- base = &sv.baselines[start];
- if (base->modelindex || base->sound || base->effects)
- {
- MSG_WriteByte (&sv_client->netchan.message, svc_spawnbaseline);
- MSG_WriteDeltaEntity (&nullstate, base, &sv_client->netchan.message, true, true);
- }
- start++;
- }
- // send next command
- if (start == MAX_EDICTS)
- {
- MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
- MSG_WriteString (&sv_client->netchan.message, va("precache %i\n", svs.spawncount) );
- }
- else
- {
- MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
- MSG_WriteString (&sv_client->netchan.message, va("cmd baselines %i %i\n",svs.spawncount, start) );
- }
- }
- /*
- ==================
- SV_Begin_f
- ==================
- */
- void SV_Begin_f (void)
- {
- Com_DPrintf ("Begin() from %s\n", sv_client->name);
- // handle the case of a level changing while a client was connecting
- if ( atoi(Cmd_Argv(1)) != svs.spawncount )
- {
- Com_Printf ("SV_Begin_f from different level\n");
- SV_New_f ();
- return;
- }
- sv_client->state = cs_spawned;
- // call the game begin function
- ge->ClientBegin (sv_player);
- Cbuf_InsertFromDefer ();
- }
- //=============================================================================
- /*
- ==================
- SV_NextDownload_f
- ==================
- */
- void SV_NextDownload_f (void)
- {
- int r;
- int percent;
- int size;
- if (!sv_client->download)
- return;
- r = sv_client->downloadsize - sv_client->downloadcount;
- if (r > 1024)
- r = 1024;
- MSG_WriteByte (&sv_client->netchan.message, svc_download);
- MSG_WriteShort (&sv_client->netchan.message, r);
- sv_client->downloadcount += r;
- size = sv_client->downloadsize;
- if (!size)
- size = 1;
- percent = sv_client->downloadcount*100/size;
- MSG_WriteByte (&sv_client->netchan.message, percent);
- SZ_Write (&sv_client->netchan.message,
- sv_client->download + sv_client->downloadcount - r, r);
- if (sv_client->downloadcount != sv_client->downloadsize)
- return;
- FS_FreeFile (sv_client->download);
- sv_client->download = NULL;
- }
- /*
- ==================
- SV_BeginDownload_f
- ==================
- */
- void SV_BeginDownload_f(void)
- {
- char *name;
- extern cvar_t *allow_download;
- extern cvar_t *allow_download_players;
- extern cvar_t *allow_download_models;
- extern cvar_t *allow_download_sounds;
- extern cvar_t *allow_download_maps;
- extern int file_from_pak; // ZOID did file come from pak?
- int offset = 0;
- name = Cmd_Argv(1);
- if (Cmd_Argc() > 2)
- offset = atoi(Cmd_Argv(2)); // downloaded offset
- // hacked by zoid to allow more conrol over download
- // first off, no .. or global allow check
- if (strstr (name, "..") || !allow_download->value
- // leading dot is no good
- || *name == '.'
- // leading slash bad as well, must be in subdir
- || *name == '/'
- // next up, skin check
- || (strncmp(name, "players/", 6) == 0 && !allow_download_players->value)
- // now models
- || (strncmp(name, "models/", 6) == 0 && !allow_download_models->value)
- // now sounds
- || (strncmp(name, "sound/", 6) == 0 && !allow_download_sounds->value)
- // now maps (note special case for maps, must not be in pak)
- || (strncmp(name, "maps/", 6) == 0 && !allow_download_maps->value)
- // MUST be in a subdirectory
- || !strstr (name, "/") )
- { // don't allow anything with .. path
- MSG_WriteByte (&sv_client->netchan.message, svc_download);
- MSG_WriteShort (&sv_client->netchan.message, -1);
- MSG_WriteByte (&sv_client->netchan.message, 0);
- return;
- }
- if (sv_client->download)
- FS_FreeFile (sv_client->download);
- sv_client->downloadsize = FS_LoadFile (name, (void **)&sv_client->download);
- sv_client->downloadcount = offset;
- if (offset > sv_client->downloadsize)
- sv_client->downloadcount = sv_client->downloadsize;
- if (!sv_client->download
- // special check for maps, if it came from a pak file, don't allow
- // download ZOID
- || (strncmp(name, "maps/", 5) == 0 && file_from_pak))
- {
- Com_DPrintf ("Couldn't download %s to %s\n", name, sv_client->name);
- if (sv_client->download) {
- FS_FreeFile (sv_client->download);
- sv_client->download = NULL;
- }
- MSG_WriteByte (&sv_client->netchan.message, svc_download);
- MSG_WriteShort (&sv_client->netchan.message, -1);
- MSG_WriteByte (&sv_client->netchan.message, 0);
- return;
- }
- SV_NextDownload_f ();
- Com_DPrintf ("Downloading %s to %s\n", name, sv_client->name);
- }
- //============================================================================
- /*
- =================
- SV_Disconnect_f
- The client is going to disconnect, so remove the connection immediately
- =================
- */
- void SV_Disconnect_f (void)
- {
- // SV_EndRedirect ();
- SV_DropClient (sv_client);
- }
- /*
- ==================
- SV_ShowServerinfo_f
- Dumps the serverinfo info string
- ==================
- */
- void SV_ShowServerinfo_f (void)
- {
- Info_Print (Cvar_Serverinfo());
- }
- void SV_Nextserver (void)
- {
- char *v;
- //ZOID, ss_pic can be nextserver'd in coop mode
- if (sv.state == ss_game || (sv.state == ss_pic && !Cvar_VariableValue("coop")))
- return; // can't nextserver while playing a normal game
- svs.spawncount++; // make sure another doesn't sneak in
- v = Cvar_VariableString ("nextserver");
- if (!v[0])
- Cbuf_AddText ("killserver\n");
- else
- {
- Cbuf_AddText (v);
- Cbuf_AddText ("\n");
- }
- Cvar_Set ("nextserver","");
- }
- /*
- ==================
- SV_Nextserver_f
- A cinematic has completed or been aborted by a client, so move
- to the next server,
- ==================
- */
- void SV_Nextserver_f (void)
- {
- if ( atoi(Cmd_Argv(1)) != svs.spawncount ) {
- Com_DPrintf ("Nextserver() from wrong level, from %s\n", sv_client->name);
- return; // leftover from last server
- }
- Com_DPrintf ("Nextserver() from %s\n", sv_client->name);
- SV_Nextserver ();
- }
- typedef struct
- {
- char *name;
- void (*func) (void);
- } ucmd_t;
- ucmd_t ucmds[] =
- {
- // auto issued
- {"new", SV_New_f},
- {"configstrings", SV_Configstrings_f},
- {"baselines", SV_Baselines_f},
- {"begin", SV_Begin_f},
- {"nextserver", SV_Nextserver_f},
- {"disconnect", SV_Disconnect_f},
- // issued by hand at client consoles
- {"info", SV_ShowServerinfo_f},
- {"download", SV_BeginDownload_f},
- {"nextdl", SV_NextDownload_f},
- {NULL, NULL}
- };
- /*
- ==================
- SV_ExecuteUserCommand
- ==================
- */
- void SV_ExecuteUserCommand (char *s)
- {
- ucmd_t *u;
-
- Cmd_TokenizeString (s, true);
- sv_player = sv_client->edict;
- // SV_BeginRedirect (RD_CLIENT);
- for (u=ucmds ; u->name ; u++)
- if (!strcmp (Cmd_Argv(0), u->name) )
- {
- u->func ();
- break;
- }
- if (!u->name && sv.state == ss_game)
- ge->ClientCommand (sv_player);
- // SV_EndRedirect ();
- }
- /*
- ===========================================================================
- USER CMD EXECUTION
- ===========================================================================
- */
- void SV_ClientThink (client_t *cl, usercmd_t *cmd)
- {
- cl->commandMsec -= cmd->msec;
- if (cl->commandMsec < 0 && sv_enforcetime->value )
- {
- Com_DPrintf ("commandMsec underflow from %s\n", cl->name);
- return;
- }
- ge->ClientThink (cl->edict, cmd);
- }
- #define MAX_STRINGCMDS 8
- /*
- ===================
- SV_ExecuteClientMessage
- The current net_message is parsed for the given client
- ===================
- */
- void SV_ExecuteClientMessage (client_t *cl)
- {
- int c;
- char *s;
- usercmd_t nullcmd;
- usercmd_t oldest, oldcmd, newcmd;
- int net_drop;
- int stringCmdCount;
- int checksum, calculatedChecksum;
- int checksumIndex;
- qboolean move_issued;
- int lastframe;
- sv_client = cl;
- sv_player = sv_client->edict;
- // only allow one move command
- move_issued = false;
- stringCmdCount = 0;
- while (1)
- {
- if (net_message.readcount > net_message.cursize)
- {
- Com_Printf ("SV_ReadClientMessage: badread\n");
- SV_DropClient (cl);
- return;
- }
- c = MSG_ReadByte (&net_message);
- if (c == -1)
- break;
-
- switch (c)
- {
- default:
- Com_Printf ("SV_ReadClientMessage: unknown command char\n");
- SV_DropClient (cl);
- return;
-
- case clc_nop:
- break;
- case clc_userinfo:
- strncpy (cl->userinfo, MSG_ReadString (&net_message), sizeof(cl->userinfo)-1);
- SV_UserinfoChanged (cl);
- break;
- case clc_move:
- if (move_issued)
- return; // someone is trying to cheat...
- move_issued = true;
- checksumIndex = net_message.readcount;
- checksum = MSG_ReadByte (&net_message);
- lastframe = MSG_ReadLong (&net_message);
- if (lastframe != cl->lastframe) {
- cl->lastframe = lastframe;
- if (cl->lastframe > 0) {
- cl->frame_latency[cl->lastframe&(LATENCY_COUNTS-1)] =
- svs.realtime - cl->frames[cl->lastframe & UPDATE_MASK].senttime;
- }
- }
- memset (&nullcmd, 0, sizeof(nullcmd));
- MSG_ReadDeltaUsercmd (&net_message, &nullcmd, &oldest);
- MSG_ReadDeltaUsercmd (&net_message, &oldest, &oldcmd);
- MSG_ReadDeltaUsercmd (&net_message, &oldcmd, &newcmd);
- if ( cl->state != cs_spawned )
- {
- cl->lastframe = -1;
- break;
- }
- // if the checksum fails, ignore the rest of the packet
- calculatedChecksum = COM_BlockSequenceCRCByte (
- net_message.data + checksumIndex + 1,
- net_message.readcount - checksumIndex - 1,
- cl->netchan.incoming_sequence);
- if (calculatedChecksum != checksum)
- {
- Com_DPrintf ("Failed command checksum for %s (%d != %d)/%d\n",
- cl->name, calculatedChecksum, checksum,
- cl->netchan.incoming_sequence);
- return;
- }
- if (!sv_paused->value)
- {
- net_drop = cl->netchan.dropped;
- if (net_drop < 20)
- {
- //if (net_drop > 2)
- // Com_Printf ("drop %i\n", net_drop);
- while (net_drop > 2)
- {
- SV_ClientThink (cl, &cl->lastcmd);
- net_drop--;
- }
- if (net_drop > 1)
- SV_ClientThink (cl, &oldest);
- if (net_drop > 0)
- SV_ClientThink (cl, &oldcmd);
- }
- SV_ClientThink (cl, &newcmd);
- }
- cl->lastcmd = newcmd;
- break;
- case clc_stringcmd:
- s = MSG_ReadString (&net_message);
- // malicious users may try using too many string commands
- if (++stringCmdCount < MAX_STRINGCMDS)
- SV_ExecuteUserCommand (s);
- if (cl->state == cs_zombie)
- return; // disconnect command
- break;
- }
- }
- }
|