|
|
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);
195: MSG_WriteDeltaEntity (&nullstate, base, &sv_client->netchan.message, true);
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);
205: MSG_WriteString (&sv_client->netchan.message, va("precache ; cmd begin %i\n",svs.spawncount) );
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;
232:
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: ==================
281: SV_BeginDownload_f
282: ==================
283: */
284: void SV_BeginDownload_f(void)
285: {
286: char *name;
287: extern cvar_t *allow_download;
288:
289: name = Cmd_Argv(1);
290: if (strstr (name, "..") || !allow_download->value
291: || strstr (name, "maps") ) // don't allow full map downloads
292: { // don't allow anything with .. path
293: MSG_WriteByte (&sv_client->netchan.message, svc_download);
294: MSG_WriteShort (&sv_client->netchan.message, -1);
295: MSG_WriteByte (&sv_client->netchan.message, 0);
296: return;
297: }
298:
299: if (sv_client->download)
300: FS_FreeFile (sv_client->download);
301:
302: sv_client->downloadsize = FS_LoadFile (name, (void **)&sv_client->download);
303: sv_client->downloadcount = 0;
304:
305: if (!sv_client->download)
306: {
307: Com_DPrintf ("Couldn't download %s to %s\n", name, sv_client->name);
308: MSG_WriteByte (&sv_client->netchan.message, svc_download);
309: MSG_WriteShort (&sv_client->netchan.message, -1);
310: MSG_WriteByte (&sv_client->netchan.message, 0);
311: return;
312: }
313:
314: SV_NextDownload_f ();
315: Com_DPrintf ("Downloading %s to %s\n", name, sv_client->name);
316: }
317:
318:
319: //============================================================================
320:
321:
322: /*
323: =================
324: SV_Disconnect_f
325:
326: The client is going to disconnect, so remove the connection immediately
327: =================
328: */
329: void SV_Disconnect_f (void)
330: {
331: // SV_EndRedirect ();
332: SV_DropClient (sv_client);
333: }
334:
335:
336: /*
337: ==================
338: SV_ShowServerinfo_f
339:
340: Dumps the serverinfo info string
341: ==================
342: */
343: void SV_ShowServerinfo_f (void)
344: {
345: Info_Print (Cvar_Serverinfo());
346: }
347:
348:
349: void SV_Nextserver (void)
350: {
351: char *v;
352:
353: if (sv.state == ss_game || sv.state == ss_pic)
354: return; // can't nextserver while playing a normal game
355:
356: svs.spawncount++; // make sure another doesn't sneak in
357: v = Cvar_VariableString ("nextserver");
358: if (!v[0])
359: Cbuf_AddText ("killserver\n");
360: else
361: {
362: Cbuf_AddText (v);
363: Cbuf_AddText ("\n");
364: }
365: Cvar_Set ("nextserver","");
366: }
367:
368: /*
369: ==================
370: SV_Nextserver_f
371:
372: A cinematic has completed or been aborted by a client, so move
373: to the next server,
374: ==================
375: */
376: void SV_Nextserver_f (void)
377: {
378: if ( atoi(Cmd_Argv(1)) != svs.spawncount )
379: return; // leftover from last server
380:
381: SV_Nextserver ();
382: }
383:
384: typedef struct
385: {
386: char *name;
387: void (*func) (void);
388: } ucmd_t;
389:
390: ucmd_t ucmds[] =
391: {
392: // auto issued
393: {"new", SV_New_f},
394: {"configstrings", SV_Configstrings_f},
395: {"baselines", SV_Baselines_f},
396: {"begin", SV_Begin_f},
397:
398: {"nextserver", SV_Nextserver_f},
399:
400: {"disconnect", SV_Disconnect_f},
401:
402: // issued by hand at client consoles
403: {"info", SV_ShowServerinfo_f},
404:
405: {"download", SV_BeginDownload_f},
406: {"nextdl", SV_NextDownload_f},
407:
408: {NULL, NULL}
409: };
410:
411: /*
412: ==================
413: SV_ExecuteUserCommand
414: ==================
415: */
416: void SV_ExecuteUserCommand (char *s)
417: {
418: ucmd_t *u;
419:
420: Cmd_TokenizeString (s, true);
421: sv_player = sv_client->edict;
422:
423: // SV_BeginRedirect (RD_CLIENT);
424:
425: for (u=ucmds ; u->name ; u++)
426: if (!strcmp (Cmd_Argv(0), u->name) )
427: {
428: u->func ();
429: break;
430: }
431:
432: if (!u->name && sv.state == ss_game)
433: ge->ClientCommand (sv_player);
434:
435: // SV_EndRedirect ();
436: }
437:
438: /*
439: ===========================================================================
440:
441: USER CMD EXECUTION
442:
443: ===========================================================================
444: */
445:
446:
447: void ClientThink (client_t *cl, usercmd_t *cmd)
448: {
449: cl->commandMsec -= cmd->msec;
450: if (cl->commandMsec < 0 && sv_enforcetime->value )
451: {
452: Com_DPrintf ("commandMsec underflow from %s\n", cl->name);
453: return;
454: }
455: ge->ClientThink (cl->edict, cmd);
456: }
457:
458: #define MAX_STRINGCMDS 8
459: /*
460: ===================
461: SV_ExecuteClientMessage
462:
463: The current net_message is parsed for the given client
464: ===================
465: */
466: void SV_ExecuteClientMessage (client_t *cl)
467: {
468: int c;
469: char *s;
470: usercmd_t nullcmd;
471: usercmd_t oldest, oldcmd, newcmd;
472: int net_drop;
473: int stringCmdCount;
474: int checksum, calculatedChecksum;
475: int checksumIndex;
476: qboolean move_issued;
477:
478: sv_client = cl;
479: sv_player = sv_client->edict;
480:
481: // only allow one move command
482: move_issued = false;
483:
484: stringCmdCount = 0;
485:
486: while (1)
487: {
488: if (net_message.readcount > net_message.cursize)
489: {
490: Com_Printf ("SV_ReadClientMessage: badread\n");
491: SV_DropClient (cl);
492: return;
493: }
494:
495: c = MSG_ReadByte (&net_message);
496: if (c == -1)
497: break;
498:
499: switch (c)
500: {
501: default:
502: Com_Printf ("SV_ReadClientMessage: unknown command char\n");
503: SV_DropClient (cl);
504: return;
505:
506: case clc_nop:
507: break;
508:
509: case clc_userinfo:
510: strncpy (cl->userinfo, MSG_ReadString (&net_message), sizeof(cl->userinfo)-1);
511: SV_UserinfoChanged (cl);
512: break;
513:
514: case clc_move:
515: if (move_issued)
516: return; // someone is trying to cheat...
517: move_issued = true;
518:
519: checksumIndex = net_message.readcount;
520: checksum = MSG_ReadByte (&net_message);
521:
522: cl->lastframe = MSG_ReadLong (&net_message);
523: memset (&nullcmd, 0, sizeof(nullcmd));
524: MSG_ReadDeltaUsercmd (&net_message, &nullcmd, &oldest);
525: MSG_ReadDeltaUsercmd (&net_message, &oldest, &oldcmd);
526: MSG_ReadDeltaUsercmd (&net_message, &oldcmd, &newcmd);
527:
528: if ( cl->state != cs_spawned )
529: {
530: cl->lastframe = -1;
531: break;
532: }
533:
534: // if the checksum fails, ignore the rest of the packet
535: calculatedChecksum = COM_BlockSequenceCheckByte (
536: net_message.data + checksumIndex + 1,
537: net_message.readcount - checksumIndex - 1,
538: cl->netchan.incoming_sequence);
539:
540: if (calculatedChecksum != checksum)
541: {
542: Com_DPrintf ("Failed command checksum for %s\n", cl->name);
543: return;
544: }
545:
546: if (!sv_paused->value)
547: {
548: net_drop = cl->netchan.dropped;
549: if (net_drop < 20)
550: {
551: //if (net_drop > 2)
552: // Com_Printf ("drop %i\n", net_drop);
553: while (net_drop > 2)
554: {
555: ClientThink (cl, &cl->lastcmd);
556: net_drop--;
557: }
558: if (net_drop > 1)
559: ClientThink (cl, &oldest);
560: if (net_drop > 0)
561: ClientThink (cl, &oldcmd);
562: }
563: ClientThink (cl, &newcmd);
564: }
565:
566: cl->lastcmd = newcmd;
567: break;
568:
569:
570: case clc_stringcmd:
571: s = MSG_ReadString (&net_message);
572:
573: // malicious users may try using too many string commands
574: if (++stringCmdCount < MAX_STRINGCMDS)
575: SV_ExecuteUserCommand (s);
576: if (cl->state == cs_zombie)
577: return; // disconnect command
578: break;
579: }
580: }
581: }
582:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.