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