sv_user.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3. This program is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU General Public License
  5. as published by the Free Software Foundation; either version 2
  6. of the License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. See the GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  14. */
  15. // sv_user.c -- server code for moving users
  16. #include "server.h"
  17. edict_t *sv_player;
  18. /*
  19. ============================================================
  20. USER STRINGCMD EXECUTION
  21. sv_client and sv_player will be valid.
  22. ============================================================
  23. */
  24. /*
  25. ==================
  26. SV_BeginDemoServer
  27. ==================
  28. */
  29. void SV_BeginDemoserver (void)
  30. {
  31. char name[MAX_OSPATH];
  32. Com_sprintf (name, sizeof(name), "demos/%s", sv.name);
  33. FS_FOpenFile (name, &sv.demofile);
  34. if (!sv.demofile)
  35. Com_Error (ERR_DROP, "Couldn't open %s\n", name);
  36. }
  37. /*
  38. ================
  39. SV_New_f
  40. Sends the first message from the server to a connected client.
  41. This will be sent on the initial connection and upon each server load.
  42. ================
  43. */
  44. void SV_New_f (void)
  45. {
  46. char *gamedir;
  47. int playernum;
  48. edict_t *ent;
  49. Com_DPrintf ("New() from %s\n", sv_client->name);
  50. if (sv_client->state != cs_connected)
  51. {
  52. Com_Printf ("New not valid -- already spawned\n");
  53. return;
  54. }
  55. // demo servers just dump the file message
  56. if (sv.state == ss_demo)
  57. {
  58. SV_BeginDemoserver ();
  59. return;
  60. }
  61. //
  62. // serverdata needs to go over for all types of servers
  63. // to make sure the protocol is right, and to set the gamedir
  64. //
  65. gamedir = Cvar_VariableString ("gamedir");
  66. // send the serverdata
  67. MSG_WriteByte (&sv_client->netchan.message, svc_serverdata);
  68. MSG_WriteLong (&sv_client->netchan.message, PROTOCOL_VERSION);
  69. MSG_WriteLong (&sv_client->netchan.message, svs.spawncount);
  70. MSG_WriteByte (&sv_client->netchan.message, sv.attractloop);
  71. MSG_WriteString (&sv_client->netchan.message, gamedir);
  72. if (sv.state == ss_cinematic || sv.state == ss_pic)
  73. playernum = -1;
  74. else
  75. playernum = sv_client - svs.clients;
  76. MSG_WriteShort (&sv_client->netchan.message, playernum);
  77. // send full levelname
  78. MSG_WriteString (&sv_client->netchan.message, sv.configstrings[CS_NAME]);
  79. //
  80. // game server
  81. //
  82. if (sv.state == ss_game)
  83. {
  84. // set up the entity for the client
  85. ent = EDICT_NUM(playernum+1);
  86. ent->s.number = playernum+1;
  87. sv_client->edict = ent;
  88. memset (&sv_client->lastcmd, 0, sizeof(sv_client->lastcmd));
  89. // begin fetching configstrings
  90. MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
  91. MSG_WriteString (&sv_client->netchan.message, va("cmd configstrings %i 0\n",svs.spawncount) );
  92. }
  93. }
  94. /*
  95. ==================
  96. SV_Configstrings_f
  97. ==================
  98. */
  99. void SV_Configstrings_f (void)
  100. {
  101. int start;
  102. Com_DPrintf ("Configstrings() from %s\n", sv_client->name);
  103. if (sv_client->state != cs_connected)
  104. {
  105. Com_Printf ("configstrings not valid -- already spawned\n");
  106. return;
  107. }
  108. // handle the case of a level changing while a client was connecting
  109. if ( atoi(Cmd_Argv(1)) != svs.spawncount )
  110. {
  111. Com_Printf ("SV_Configstrings_f from different level\n");
  112. SV_New_f ();
  113. return;
  114. }
  115. start = atoi(Cmd_Argv(2));
  116. // write a packet full of data
  117. while ( sv_client->netchan.message.cursize < MAX_MSGLEN/2
  118. && start < MAX_CONFIGSTRINGS)
  119. {
  120. if (sv.configstrings[start][0])
  121. {
  122. MSG_WriteByte (&sv_client->netchan.message, svc_configstring);
  123. MSG_WriteShort (&sv_client->netchan.message, start);
  124. MSG_WriteString (&sv_client->netchan.message, sv.configstrings[start]);
  125. }
  126. start++;
  127. }
  128. // send next command
  129. if (start == MAX_CONFIGSTRINGS)
  130. {
  131. MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
  132. MSG_WriteString (&sv_client->netchan.message, va("cmd baselines %i 0\n",svs.spawncount) );
  133. }
  134. else
  135. {
  136. MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
  137. MSG_WriteString (&sv_client->netchan.message, va("cmd configstrings %i %i\n",svs.spawncount, start) );
  138. }
  139. }
  140. /*
  141. ==================
  142. SV_Baselines_f
  143. ==================
  144. */
  145. void SV_Baselines_f (void)
  146. {
  147. int start;
  148. entity_state_t nullstate;
  149. entity_state_t *base;
  150. Com_DPrintf ("Baselines() from %s\n", sv_client->name);
  151. if (sv_client->state != cs_connected)
  152. {
  153. Com_Printf ("baselines not valid -- already spawned\n");
  154. return;
  155. }
  156. // handle the case of a level changing while a client was connecting
  157. if ( atoi(Cmd_Argv(1)) != svs.spawncount )
  158. {
  159. Com_Printf ("SV_Baselines_f from different level\n");
  160. SV_New_f ();
  161. return;
  162. }
  163. start = atoi(Cmd_Argv(2));
  164. memset (&nullstate, 0, sizeof(nullstate));
  165. // write a packet full of data
  166. while ( sv_client->netchan.message.cursize < MAX_MSGLEN/2
  167. && start < MAX_EDICTS)
  168. {
  169. base = &sv.baselines[start];
  170. if (base->modelindex || base->sound || base->effects)
  171. {
  172. MSG_WriteByte (&sv_client->netchan.message, svc_spawnbaseline);
  173. MSG_WriteDeltaEntity (&nullstate, base, &sv_client->netchan.message, true, true);
  174. }
  175. start++;
  176. }
  177. // send next command
  178. if (start == MAX_EDICTS)
  179. {
  180. MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
  181. MSG_WriteString (&sv_client->netchan.message, va("precache %i\n", svs.spawncount) );
  182. }
  183. else
  184. {
  185. MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
  186. MSG_WriteString (&sv_client->netchan.message, va("cmd baselines %i %i\n",svs.spawncount, start) );
  187. }
  188. }
  189. /*
  190. ==================
  191. SV_Begin_f
  192. ==================
  193. */
  194. void SV_Begin_f (void)
  195. {
  196. Com_DPrintf ("Begin() from %s\n", sv_client->name);
  197. // handle the case of a level changing while a client was connecting
  198. if ( atoi(Cmd_Argv(1)) != svs.spawncount )
  199. {
  200. Com_Printf ("SV_Begin_f from different level\n");
  201. SV_New_f ();
  202. return;
  203. }
  204. sv_client->state = cs_spawned;
  205. // call the game begin function
  206. ge->ClientBegin (sv_player);
  207. Cbuf_InsertFromDefer ();
  208. }
  209. //=============================================================================
  210. /*
  211. ==================
  212. SV_NextDownload_f
  213. ==================
  214. */
  215. void SV_NextDownload_f (void)
  216. {
  217. int r;
  218. int percent;
  219. int size;
  220. if (!sv_client->download)
  221. return;
  222. r = sv_client->downloadsize - sv_client->downloadcount;
  223. if (r > 1024)
  224. r = 1024;
  225. MSG_WriteByte (&sv_client->netchan.message, svc_download);
  226. MSG_WriteShort (&sv_client->netchan.message, r);
  227. sv_client->downloadcount += r;
  228. size = sv_client->downloadsize;
  229. if (!size)
  230. size = 1;
  231. percent = sv_client->downloadcount*100/size;
  232. MSG_WriteByte (&sv_client->netchan.message, percent);
  233. SZ_Write (&sv_client->netchan.message,
  234. sv_client->download + sv_client->downloadcount - r, r);
  235. if (sv_client->downloadcount != sv_client->downloadsize)
  236. return;
  237. FS_FreeFile (sv_client->download);
  238. sv_client->download = NULL;
  239. }
  240. /*
  241. ==================
  242. SV_BeginDownload_f
  243. ==================
  244. */
  245. void SV_BeginDownload_f(void)
  246. {
  247. char *name;
  248. extern cvar_t *allow_download;
  249. extern cvar_t *allow_download_players;
  250. extern cvar_t *allow_download_models;
  251. extern cvar_t *allow_download_sounds;
  252. extern cvar_t *allow_download_maps;
  253. extern int file_from_pak; // ZOID did file come from pak?
  254. int offset = 0;
  255. name = Cmd_Argv(1);
  256. if (Cmd_Argc() > 2)
  257. offset = atoi(Cmd_Argv(2)); // downloaded offset
  258. // hacked by zoid to allow more conrol over download
  259. // first off, no .. or global allow check
  260. if (strstr (name, "..") || !allow_download->value
  261. // leading dot is no good
  262. || *name == '.'
  263. // leading slash bad as well, must be in subdir
  264. || *name == '/'
  265. // next up, skin check
  266. || (strncmp(name, "players/", 6) == 0 && !allow_download_players->value)
  267. // now models
  268. || (strncmp(name, "models/", 6) == 0 && !allow_download_models->value)
  269. // now sounds
  270. || (strncmp(name, "sound/", 6) == 0 && !allow_download_sounds->value)
  271. // now maps (note special case for maps, must not be in pak)
  272. || (strncmp(name, "maps/", 6) == 0 && !allow_download_maps->value)
  273. // MUST be in a subdirectory
  274. || !strstr (name, "/") )
  275. { // don't allow anything with .. path
  276. MSG_WriteByte (&sv_client->netchan.message, svc_download);
  277. MSG_WriteShort (&sv_client->netchan.message, -1);
  278. MSG_WriteByte (&sv_client->netchan.message, 0);
  279. return;
  280. }
  281. if (sv_client->download)
  282. FS_FreeFile (sv_client->download);
  283. sv_client->downloadsize = FS_LoadFile (name, (void **)&sv_client->download);
  284. sv_client->downloadcount = offset;
  285. if (offset > sv_client->downloadsize)
  286. sv_client->downloadcount = sv_client->downloadsize;
  287. if (!sv_client->download
  288. // special check for maps, if it came from a pak file, don't allow
  289. // download ZOID
  290. || (strncmp(name, "maps/", 5) == 0 && file_from_pak))
  291. {
  292. Com_DPrintf ("Couldn't download %s to %s\n", name, sv_client->name);
  293. if (sv_client->download) {
  294. FS_FreeFile (sv_client->download);
  295. sv_client->download = NULL;
  296. }
  297. MSG_WriteByte (&sv_client->netchan.message, svc_download);
  298. MSG_WriteShort (&sv_client->netchan.message, -1);
  299. MSG_WriteByte (&sv_client->netchan.message, 0);
  300. return;
  301. }
  302. SV_NextDownload_f ();
  303. Com_DPrintf ("Downloading %s to %s\n", name, sv_client->name);
  304. }
  305. //============================================================================
  306. /*
  307. =================
  308. SV_Disconnect_f
  309. The client is going to disconnect, so remove the connection immediately
  310. =================
  311. */
  312. void SV_Disconnect_f (void)
  313. {
  314. // SV_EndRedirect ();
  315. SV_DropClient (sv_client);
  316. }
  317. /*
  318. ==================
  319. SV_ShowServerinfo_f
  320. Dumps the serverinfo info string
  321. ==================
  322. */
  323. void SV_ShowServerinfo_f (void)
  324. {
  325. Info_Print (Cvar_Serverinfo());
  326. }
  327. void SV_Nextserver (void)
  328. {
  329. char *v;
  330. //ZOID, ss_pic can be nextserver'd in coop mode
  331. if (sv.state == ss_game || (sv.state == ss_pic && !Cvar_VariableValue("coop")))
  332. return; // can't nextserver while playing a normal game
  333. svs.spawncount++; // make sure another doesn't sneak in
  334. v = Cvar_VariableString ("nextserver");
  335. if (!v[0])
  336. Cbuf_AddText ("killserver\n");
  337. else
  338. {
  339. Cbuf_AddText (v);
  340. Cbuf_AddText ("\n");
  341. }
  342. Cvar_Set ("nextserver","");
  343. }
  344. /*
  345. ==================
  346. SV_Nextserver_f
  347. A cinematic has completed or been aborted by a client, so move
  348. to the next server,
  349. ==================
  350. */
  351. void SV_Nextserver_f (void)
  352. {
  353. if ( atoi(Cmd_Argv(1)) != svs.spawncount ) {
  354. Com_DPrintf ("Nextserver() from wrong level, from %s\n", sv_client->name);
  355. return; // leftover from last server
  356. }
  357. Com_DPrintf ("Nextserver() from %s\n", sv_client->name);
  358. SV_Nextserver ();
  359. }
  360. typedef struct
  361. {
  362. char *name;
  363. void (*func) (void);
  364. } ucmd_t;
  365. ucmd_t ucmds[] =
  366. {
  367. // auto issued
  368. {"new", SV_New_f},
  369. {"configstrings", SV_Configstrings_f},
  370. {"baselines", SV_Baselines_f},
  371. {"begin", SV_Begin_f},
  372. {"nextserver", SV_Nextserver_f},
  373. {"disconnect", SV_Disconnect_f},
  374. // issued by hand at client consoles
  375. {"info", SV_ShowServerinfo_f},
  376. {"download", SV_BeginDownload_f},
  377. {"nextdl", SV_NextDownload_f},
  378. {NULL, NULL}
  379. };
  380. /*
  381. ==================
  382. SV_ExecuteUserCommand
  383. ==================
  384. */
  385. void SV_ExecuteUserCommand (char *s)
  386. {
  387. ucmd_t *u;
  388. Cmd_TokenizeString (s, true);
  389. sv_player = sv_client->edict;
  390. // SV_BeginRedirect (RD_CLIENT);
  391. for (u=ucmds ; u->name ; u++)
  392. if (!strcmp (Cmd_Argv(0), u->name) )
  393. {
  394. u->func ();
  395. break;
  396. }
  397. if (!u->name && sv.state == ss_game)
  398. ge->ClientCommand (sv_player);
  399. // SV_EndRedirect ();
  400. }
  401. /*
  402. ===========================================================================
  403. USER CMD EXECUTION
  404. ===========================================================================
  405. */
  406. void SV_ClientThink (client_t *cl, usercmd_t *cmd)
  407. {
  408. cl->commandMsec -= cmd->msec;
  409. if (cl->commandMsec < 0 && sv_enforcetime->value )
  410. {
  411. Com_DPrintf ("commandMsec underflow from %s\n", cl->name);
  412. return;
  413. }
  414. ge->ClientThink (cl->edict, cmd);
  415. }
  416. #define MAX_STRINGCMDS 8
  417. /*
  418. ===================
  419. SV_ExecuteClientMessage
  420. The current net_message is parsed for the given client
  421. ===================
  422. */
  423. void SV_ExecuteClientMessage (client_t *cl)
  424. {
  425. int c;
  426. char *s;
  427. usercmd_t nullcmd;
  428. usercmd_t oldest, oldcmd, newcmd;
  429. int net_drop;
  430. int stringCmdCount;
  431. int checksum, calculatedChecksum;
  432. int checksumIndex;
  433. qboolean move_issued;
  434. int lastframe;
  435. sv_client = cl;
  436. sv_player = sv_client->edict;
  437. // only allow one move command
  438. move_issued = false;
  439. stringCmdCount = 0;
  440. while (1)
  441. {
  442. if (net_message.readcount > net_message.cursize)
  443. {
  444. Com_Printf ("SV_ReadClientMessage: badread\n");
  445. SV_DropClient (cl);
  446. return;
  447. }
  448. c = MSG_ReadByte (&net_message);
  449. if (c == -1)
  450. break;
  451. switch (c)
  452. {
  453. default:
  454. Com_Printf ("SV_ReadClientMessage: unknown command char\n");
  455. SV_DropClient (cl);
  456. return;
  457. case clc_nop:
  458. break;
  459. case clc_userinfo:
  460. strncpy (cl->userinfo, MSG_ReadString (&net_message), sizeof(cl->userinfo)-1);
  461. SV_UserinfoChanged (cl);
  462. break;
  463. case clc_move:
  464. if (move_issued)
  465. return; // someone is trying to cheat...
  466. move_issued = true;
  467. checksumIndex = net_message.readcount;
  468. checksum = MSG_ReadByte (&net_message);
  469. lastframe = MSG_ReadLong (&net_message);
  470. if (lastframe != cl->lastframe) {
  471. cl->lastframe = lastframe;
  472. if (cl->lastframe > 0) {
  473. cl->frame_latency[cl->lastframe&(LATENCY_COUNTS-1)] =
  474. svs.realtime - cl->frames[cl->lastframe & UPDATE_MASK].senttime;
  475. }
  476. }
  477. memset (&nullcmd, 0, sizeof(nullcmd));
  478. MSG_ReadDeltaUsercmd (&net_message, &nullcmd, &oldest);
  479. MSG_ReadDeltaUsercmd (&net_message, &oldest, &oldcmd);
  480. MSG_ReadDeltaUsercmd (&net_message, &oldcmd, &newcmd);
  481. if ( cl->state != cs_spawned )
  482. {
  483. cl->lastframe = -1;
  484. break;
  485. }
  486. // if the checksum fails, ignore the rest of the packet
  487. calculatedChecksum = COM_BlockSequenceCRCByte (
  488. net_message.data + checksumIndex + 1,
  489. net_message.readcount - checksumIndex - 1,
  490. cl->netchan.incoming_sequence);
  491. if (calculatedChecksum != checksum)
  492. {
  493. Com_DPrintf ("Failed command checksum for %s (%d != %d)/%d\n",
  494. cl->name, calculatedChecksum, checksum,
  495. cl->netchan.incoming_sequence);
  496. return;
  497. }
  498. if (!sv_paused->value)
  499. {
  500. net_drop = cl->netchan.dropped;
  501. if (net_drop < 20)
  502. {
  503. //if (net_drop > 2)
  504. // Com_Printf ("drop %i\n", net_drop);
  505. while (net_drop > 2)
  506. {
  507. SV_ClientThink (cl, &cl->lastcmd);
  508. net_drop--;
  509. }
  510. if (net_drop > 1)
  511. SV_ClientThink (cl, &oldest);
  512. if (net_drop > 0)
  513. SV_ClientThink (cl, &oldcmd);
  514. }
  515. SV_ClientThink (cl, &newcmd);
  516. }
  517. cl->lastcmd = newcmd;
  518. break;
  519. case clc_stringcmd:
  520. s = MSG_ReadString (&net_message);
  521. // malicious users may try using too many string commands
  522. if (++stringCmdCount < MAX_STRINGCMDS)
  523. SV_ExecuteUserCommand (s);
  524. if (cl->state == cs_zombie)
  525. return; // disconnect command
  526. break;
  527. }
  528. }
  529. }