|
|
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.