1 #include "game.h" 2 3 namespace game 4 { parseoptions(vector<const char * > & args)5 void parseoptions(vector<const char *> &args) 6 { 7 loopv(args) 8 #ifndef STANDALONE 9 if(!game::clientoption(args[i])) 10 #endif 11 if(!server::serveroption(args[i])) 12 conoutf(CON_ERROR, "unknown command-line option: %s", args[i]); 13 } 14 gameident()15 const char *gameident() { return "fps"; } 16 } 17 18 VAR(regenbluearmour, 0, 1, 1); 19 20 extern ENetAddress masteraddress; 21 22 namespace server 23 { 24 struct server_entity // server side version of "entity" type 25 { 26 int type; 27 int spawntime; 28 bool spawned; 29 }; 30 31 static const int DEATHMILLIS = 300; 32 33 struct clientinfo; 34 35 struct gameevent 36 { ~gameeventserver::gameevent37 virtual ~gameevent() {} 38 39 virtual bool flush(clientinfo *ci, int fmillis); processserver::gameevent40 virtual void process(clientinfo *ci) {} 41 keepableserver::gameevent42 virtual bool keepable() const { return false; } 43 }; 44 45 struct timedevent : gameevent 46 { 47 int millis; 48 49 bool flush(clientinfo *ci, int fmillis); 50 }; 51 52 struct hitinfo 53 { 54 int target; 55 int lifesequence; 56 int rays; 57 float dist; 58 vec dir; 59 }; 60 61 struct shotevent : timedevent 62 { 63 int id, gun; 64 vec from, to; 65 vector<hitinfo> hits; 66 67 void process(clientinfo *ci); 68 }; 69 70 struct explodeevent : timedevent 71 { 72 int id, gun; 73 vector<hitinfo> hits; 74 keepableserver::explodeevent75 bool keepable() const { return true; } 76 77 void process(clientinfo *ci); 78 }; 79 80 struct suicideevent : gameevent 81 { 82 void process(clientinfo *ci); 83 }; 84 85 struct pickupevent : gameevent 86 { 87 int ent; 88 89 void process(clientinfo *ci); 90 }; 91 92 template <int N> 93 struct projectilestate 94 { 95 int projs[N]; 96 int numprojs; 97 projectilestateserver::projectilestate98 projectilestate() : numprojs(0) {} 99 resetserver::projectilestate100 void reset() { numprojs = 0; } 101 addserver::projectilestate102 void add(int val) 103 { 104 if(numprojs>=N) numprojs = 0; 105 projs[numprojs++] = val; 106 } 107 removeserver::projectilestate108 bool remove(int val) 109 { 110 loopi(numprojs) if(projs[i]==val) 111 { 112 projs[i] = projs[--numprojs]; 113 return true; 114 } 115 return false; 116 } 117 }; 118 119 struct gamestate : fpsstate 120 { 121 vec o; 122 int state, editstate; 123 int lastdeath, deadflush, lastspawn, lifesequence; 124 int lastshot; 125 projectilestate<8> rockets, grenades; 126 int frags, flags, deaths, teamkills, shotdamage, damage, tokens; 127 int lasttimeplayed, timeplayed; 128 float effectiveness; 129 gamestateserver::gamestate130 gamestate() : state(CS_DEAD), editstate(CS_DEAD), lifesequence(0) {} 131 isaliveserver::gamestate132 bool isalive(int gamemillis) 133 { 134 return state==CS_ALIVE || (state==CS_DEAD && gamemillis - lastdeath <= DEATHMILLIS); 135 } 136 waitexpiredserver::gamestate137 bool waitexpired(int gamemillis) 138 { 139 return gamemillis - lastshot >= gunwait; 140 } 141 resetserver::gamestate142 void reset() 143 { 144 if(state!=CS_SPECTATOR) state = editstate = CS_DEAD; 145 maxhealth = 100; 146 rockets.reset(); 147 grenades.reset(); 148 149 timeplayed = 0; 150 effectiveness = 0; 151 frags = flags = deaths = teamkills = shotdamage = damage = tokens = 0; 152 153 lastdeath = 0; 154 155 respawn(); 156 } 157 respawnserver::gamestate158 void respawn() 159 { 160 fpsstate::respawn(); 161 o = vec(-1e10f, -1e10f, -1e10f); 162 deadflush = 0; 163 lastspawn = -1; 164 lastshot = 0; 165 tokens = 0; 166 } 167 reassignserver::gamestate168 void reassign() 169 { 170 respawn(); 171 rockets.reset(); 172 grenades.reset(); 173 } 174 }; 175 176 struct savedscore 177 { 178 uint ip; 179 string name; 180 int frags, flags, deaths, teamkills, shotdamage, damage; 181 int timeplayed; 182 float effectiveness; 183 saveserver::savedscore184 void save(gamestate &gs) 185 { 186 frags = gs.frags; 187 flags = gs.flags; 188 deaths = gs.deaths; 189 teamkills = gs.teamkills; 190 shotdamage = gs.shotdamage; 191 damage = gs.damage; 192 timeplayed = gs.timeplayed; 193 effectiveness = gs.effectiveness; 194 } 195 restoreserver::savedscore196 void restore(gamestate &gs) 197 { 198 gs.frags = frags; 199 gs.flags = flags; 200 gs.deaths = deaths; 201 gs.teamkills = teamkills; 202 gs.shotdamage = shotdamage; 203 gs.damage = damage; 204 gs.timeplayed = timeplayed; 205 gs.effectiveness = effectiveness; 206 } 207 }; 208 209 extern int gamemillis, nextexceeded; 210 211 struct clientinfo 212 { 213 int clientnum, ownernum, connectmillis, sessionid, overflow; 214 string name, team, mapvote; 215 int playermodel; 216 int modevote; 217 int privilege; 218 bool connected, local, timesync; 219 int gameoffset, lastevent, pushed, exceeded; 220 gamestate state; 221 vector<gameevent *> events; 222 vector<uchar> position, messages; 223 uchar *wsdata; 224 int wslen; 225 vector<clientinfo *> bots; 226 int ping, aireinit; 227 string clientmap; 228 int mapcrc; 229 bool warned, gameclip; 230 ENetPacket *getdemo, *getmap, *clipboard; 231 int lastclipboard, needclipboard; 232 int connectauth; 233 uint authreq; 234 string authname, authdesc; 235 void *authchallenge; 236 int authkickvictim; 237 char *authkickreason; 238 clientinfoserver::clientinfo239 clientinfo() : getdemo(NULL), getmap(NULL), clipboard(NULL), authchallenge(NULL), authkickreason(NULL) { reset(); } ~clientinfoserver::clientinfo240 ~clientinfo() { events.deletecontents(); cleanclipboard(); cleanauth(); } 241 addeventserver::clientinfo242 void addevent(gameevent *e) 243 { 244 if(state.state==CS_SPECTATOR || events.length()>100) delete e; 245 else events.add(e); 246 } 247 248 enum 249 { 250 PUSHMILLIS = 3000 251 }; 252 calcpushrangeserver::clientinfo253 int calcpushrange() 254 { 255 ENetPeer *peer = getclientpeer(ownernum); 256 return PUSHMILLIS + (peer ? peer->roundTripTime + peer->roundTripTimeVariance : ENET_PEER_DEFAULT_ROUND_TRIP_TIME); 257 } 258 checkpushedserver::clientinfo259 bool checkpushed(int millis, int range) 260 { 261 return millis >= pushed - range && millis <= pushed + range; 262 } 263 scheduleexceededserver::clientinfo264 void scheduleexceeded() 265 { 266 if(state.state!=CS_ALIVE || !exceeded) return; 267 int range = calcpushrange(); 268 if(!nextexceeded || exceeded + range < nextexceeded) nextexceeded = exceeded + range; 269 } 270 setexceededserver::clientinfo271 void setexceeded() 272 { 273 if(state.state==CS_ALIVE && !exceeded && !checkpushed(gamemillis, calcpushrange())) exceeded = gamemillis; 274 scheduleexceeded(); 275 } 276 setpushedserver::clientinfo277 void setpushed() 278 { 279 pushed = max(pushed, gamemillis); 280 if(exceeded && checkpushed(exceeded, calcpushrange())) exceeded = 0; 281 } 282 checkexceededserver::clientinfo283 bool checkexceeded() 284 { 285 return state.state==CS_ALIVE && exceeded && gamemillis > exceeded + calcpushrange(); 286 } 287 mapchangeserver::clientinfo288 void mapchange() 289 { 290 mapvote[0] = 0; 291 modevote = INT_MAX; 292 state.reset(); 293 events.deletecontents(); 294 overflow = 0; 295 timesync = false; 296 lastevent = 0; 297 exceeded = 0; 298 pushed = 0; 299 clientmap[0] = '\0'; 300 mapcrc = 0; 301 warned = false; 302 gameclip = false; 303 } 304 reassignserver::clientinfo305 void reassign() 306 { 307 state.reassign(); 308 events.deletecontents(); 309 timesync = false; 310 lastevent = 0; 311 } 312 cleanclipboardserver::clientinfo313 void cleanclipboard(bool fullclean = true) 314 { 315 if(clipboard) { if(--clipboard->referenceCount <= 0) enet_packet_destroy(clipboard); clipboard = NULL; } 316 if(fullclean) lastclipboard = 0; 317 } 318 cleanauthkickserver::clientinfo319 void cleanauthkick() 320 { 321 authkickvictim = -1; 322 DELETEA(authkickreason); 323 } 324 cleanauthserver::clientinfo325 void cleanauth(bool full = true) 326 { 327 authreq = 0; 328 if(authchallenge) { freechallenge(authchallenge); authchallenge = NULL; } 329 if(full) cleanauthkick(); 330 } 331 resetserver::clientinfo332 void reset() 333 { 334 name[0] = team[0] = 0; 335 playermodel = -1; 336 privilege = PRIV_NONE; 337 connected = local = false; 338 connectauth = 0; 339 position.setsize(0); 340 messages.setsize(0); 341 ping = 0; 342 aireinit = 0; 343 needclipboard = 0; 344 cleanclipboard(); 345 cleanauth(); 346 mapchange(); 347 } 348 geteventmillisserver::clientinfo349 int geteventmillis(int servmillis, int clientmillis) 350 { 351 if(!timesync || (events.empty() && state.waitexpired(servmillis))) 352 { 353 timesync = true; 354 gameoffset = servmillis - clientmillis; 355 return servmillis; 356 } 357 else return gameoffset + clientmillis; 358 } 359 }; 360 361 struct ban 362 { 363 int time, expire; 364 uint ip; 365 }; 366 367 namespace aiman 368 { 369 extern void removeai(clientinfo *ci); 370 extern void clearai(); 371 extern void checkai(); 372 extern void reqadd(clientinfo *ci, int skill); 373 extern void reqdel(clientinfo *ci); 374 extern void setbotlimit(clientinfo *ci, int limit); 375 extern void setbotbalance(clientinfo *ci, bool balance); 376 extern void changemap(); 377 extern void addclient(clientinfo *ci); 378 extern void changeteam(clientinfo *ci); 379 } 380 381 #define MM_MODE 0xF 382 #define MM_AUTOAPPROVE 0x1000 383 #define MM_PRIVSERV (MM_MODE | MM_AUTOAPPROVE) 384 #define MM_PUBSERV ((1<<MM_OPEN) | (1<<MM_VETO)) 385 #define MM_COOPSERV (MM_AUTOAPPROVE | MM_PUBSERV | (1<<MM_LOCKED)) 386 387 bool notgotitems = true; // true when map has changed and waiting for clients to send item 388 int gamemode = 0; 389 int gamemillis = 0, gamelimit = 0, nextexceeded = 0, gamespeed = 100; 390 bool gamepaused = false, shouldstep = true; 391 392 string smapname = ""; 393 int interm = 0; 394 enet_uint32 lastsend = 0; 395 int mastermode = MM_OPEN, mastermask = MM_PRIVSERV; 396 stream *mapdata = NULL; 397 398 vector<uint> allowedips; 399 vector<ban> bannedips; 400 addban(uint ip,int expire)401 void addban(uint ip, int expire) 402 { 403 allowedips.removeobj(ip); 404 ban b; 405 b.time = totalmillis; 406 b.expire = totalmillis + expire; 407 b.ip = ip; 408 loopv(bannedips) if(bannedips[i].expire - b.expire > 0) { bannedips.insert(i, b); return; } 409 bannedips.add(b); 410 } 411 412 vector<clientinfo *> connects, clients, bots; 413 kickclients(uint ip,clientinfo * actor=NULL,int priv=PRIV_NONE)414 void kickclients(uint ip, clientinfo *actor = NULL, int priv = PRIV_NONE) 415 { 416 loopvrev(clients) 417 { 418 clientinfo &c = *clients[i]; 419 if(c.state.aitype != AI_NONE || c.privilege >= PRIV_ADMIN || c.local) continue; 420 if(actor && ((c.privilege > priv && !actor->local) || c.clientnum == actor->clientnum)) continue; 421 if(getclientip(c.clientnum) == ip) disconnect_client(c.clientnum, DISC_KICK); 422 } 423 } 424 425 struct maprotation 426 { 427 static int exclude; 428 int modes; 429 string map; 430 calcmodemaskserver::maprotation431 int calcmodemask() const { return modes&(1<<NUMGAMEMODES) ? modes & ~exclude : modes; } hasmodeserver::maprotation432 bool hasmode(int mode, int offset = STARTGAMEMODE) const { return (calcmodemask() & (1 << (mode-offset))) != 0; } 433 findmodeserver::maprotation434 int findmode(int mode) const 435 { 436 if(!hasmode(mode)) loopi(NUMGAMEMODES) if(hasmode(i, 0)) return i+STARTGAMEMODE; 437 return mode; 438 } 439 matchserver::maprotation440 bool match(int reqmode, const char *reqmap) const 441 { 442 return hasmode(reqmode) && (!map[0] || !reqmap[0] || !strcmp(map, reqmap)); 443 } 444 includesserver::maprotation445 bool includes(const maprotation &rot) const 446 { 447 return rot.modes == modes ? rot.map[0] && !map[0] : (rot.modes & modes) == rot.modes; 448 } 449 }; 450 int maprotation::exclude = 0; 451 vector<maprotation> maprotations; 452 int curmaprotation = 0; 453 454 VAR(lockmaprotation, 0, 0, 2); 455 maprotationreset()456 void maprotationreset() 457 { 458 maprotations.setsize(0); 459 curmaprotation = 0; 460 maprotation::exclude = 0; 461 } 462 nextmaprotation()463 void nextmaprotation() 464 { 465 curmaprotation++; 466 if(maprotations.inrange(curmaprotation) && maprotations[curmaprotation].modes) return; 467 do curmaprotation--; 468 while(maprotations.inrange(curmaprotation) && maprotations[curmaprotation].modes); 469 curmaprotation++; 470 } 471 findmaprotation(int mode,const char * map)472 int findmaprotation(int mode, const char *map) 473 { 474 for(int i = max(curmaprotation, 0); i < maprotations.length(); i++) 475 { 476 maprotation &rot = maprotations[i]; 477 if(!rot.modes) break; 478 if(rot.match(mode, map)) return i; 479 } 480 int start; 481 for(start = max(curmaprotation, 0) - 1; start >= 0; start--) if(!maprotations[start].modes) break; 482 start++; 483 for(int i = start; i < curmaprotation; i++) 484 { 485 maprotation &rot = maprotations[i]; 486 if(!rot.modes) break; 487 if(rot.match(mode, map)) return i; 488 } 489 int best = -1; 490 loopv(maprotations) 491 { 492 maprotation &rot = maprotations[i]; 493 if(rot.match(mode, map) && (best < 0 || maprotations[best].includes(rot))) best = i; 494 } 495 return best; 496 } 497 searchmodename(const char * haystack,const char * needle)498 bool searchmodename(const char *haystack, const char *needle) 499 { 500 if(!needle[0]) return true; 501 do 502 { 503 if(needle[0] != '.') 504 { 505 haystack = strchr(haystack, needle[0]); 506 if(!haystack) break; 507 haystack++; 508 } 509 const char *h = haystack, *n = needle+1; 510 for(; *h && *n; h++) 511 { 512 if(*h == *n) n++; 513 else if(*h != ' ') break; 514 } 515 if(!*n) return true; 516 if(*n == '.') return !*h; 517 } while(needle[0] != '.'); 518 return false; 519 } 520 genmodemask(vector<char * > & modes)521 int genmodemask(vector<char *> &modes) 522 { 523 int modemask = 0; 524 loopv(modes) 525 { 526 const char *mode = modes[i]; 527 int op = mode[0]; 528 switch(mode[0]) 529 { 530 case '*': 531 modemask |= 1<<NUMGAMEMODES; 532 loopk(NUMGAMEMODES) if(m_checknot(k+STARTGAMEMODE, M_DEMO|M_EDIT|M_LOCAL)) modemask |= 1<<k; 533 continue; 534 case '!': 535 mode++; 536 if(mode[0] != '?') break; 537 case '?': 538 mode++; 539 loopk(NUMGAMEMODES) if(searchmodename(gamemodes[k].name, mode)) 540 { 541 if(op == '!') modemask &= ~(1<<k); 542 else modemask |= 1<<k; 543 } 544 continue; 545 } 546 int modenum = INT_MAX; 547 if(isdigit(mode[0])) modenum = atoi(mode); 548 else loopk(NUMGAMEMODES) if(searchmodename(gamemodes[k].name, mode)) { modenum = k+STARTGAMEMODE; break; } 549 if(!m_valid(modenum)) continue; 550 switch(op) 551 { 552 case '!': modemask &= ~(1 << (modenum - STARTGAMEMODE)); break; 553 default: modemask |= 1 << (modenum - STARTGAMEMODE); break; 554 } 555 } 556 return modemask; 557 } 558 addmaprotation(int modemask,const char * map)559 bool addmaprotation(int modemask, const char *map) 560 { 561 if(!map[0]) loopk(NUMGAMEMODES) if(modemask&(1<<k) && !m_check(k+STARTGAMEMODE, M_EDIT)) modemask &= ~(1<<k); 562 if(!modemask) return false; 563 if(!(modemask&(1<<NUMGAMEMODES))) maprotation::exclude |= modemask; 564 maprotation &rot = maprotations.add(); 565 rot.modes = modemask; 566 copystring(rot.map, map); 567 return true; 568 } 569 addmaprotations(tagval * args,int numargs)570 void addmaprotations(tagval *args, int numargs) 571 { 572 vector<char *> modes, maps; 573 for(int i = 0; i + 1 < numargs; i += 2) 574 { 575 explodelist(args[i].getstr(), modes); 576 explodelist(args[i+1].getstr(), maps); 577 int modemask = genmodemask(modes); 578 if(maps.length()) loopvj(maps) addmaprotation(modemask, maps[j]); 579 else addmaprotation(modemask, ""); 580 modes.deletearrays(); 581 maps.deletearrays(); 582 } 583 if(maprotations.length() && maprotations.last().modes) 584 { 585 maprotation &rot = maprotations.add(); 586 rot.modes = 0; 587 rot.map[0] = '\0'; 588 } 589 } 590 591 COMMAND(maprotationreset, ""); 592 COMMANDN(maprotation, addmaprotations, "ss2V"); 593 594 struct demofile 595 { 596 string info; 597 uchar *data; 598 int len; 599 }; 600 601 vector<demofile> demos; 602 603 bool demonextmatch = false; 604 stream *demotmp = NULL, *demorecord = NULL, *demoplayback = NULL; 605 int nextplayback = 0; 606 607 VAR(maxdemos, 0, 5, 25); 608 VAR(maxdemosize, 0, 16, 31); 609 VAR(restrictdemos, 0, 1, 1); 610 VARF(autorecorddemo, 0, 0, 1, demonextmatch = autorecorddemo!=0); 611 612 VAR(restrictpausegame, 0, 1, 1); 613 VAR(restrictgamespeed, 0, 1, 1); 614 615 SVAR(serverdesc, ""); 616 SVAR(serverpass, ""); 617 SVAR(adminpass, ""); 618 VARF(publicserver, 0, 0, 2, { 619 switch(publicserver) 620 { 621 case 0: default: mastermask = MM_PRIVSERV; break; 622 case 1: mastermask = MM_PUBSERV; break; 623 case 2: mastermask = MM_COOPSERV; break; 624 } 625 }); 626 SVAR(servermotd, ""); 627 628 struct teamkillkick 629 { 630 int modes, limit, ban; 631 matchserver::teamkillkick632 bool match(int mode) const 633 { 634 return (modes&(1<<(mode-STARTGAMEMODE)))!=0; 635 } 636 includesserver::teamkillkick637 bool includes(const teamkillkick &tk) const 638 { 639 return tk.modes != modes && (tk.modes & modes) == tk.modes; 640 } 641 }; 642 vector<teamkillkick> teamkillkicks; 643 teamkillkickreset()644 void teamkillkickreset() 645 { 646 teamkillkicks.setsize(0); 647 } 648 addteamkillkick(char * modestr,int * limit,int * ban)649 void addteamkillkick(char *modestr, int *limit, int *ban) 650 { 651 vector<char *> modes; 652 explodelist(modestr, modes); 653 teamkillkick &kick = teamkillkicks.add(); 654 kick.modes = genmodemask(modes); 655 kick.limit = *limit; 656 kick.ban = *ban > 0 ? *ban*60000 : (*ban < 0 ? 0 : 30*60000); 657 modes.deletearrays(); 658 } 659 660 COMMAND(teamkillkickreset, ""); 661 COMMANDN(teamkillkick, addteamkillkick, "sii"); 662 663 struct teamkillinfo 664 { 665 uint ip; 666 int teamkills; 667 }; 668 vector<teamkillinfo> teamkills; 669 bool shouldcheckteamkills = false; 670 addteamkill(clientinfo * actor,clientinfo * victim,int n)671 void addteamkill(clientinfo *actor, clientinfo *victim, int n) 672 { 673 if(!m_timed || actor->state.aitype != AI_NONE || actor->local || actor->privilege || (victim && victim->state.aitype != AI_NONE)) return; 674 shouldcheckteamkills = true; 675 uint ip = getclientip(actor->clientnum); 676 loopv(teamkills) if(teamkills[i].ip == ip) 677 { 678 teamkills[i].teamkills += n; 679 return; 680 } 681 teamkillinfo &tk = teamkills.add(); 682 tk.ip = ip; 683 tk.teamkills = n; 684 } 685 checkteamkills()686 void checkteamkills() 687 { 688 teamkillkick *kick = NULL; 689 if(m_timed) loopv(teamkillkicks) if(teamkillkicks[i].match(gamemode) && (!kick || kick->includes(teamkillkicks[i]))) 690 kick = &teamkillkicks[i]; 691 if(kick) loopvrev(teamkills) 692 { 693 teamkillinfo &tk = teamkills[i]; 694 if(tk.teamkills >= kick->limit) 695 { 696 if(kick->ban > 0) addban(tk.ip, kick->ban); 697 kickclients(tk.ip); 698 teamkills.removeunordered(i); 699 } 700 } 701 shouldcheckteamkills = false; 702 } 703 newclientinfo()704 void *newclientinfo() { return new clientinfo; } deleteclientinfo(void * ci)705 void deleteclientinfo(void *ci) { delete (clientinfo *)ci; } 706 getinfo(int n)707 clientinfo *getinfo(int n) 708 { 709 if(n < MAXCLIENTS) return (clientinfo *)getclientinfo(n); 710 n -= MAXCLIENTS; 711 return bots.inrange(n) ? bots[n] : NULL; 712 } 713 714 uint mcrc = 0; 715 vector<entity> ments; 716 vector<server_entity> sents; 717 vector<savedscore> scores; 718 msgsizelookup(int msg)719 int msgsizelookup(int msg) 720 { 721 static int sizetable[NUMMSG] = { -1 }; 722 if(sizetable[0] < 0) 723 { 724 memset(sizetable, -1, sizeof(sizetable)); 725 for(const int *p = msgsizes; *p >= 0; p += 2) sizetable[p[0]] = p[1]; 726 } 727 return msg >= 0 && msg < NUMMSG ? sizetable[msg] : -1; 728 } 729 modename(int n,const char * unknown)730 const char *modename(int n, const char *unknown) 731 { 732 if(m_valid(n)) return gamemodes[n - STARTGAMEMODE].name; 733 return unknown; 734 } 735 mastermodename(int n,const char * unknown)736 const char *mastermodename(int n, const char *unknown) 737 { 738 return (n>=MM_START && size_t(n-MM_START)<sizeof(mastermodenames)/sizeof(mastermodenames[0])) ? mastermodenames[n-MM_START] : unknown; 739 } 740 privname(int type)741 const char *privname(int type) 742 { 743 switch(type) 744 { 745 case PRIV_ADMIN: return "admin"; 746 case PRIV_AUTH: return "auth"; 747 case PRIV_MASTER: return "master"; 748 default: return "unknown"; 749 } 750 } 751 sendservmsg(const char * s)752 void sendservmsg(const char *s) { sendf(-1, 1, "ris", N_SERVMSG, s); } sendservmsgf(const char * fmt,...)753 void sendservmsgf(const char *fmt, ...) 754 { 755 defvformatstring(s, fmt, fmt); 756 sendf(-1, 1, "ris", N_SERVMSG, s); 757 } 758 resetitems()759 void resetitems() 760 { 761 mcrc = 0; 762 ments.setsize(0); 763 sents.setsize(0); 764 //cps.reset(); 765 } 766 serveroption(const char * arg)767 bool serveroption(const char *arg) 768 { 769 if(arg[0]=='-') switch(arg[1]) 770 { 771 case 'n': setsvar("serverdesc", &arg[2]); return true; 772 case 'y': setsvar("serverpass", &arg[2]); return true; 773 case 'p': setsvar("adminpass", &arg[2]); return true; 774 case 'o': setvar("publicserver", atoi(&arg[2])); return true; 775 } 776 return false; 777 } 778 serverinit()779 void serverinit() 780 { 781 smapname[0] = '\0'; 782 resetitems(); 783 } 784 numclients(int exclude=-1,bool nospec=true,bool noai=true,bool priv=false)785 int numclients(int exclude = -1, bool nospec = true, bool noai = true, bool priv = false) 786 { 787 int n = 0; 788 loopv(clients) 789 { 790 clientinfo *ci = clients[i]; 791 if(ci->clientnum!=exclude && (!nospec || ci->state.state!=CS_SPECTATOR || (priv && (ci->privilege || ci->local))) && (!noai || ci->state.aitype == AI_NONE)) n++; 792 } 793 return n; 794 } 795 duplicatename(clientinfo * ci,const char * name)796 bool duplicatename(clientinfo *ci, const char *name) 797 { 798 if(!name) name = ci->name; 799 loopv(clients) if(clients[i]!=ci && !strcmp(name, clients[i]->name)) return true; 800 return false; 801 } 802 colorname(clientinfo * ci,const char * name=NULL)803 const char *colorname(clientinfo *ci, const char *name = NULL) 804 { 805 if(!name) name = ci->name; 806 if(name[0] && !duplicatename(ci, name) && ci->state.aitype == AI_NONE) return name; 807 static string cname[3]; 808 static int cidx = 0; 809 cidx = (cidx+1)%3; 810 formatstring(cname[cidx], ci->state.aitype == AI_NONE ? "%s \fs\f5(%d)\fr" : "%s \fs\f5[%d]\fr", name, ci->clientnum); 811 return cname[cidx]; 812 } 813 814 struct servmode 815 { ~servmodeserver::servmode816 virtual ~servmode() {} 817 entergameserver::servmode818 virtual void entergame(clientinfo *ci) {} leavegameserver::servmode819 virtual void leavegame(clientinfo *ci, bool disconnecting = false) {} 820 movedserver::servmode821 virtual void moved(clientinfo *ci, const vec &oldpos, bool oldclip, const vec &newpos, bool newclip) {} canspawnserver::servmode822 virtual bool canspawn(clientinfo *ci, bool connecting = false) { return true; } spawnedserver::servmode823 virtual void spawned(clientinfo *ci) {} fragvalueserver::servmode824 virtual int fragvalue(clientinfo *victim, clientinfo *actor) 825 { 826 if(victim==actor || isteam(victim->team, actor->team)) return -1; 827 return 1; 828 } diedserver::servmode829 virtual void died(clientinfo *victim, clientinfo *actor) {} canchangeteamserver::servmode830 virtual bool canchangeteam(clientinfo *ci, const char *oldteam, const char *newteam) { return true; } changeteamserver::servmode831 virtual void changeteam(clientinfo *ci, const char *oldteam, const char *newteam) {} initclientserver::servmode832 virtual void initclient(clientinfo *ci, packetbuf &p, bool connecting) {} updateserver::servmode833 virtual void update() {} cleanupserver::servmode834 virtual void cleanup() {} setupserver::servmode835 virtual void setup() {} newmapserver::servmode836 virtual void newmap() {} intermissionserver::servmode837 virtual void intermission() {} hidefragsserver::servmode838 virtual bool hidefrags() { return false; } getteamscoreserver::servmode839 virtual int getteamscore(const char *team) { return 0; } getteamscoresserver::servmode840 virtual void getteamscores(vector<teamscore> &scores) {} extinfoteamserver::servmode841 virtual bool extinfoteam(const char *team, ucharbuf &p) { return false; } 842 }; 843 844 #define SERVMODE 1 845 #include "capture.h" 846 #include "ctf.h" 847 #include "collect.h" 848 849 captureservmode capturemode; 850 ctfservmode ctfmode; 851 collectservmode collectmode; 852 servmode *smode = NULL; 853 canspawnitem(int type)854 bool canspawnitem(int type) { return !m_noitems && (type>=I_SHELLS && type<=I_QUAD && (!m_noammo || type<I_SHELLS || type>I_CARTRIDGES)); } 855 spawntime(int type)856 int spawntime(int type) 857 { 858 if(m_classicsp) return INT_MAX; 859 int np = numclients(-1, true, false); 860 np = np<3 ? 4 : (np>4 ? 2 : 3); // spawn times are dependent on number of players 861 int sec = 0; 862 switch(type) 863 { 864 case I_SHELLS: 865 case I_BULLETS: 866 case I_ROCKETS: 867 case I_ROUNDS: 868 case I_GRENADES: 869 case I_CARTRIDGES: sec = np*4; break; 870 case I_HEALTH: sec = np*5; break; 871 case I_GREENARMOUR: sec = 20; break; 872 case I_YELLOWARMOUR: sec = 30; break; 873 case I_BOOST: sec = 60; break; 874 case I_QUAD: sec = 70; break; 875 } 876 return sec*1000; 877 } 878 delayspawn(int type)879 bool delayspawn(int type) 880 { 881 switch(type) 882 { 883 case I_GREENARMOUR: 884 case I_YELLOWARMOUR: 885 return !m_classicsp; 886 case I_BOOST: 887 case I_QUAD: 888 return true; 889 default: 890 return false; 891 } 892 } 893 pickup(int i,int sender)894 bool pickup(int i, int sender) // server side item pickup, acknowledge first client that gets it 895 { 896 if((m_timed && gamemillis>=gamelimit) || !sents.inrange(i) || !sents[i].spawned) return false; 897 clientinfo *ci = getinfo(sender); 898 if(!ci) return false; 899 if(!ci->local && !ci->state.canpickup(sents[i].type)) 900 { 901 sendf(sender, 1, "ri3", N_ITEMACC, i, -1); 902 return false; 903 } 904 sents[i].spawned = false; 905 sents[i].spawntime = spawntime(sents[i].type); 906 sendf(-1, 1, "ri3", N_ITEMACC, i, sender); 907 ci->state.pickup(sents[i].type); 908 return true; 909 } 910 911 static hashset<teaminfo> teaminfos; 912 clearteaminfo()913 void clearteaminfo() 914 { 915 teaminfos.clear(); 916 } 917 teamhasplayers(const char * team)918 bool teamhasplayers(const char *team) { loopv(clients) if(!strcmp(clients[i]->team, team)) return true; return false; } 919 pruneteaminfo()920 bool pruneteaminfo() 921 { 922 int oldteams = teaminfos.numelems; 923 enumerate(teaminfos, teaminfo, old, 924 if(!old.frags && !teamhasplayers(old.team)) teaminfos.remove(old.team); 925 ); 926 return teaminfos.numelems < oldteams; 927 } 928 addteaminfo(const char * team)929 teaminfo *addteaminfo(const char *team) 930 { 931 teaminfo *t = teaminfos.access(team); 932 if(!t) 933 { 934 if(teaminfos.numelems >= MAXTEAMS && !pruneteaminfo()) return NULL; 935 t = &teaminfos[team]; 936 copystring(t->team, team, sizeof(t->team)); 937 t->frags = 0; 938 } 939 return t; 940 } 941 choosebestclient(float & bestrank)942 clientinfo *choosebestclient(float &bestrank) 943 { 944 clientinfo *best = NULL; 945 bestrank = -1; 946 loopv(clients) 947 { 948 clientinfo *ci = clients[i]; 949 if(ci->state.timeplayed<0) continue; 950 float rank = ci->state.state!=CS_SPECTATOR ? ci->state.effectiveness/max(ci->state.timeplayed, 1) : -1; 951 if(!best || rank > bestrank) { best = ci; bestrank = rank; } 952 } 953 return best; 954 } 955 956 VAR(persistteams, 0, 0, 1); 957 autoteam()958 void autoteam() 959 { 960 static const char * const teamnames[2] = {"good", "evil"}; 961 vector<clientinfo *> team[2]; 962 float teamrank[2] = {0, 0}; 963 for(int round = 0, remaining = clients.length(); remaining>=0; round++) 964 { 965 int first = round&1, second = (round+1)&1, selected = 0; 966 while(teamrank[first] <= teamrank[second]) 967 { 968 float rank; 969 clientinfo *ci = choosebestclient(rank); 970 if(!ci) break; 971 if(smode && smode->hidefrags()) rank = 1; 972 else if(selected && rank<=0) break; 973 ci->state.timeplayed = -1; 974 team[first].add(ci); 975 if(rank>0) teamrank[first] += rank; 976 selected++; 977 if(rank<=0) break; 978 } 979 if(!selected) break; 980 remaining -= selected; 981 } 982 loopi(sizeof(team)/sizeof(team[0])) 983 { 984 addteaminfo(teamnames[i]); 985 loopvj(team[i]) 986 { 987 clientinfo *ci = team[i][j]; 988 if(!strcmp(ci->team, teamnames[i])) continue; 989 if(persistteams && ci->team[0] && (!smode || smode->canchangeteam(ci, teamnames[i], ci->team))) 990 { 991 addteaminfo(ci->team); 992 continue; 993 } 994 copystring(ci->team, teamnames[i], MAXTEAMLEN+1); 995 sendf(-1, 1, "riisi", N_SETTEAM, ci->clientnum, teamnames[i], -1); 996 } 997 } 998 } 999 1000 struct teamrank 1001 { 1002 const char *name; 1003 float rank; 1004 int clients; 1005 teamrankserver::teamrank1006 teamrank(const char *name) : name(name), rank(0), clients(0) {} 1007 }; 1008 chooseworstteam(const char * suggest=NULL,clientinfo * exclude=NULL)1009 const char *chooseworstteam(const char *suggest = NULL, clientinfo *exclude = NULL) 1010 { 1011 teamrank teamranks[2] = { teamrank("good"), teamrank("evil") }; 1012 const int numteams = sizeof(teamranks)/sizeof(teamranks[0]); 1013 loopv(clients) 1014 { 1015 clientinfo *ci = clients[i]; 1016 if(ci==exclude || ci->state.aitype!=AI_NONE || ci->state.state==CS_SPECTATOR || !ci->team[0]) continue; 1017 ci->state.timeplayed += lastmillis - ci->state.lasttimeplayed; 1018 ci->state.lasttimeplayed = lastmillis; 1019 1020 loopj(numteams) if(!strcmp(ci->team, teamranks[j].name)) 1021 { 1022 teamrank &ts = teamranks[j]; 1023 ts.rank += ci->state.effectiveness/max(ci->state.timeplayed, 1); 1024 ts.clients++; 1025 break; 1026 } 1027 } 1028 teamrank *worst = &teamranks[numteams-1]; 1029 loopi(numteams-1) 1030 { 1031 teamrank &ts = teamranks[i]; 1032 if(smode && smode->hidefrags()) 1033 { 1034 if(ts.clients < worst->clients || (ts.clients == worst->clients && ts.rank < worst->rank)) worst = &ts; 1035 } 1036 else if(ts.rank < worst->rank || (ts.rank == worst->rank && ts.clients < worst->clients)) worst = &ts; 1037 } 1038 return worst->name; 1039 } 1040 prunedemos(int extra=0)1041 void prunedemos(int extra = 0) 1042 { 1043 int n = clamp(demos.length() + extra - maxdemos, 0, demos.length()); 1044 if(n <= 0) return; 1045 loopi(n) delete[] demos[i].data; 1046 demos.remove(0, n); 1047 } 1048 adddemo()1049 void adddemo() 1050 { 1051 if(!demotmp) return; 1052 int len = (int)min(demotmp->size(), stream::offset((maxdemosize<<20) + 0x10000)); 1053 demofile &d = demos.add(); 1054 time_t t = time(NULL); 1055 char *timestr = ctime(&t), *trim = timestr + strlen(timestr); 1056 while(trim>timestr && iscubespace(*--trim)) *trim = '\0'; 1057 formatstring(d.info, "%s: %s, %s, %.2f%s", timestr, modename(gamemode), smapname, len > 1024*1024 ? len/(1024*1024.f) : len/1024.0f, len > 1024*1024 ? "MB" : "kB"); 1058 sendservmsgf("demo \"%s\" recorded", d.info); 1059 d.data = new uchar[len]; 1060 d.len = len; 1061 demotmp->seek(0, SEEK_SET); 1062 demotmp->read(d.data, len); 1063 DELETEP(demotmp); 1064 } 1065 enddemorecord()1066 void enddemorecord() 1067 { 1068 if(!demorecord) return; 1069 1070 DELETEP(demorecord); 1071 1072 if(!demotmp) return; 1073 if(!maxdemos || !maxdemosize) { DELETEP(demotmp); return; } 1074 1075 prunedemos(1); 1076 adddemo(); 1077 } 1078 writedemo(int chan,void * data,int len)1079 void writedemo(int chan, void *data, int len) 1080 { 1081 if(!demorecord) return; 1082 int stamp[3] = { gamemillis, chan, len }; 1083 lilswap(stamp, 3); 1084 demorecord->write(stamp, sizeof(stamp)); 1085 demorecord->write(data, len); 1086 if(demorecord->rawtell() >= (maxdemosize<<20)) enddemorecord(); 1087 } 1088 recordpacket(int chan,void * data,int len)1089 void recordpacket(int chan, void *data, int len) 1090 { 1091 writedemo(chan, data, len); 1092 } 1093 1094 int welcomepacket(packetbuf &p, clientinfo *ci); 1095 void sendwelcome(clientinfo *ci); 1096 setupdemorecord()1097 void setupdemorecord() 1098 { 1099 if(!m_mp(gamemode) || m_edit) return; 1100 1101 demotmp = opentempfile("demorecord", "w+b"); 1102 if(!demotmp) return; 1103 1104 stream *f = opengzfile(NULL, "wb", demotmp); 1105 if(!f) { DELETEP(demotmp); return; } 1106 1107 sendservmsg("recording demo"); 1108 1109 demorecord = f; 1110 1111 demoheader hdr; 1112 memcpy(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic)); 1113 hdr.version = DEMO_VERSION; 1114 hdr.protocol = PROTOCOL_VERSION; 1115 lilswap(&hdr.version, 2); 1116 demorecord->write(&hdr, sizeof(demoheader)); 1117 1118 packetbuf p(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); 1119 welcomepacket(p, NULL); 1120 writedemo(1, p.buf, p.len); 1121 } 1122 listdemos(int cn)1123 void listdemos(int cn) 1124 { 1125 packetbuf p(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); 1126 putint(p, N_SENDDEMOLIST); 1127 putint(p, demos.length()); 1128 loopv(demos) sendstring(demos[i].info, p); 1129 sendpacket(cn, 1, p.finalize()); 1130 } 1131 cleardemos(int n)1132 void cleardemos(int n) 1133 { 1134 if(!n) 1135 { 1136 loopv(demos) delete[] demos[i].data; 1137 demos.shrink(0); 1138 sendservmsg("cleared all demos"); 1139 } 1140 else if(demos.inrange(n-1)) 1141 { 1142 delete[] demos[n-1].data; 1143 demos.remove(n-1); 1144 sendservmsgf("cleared demo %d", n); 1145 } 1146 } 1147 freegetmap(ENetPacket * packet)1148 static void freegetmap(ENetPacket *packet) 1149 { 1150 loopv(clients) 1151 { 1152 clientinfo *ci = clients[i]; 1153 if(ci->getmap == packet) ci->getmap = NULL; 1154 } 1155 } 1156 freegetdemo(ENetPacket * packet)1157 static void freegetdemo(ENetPacket *packet) 1158 { 1159 loopv(clients) 1160 { 1161 clientinfo *ci = clients[i]; 1162 if(ci->getdemo == packet) ci->getdemo = NULL; 1163 } 1164 } 1165 senddemo(clientinfo * ci,int num,int tag)1166 void senddemo(clientinfo *ci, int num, int tag) 1167 { 1168 if(ci->getdemo) return; 1169 if(!num) num = demos.length(); 1170 if(!demos.inrange(num-1)) return; 1171 demofile &d = demos[num-1]; 1172 if((ci->getdemo = sendf(ci->clientnum, 2, "riim", N_SENDDEMO, tag, d.len, d.data))) 1173 ci->getdemo->freeCallback = freegetdemo; 1174 } 1175 enddemoplayback()1176 void enddemoplayback() 1177 { 1178 if(!demoplayback) return; 1179 DELETEP(demoplayback); 1180 1181 loopv(clients) sendf(clients[i]->clientnum, 1, "ri3", N_DEMOPLAYBACK, 0, clients[i]->clientnum); 1182 1183 sendservmsg("demo playback finished"); 1184 1185 loopv(clients) sendwelcome(clients[i]); 1186 } 1187 1188 SVARP(demodir, "demo"); 1189 getdemofile(const char * file,bool init)1190 const char *getdemofile(const char *file, bool init) 1191 { 1192 if(!demodir[0]) return NULL; 1193 static string buf; 1194 copystring(buf, demodir); 1195 int dirlen = strlen(buf); 1196 if(buf[dirlen] != '/' && buf[dirlen] != '\\' && dirlen+1 < (int)sizeof(buf)) { buf[dirlen++] = '/'; buf[dirlen] = '\0'; } 1197 if(init) 1198 { 1199 const char *dir = findfile(buf, "w"); 1200 if(!fileexists(dir, "w")) createdir(dir); 1201 } 1202 concatstring(buf, file); 1203 return buf; 1204 } 1205 setupdemoplayback()1206 void setupdemoplayback() 1207 { 1208 if(demoplayback) return; 1209 demoheader hdr; 1210 string msg; 1211 msg[0] = '\0'; 1212 string file; 1213 copystring(file, smapname); 1214 int len = strlen(file); 1215 if(len < 4 || strcasecmp(&file[len-4], ".dmo")) concatstring(file, ".dmo"); 1216 if(const char *buf = getdemofile(file, false)) demoplayback = opengzfile(buf, "rb"); 1217 if(!demoplayback) demoplayback = opengzfile(file, "rb"); 1218 if(!demoplayback) formatstring(msg, "could not read demo \"%s\"", file); 1219 else if(demoplayback->read(&hdr, sizeof(demoheader))!=sizeof(demoheader) || memcmp(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic))) 1220 formatstring(msg, "\"%s\" is not a demo file", file); 1221 else 1222 { 1223 lilswap(&hdr.version, 2); 1224 if(hdr.version!=DEMO_VERSION) formatstring(msg, "demo \"%s\" requires an %s version of Cube 2: Sauerbraten", file, hdr.version<DEMO_VERSION ? "older" : "newer"); 1225 else if(hdr.protocol!=PROTOCOL_VERSION) formatstring(msg, "demo \"%s\" requires an %s version of Cube 2: Sauerbraten", file, hdr.protocol<PROTOCOL_VERSION ? "older" : "newer"); 1226 } 1227 if(msg[0]) 1228 { 1229 DELETEP(demoplayback); 1230 sendservmsg(msg); 1231 return; 1232 } 1233 1234 sendservmsgf("playing demo \"%s\"", file); 1235 1236 sendf(-1, 1, "ri3", N_DEMOPLAYBACK, 1, -1); 1237 1238 if(demoplayback->read(&nextplayback, sizeof(nextplayback))!=sizeof(nextplayback)) 1239 { 1240 enddemoplayback(); 1241 return; 1242 } 1243 lilswap(&nextplayback, 1); 1244 } 1245 readdemo()1246 void readdemo() 1247 { 1248 if(!demoplayback) return; 1249 while(gamemillis>=nextplayback) 1250 { 1251 int chan, len; 1252 if(demoplayback->read(&chan, sizeof(chan))!=sizeof(chan) || 1253 demoplayback->read(&len, sizeof(len))!=sizeof(len)) 1254 { 1255 enddemoplayback(); 1256 return; 1257 } 1258 lilswap(&chan, 1); 1259 lilswap(&len, 1); 1260 ENetPacket *packet = enet_packet_create(NULL, len+1, 0); 1261 if(!packet || demoplayback->read(packet->data+1, len)!=size_t(len)) 1262 { 1263 if(packet) enet_packet_destroy(packet); 1264 enddemoplayback(); 1265 return; 1266 } 1267 packet->data[0] = N_DEMOPACKET; 1268 sendpacket(-1, chan, packet); 1269 if(!packet->referenceCount) enet_packet_destroy(packet); 1270 if(!demoplayback) break; 1271 if(demoplayback->read(&nextplayback, sizeof(nextplayback))!=sizeof(nextplayback)) 1272 { 1273 enddemoplayback(); 1274 return; 1275 } 1276 lilswap(&nextplayback, 1); 1277 } 1278 } 1279 timeupdate(int secs)1280 void timeupdate(int secs) 1281 { 1282 if(!demoplayback) return; 1283 if(secs <= 0) interm = -1; 1284 else gamelimit = max(gamelimit, nextplayback + secs*1000); 1285 } 1286 seekdemo(char * t)1287 void seekdemo(char *t) 1288 { 1289 if(!demoplayback) return; 1290 bool rev = *t == '-'; 1291 if(rev) t++; 1292 int mins = strtoul(t, &t, 10), secs = 0, millis = 0; 1293 if(*t == ':') secs = strtoul(t+1, &t, 10); 1294 else { secs = mins; mins = 0; } 1295 if(*t == '.') millis = strtoul(t+1, &t, 10); 1296 int offset = max(millis + (mins*60 + secs)*1000, 0), prevmillis = gamemillis; 1297 if(rev) while(gamelimit - offset > gamemillis) 1298 { 1299 gamemillis = gamelimit - offset; 1300 readdemo(); 1301 } 1302 else if(offset > gamemillis) 1303 { 1304 gamemillis = offset; 1305 readdemo(); 1306 } 1307 if(gamemillis > prevmillis) 1308 { 1309 if(!interm) sendf(-1, 1, "ri2", N_TIMEUP, max((gamelimit - gamemillis)/1000, 1)); 1310 #ifndef STANDALONE 1311 cleardamagescreen(); 1312 #endif 1313 } 1314 } 1315 1316 ICOMMAND(seekdemo, "sN$", (char *t, int *numargs, ident *id), 1317 { 1318 if(*numargs > 0) seekdemo(t); 1319 else 1320 { 1321 int secs = gamemillis/1000; 1322 defformatstring(str, "%d:%02d.%03d", secs/60, secs%60, gamemillis%1000); 1323 if(*numargs < 0) result(str); 1324 else printsvar(id, str); 1325 } 1326 }); 1327 stopdemo()1328 void stopdemo() 1329 { 1330 if(m_demo) enddemoplayback(); 1331 else enddemorecord(); 1332 } 1333 pausegame(bool val,clientinfo * ci=NULL)1334 void pausegame(bool val, clientinfo *ci = NULL) 1335 { 1336 if(gamepaused==val) return; 1337 gamepaused = val; 1338 sendf(-1, 1, "riii", N_PAUSEGAME, gamepaused ? 1 : 0, ci ? ci->clientnum : -1); 1339 } 1340 checkpausegame()1341 void checkpausegame() 1342 { 1343 if(!gamepaused) return; 1344 int admins = 0; 1345 loopv(clients) if(clients[i]->privilege >= (restrictpausegame ? PRIV_ADMIN : PRIV_MASTER) || clients[i]->local) admins++; 1346 if(!admins) pausegame(false); 1347 } 1348 forcepaused(bool paused)1349 void forcepaused(bool paused) 1350 { 1351 pausegame(paused); 1352 } 1353 ispaused()1354 bool ispaused() { return gamepaused; } 1355 changegamespeed(int val,clientinfo * ci=NULL)1356 void changegamespeed(int val, clientinfo *ci = NULL) 1357 { 1358 val = clamp(val, 10, 1000); 1359 if(gamespeed==val) return; 1360 gamespeed = val; 1361 sendf(-1, 1, "riii", N_GAMESPEED, gamespeed, ci ? ci->clientnum : -1); 1362 } 1363 forcegamespeed(int speed)1364 void forcegamespeed(int speed) 1365 { 1366 changegamespeed(speed); 1367 } 1368 scaletime(int t)1369 int scaletime(int t) { return t*gamespeed; } 1370 1371 SVAR(serverauth, ""); 1372 1373 struct userkey 1374 { 1375 char *name; 1376 char *desc; 1377 userkeyserver::userkey1378 userkey() : name(NULL), desc(NULL) {} userkeyserver::userkey1379 userkey(char *name, char *desc) : name(name), desc(desc) {} 1380 }; 1381 hthash(const userkey & k)1382 static inline uint hthash(const userkey &k) { return ::hthash(k.name); } htcmp(const userkey & x,const userkey & y)1383 static inline bool htcmp(const userkey &x, const userkey &y) { return !strcmp(x.name, y.name) && !strcmp(x.desc, y.desc); } 1384 1385 struct userinfo : userkey 1386 { 1387 void *pubkey; 1388 int privilege; 1389 userinfoserver::userinfo1390 userinfo() : pubkey(NULL), privilege(PRIV_NONE) {} ~userinfoserver::userinfo1391 ~userinfo() { delete[] name; delete[] desc; if(pubkey) freepubkey(pubkey); } 1392 }; 1393 hashset<userinfo> users; 1394 adduser(char * name,char * desc,char * pubkey,char * priv)1395 void adduser(char *name, char *desc, char *pubkey, char *priv) 1396 { 1397 userkey key(name, desc); 1398 userinfo &u = users[key]; 1399 if(u.pubkey) { freepubkey(u.pubkey); u.pubkey = NULL; } 1400 if(!u.name) u.name = newstring(name); 1401 if(!u.desc) u.desc = newstring(desc); 1402 u.pubkey = parsepubkey(pubkey); 1403 switch(priv[0]) 1404 { 1405 case 'a': case 'A': u.privilege = PRIV_ADMIN; break; 1406 case 'm': case 'M': default: u.privilege = PRIV_AUTH; break; 1407 case 'n': case 'N': u.privilege = PRIV_NONE; break; 1408 } 1409 } 1410 COMMAND(adduser, "ssss"); 1411 clearusers()1412 void clearusers() 1413 { 1414 users.clear(); 1415 } 1416 COMMAND(clearusers, ""); 1417 hashpassword(int cn,int sessionid,const char * pwd,char * result,int maxlen)1418 void hashpassword(int cn, int sessionid, const char *pwd, char *result, int maxlen) 1419 { 1420 char buf[2*sizeof(string)]; 1421 formatstring(buf, "%d %d ", cn, sessionid); 1422 concatstring(buf, pwd, sizeof(buf)); 1423 if(!hashstring(buf, result, maxlen)) *result = '\0'; 1424 } 1425 checkpassword(clientinfo * ci,const char * wanted,const char * given)1426 bool checkpassword(clientinfo *ci, const char *wanted, const char *given) 1427 { 1428 string hash; 1429 hashpassword(ci->clientnum, ci->sessionid, wanted, hash, sizeof(hash)); 1430 return !strcmp(hash, given); 1431 } 1432 revokemaster(clientinfo * ci)1433 void revokemaster(clientinfo *ci) 1434 { 1435 ci->privilege = PRIV_NONE; 1436 if(ci->state.state==CS_SPECTATOR && !ci->local) aiman::removeai(ci); 1437 } 1438 1439 extern void connected(clientinfo *ci); 1440 setmaster(clientinfo * ci,bool val,const char * pass="",const char * authname=NULL,const char * authdesc=NULL,int authpriv=PRIV_MASTER,bool force=false,bool trial=false)1441 bool setmaster(clientinfo *ci, bool val, const char *pass = "", const char *authname = NULL, const char *authdesc = NULL, int authpriv = PRIV_MASTER, bool force = false, bool trial = false) 1442 { 1443 if(authname && !val) return false; 1444 const char *name = ""; 1445 if(val) 1446 { 1447 bool haspass = adminpass[0] && checkpassword(ci, adminpass, pass); 1448 int wantpriv = ci->local || haspass ? PRIV_ADMIN : authpriv; 1449 if(wantpriv <= ci->privilege) return true; 1450 else if(wantpriv <= PRIV_MASTER && !force) 1451 { 1452 if(ci->state.state==CS_SPECTATOR) 1453 { 1454 sendf(ci->clientnum, 1, "ris", N_SERVMSG, "Spectators may not claim master."); 1455 return false; 1456 } 1457 loopv(clients) if(ci!=clients[i] && clients[i]->privilege) 1458 { 1459 sendf(ci->clientnum, 1, "ris", N_SERVMSG, "Master is already claimed."); 1460 return false; 1461 } 1462 if(!authname && !(mastermask&MM_AUTOAPPROVE) && !ci->privilege && !ci->local) 1463 { 1464 sendf(ci->clientnum, 1, "ris", N_SERVMSG, "This server requires you to use the \"/auth\" command to claim master."); 1465 return false; 1466 } 1467 } 1468 if(trial) return true; 1469 ci->privilege = wantpriv; 1470 name = privname(ci->privilege); 1471 } 1472 else 1473 { 1474 if(!ci->privilege) return false; 1475 if(trial) return true; 1476 name = privname(ci->privilege); 1477 revokemaster(ci); 1478 } 1479 bool hasmaster = false; 1480 loopv(clients) if(clients[i]->local || clients[i]->privilege >= PRIV_MASTER) hasmaster = true; 1481 if(!hasmaster) 1482 { 1483 mastermode = MM_OPEN; 1484 allowedips.shrink(0); 1485 } 1486 string msg; 1487 if(val && authname) 1488 { 1489 if(authdesc && authdesc[0]) formatstring(msg, "%s claimed %s as '\fs\f5%s\fr' [\fs\f0%s\fr]", colorname(ci), name, authname, authdesc); 1490 else formatstring(msg, "%s claimed %s as '\fs\f5%s\fr'", colorname(ci), name, authname); 1491 } 1492 else formatstring(msg, "%s %s %s", colorname(ci), val ? "claimed" : "relinquished", name); 1493 packetbuf p(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); 1494 putint(p, N_SERVMSG); 1495 sendstring(msg, p); 1496 putint(p, N_CURRENTMASTER); 1497 putint(p, mastermode); 1498 loopv(clients) if(clients[i]->privilege >= PRIV_MASTER) 1499 { 1500 putint(p, clients[i]->clientnum); 1501 putint(p, clients[i]->privilege); 1502 } 1503 putint(p, -1); 1504 sendpacket(-1, 1, p.finalize()); 1505 checkpausegame(); 1506 return true; 1507 } 1508 trykick(clientinfo * ci,int victim,const char * reason=NULL,const char * authname=NULL,const char * authdesc=NULL,int authpriv=PRIV_NONE,bool trial=false)1509 bool trykick(clientinfo *ci, int victim, const char *reason = NULL, const char *authname = NULL, const char *authdesc = NULL, int authpriv = PRIV_NONE, bool trial = false) 1510 { 1511 int priv = ci->privilege; 1512 if(authname) 1513 { 1514 if(priv >= authpriv || ci->local) authname = authdesc = NULL; 1515 else priv = authpriv; 1516 } 1517 if((priv || ci->local) && ci->clientnum!=victim) 1518 { 1519 clientinfo *vinfo = (clientinfo *)getclientinfo(victim); 1520 if(vinfo && vinfo->connected && (priv >= vinfo->privilege || ci->local) && vinfo->privilege < PRIV_ADMIN && !vinfo->local) 1521 { 1522 if(trial) return true; 1523 string kicker; 1524 if(authname) 1525 { 1526 if(authdesc && authdesc[0]) formatstring(kicker, "%s as '\fs\f5%s\fr' [\fs\f0%s\fr]", colorname(ci), authname, authdesc); 1527 else formatstring(kicker, "%s as '\fs\f5%s\fr'", colorname(ci), authname); 1528 } 1529 else copystring(kicker, colorname(ci)); 1530 if(reason && reason[0]) sendservmsgf("%s kicked %s because: %s", kicker, colorname(vinfo), reason); 1531 else sendservmsgf("%s kicked %s", kicker, colorname(vinfo)); 1532 uint ip = getclientip(victim); 1533 addban(ip, 4*60*60000); 1534 kickclients(ip, ci, priv); 1535 } 1536 } 1537 return false; 1538 } 1539 findscore(clientinfo * ci,bool insert)1540 savedscore *findscore(clientinfo *ci, bool insert) 1541 { 1542 uint ip = getclientip(ci->clientnum); 1543 if(!ip && !ci->local) return 0; 1544 if(!insert) 1545 { 1546 loopv(clients) 1547 { 1548 clientinfo *oi = clients[i]; 1549 if(oi->clientnum != ci->clientnum && getclientip(oi->clientnum) == ip && !strcmp(oi->name, ci->name)) 1550 { 1551 oi->state.timeplayed += lastmillis - oi->state.lasttimeplayed; 1552 oi->state.lasttimeplayed = lastmillis; 1553 static savedscore curscore; 1554 curscore.save(oi->state); 1555 return &curscore; 1556 } 1557 } 1558 } 1559 loopv(scores) 1560 { 1561 savedscore &sc = scores[i]; 1562 if(sc.ip == ip && !strcmp(sc.name, ci->name)) return ≻ 1563 } 1564 if(!insert) return 0; 1565 savedscore &sc = scores.add(); 1566 sc.ip = ip; 1567 copystring(sc.name, ci->name); 1568 return ≻ 1569 } 1570 savescore(clientinfo * ci)1571 void savescore(clientinfo *ci) 1572 { 1573 savedscore *sc = findscore(ci, true); 1574 if(sc) sc->save(ci->state); 1575 } 1576 1577 static struct msgfilter 1578 { 1579 uchar msgmask[NUMMSG]; 1580 msgfilterserver::msgfilter1581 msgfilter(int msg, ...) 1582 { 1583 memset(msgmask, 0, sizeof(msgmask)); 1584 va_list msgs; 1585 va_start(msgs, msg); 1586 for(uchar val = 1; msg < NUMMSG; msg = va_arg(msgs, int)) 1587 { 1588 if(msg < 0) val = uchar(-msg); 1589 else msgmask[msg] = val; 1590 } 1591 va_end(msgs); 1592 } 1593 operator []server::msgfilter1594 uchar operator[](int msg) const { return msg >= 0 && msg < NUMMSG ? msgmask[msg] : 0; } 1595 } msgfilter(-1, N_CONNECT, N_SERVINFO, N_INITCLIENT, N_WELCOME, N_MAPCHANGE, N_SERVMSG, N_DAMAGE, N_HITPUSH, N_SHOTFX, N_EXPLODEFX, N_DIED, N_SPAWNSTATE, N_FORCEDEATH, N_TEAMINFO, N_ITEMACC, N_ITEMSPAWN, N_TIMEUP, N_CDIS, N_CURRENTMASTER, N_PONG, N_RESUME, N_BASESCORE, N_BASEINFO, N_BASEREGEN, N_ANNOUNCE, N_SENDDEMOLIST, N_SENDDEMO, N_DEMOPLAYBACK, N_SENDMAP, N_DROPFLAG, N_SCOREFLAG, N_RETURNFLAG, N_RESETFLAG, N_INVISFLAG, N_CLIENT, N_AUTHCHAL, N_INITAI, N_EXPIRETOKENS, N_DROPTOKENS, N_STEALTOKENS, N_DEMOPACKET, -2, N_REMIP, N_NEWMAP, N_GETMAP, N_SENDMAP, N_CLIPBOARD, -3, N_EDITENT, N_EDITF, N_EDITT, N_EDITM, N_FLIP, N_COPY, N_PASTE, N_ROTATE, N_REPLACE, N_DELCUBE, N_EDITVAR, N_EDITVSLOT, N_UNDO, N_REDO, -4, N_POS, NUMMSG), 1596 connectfilter(-1, N_CONNECT, -2, N_AUTHANS, -3, N_PING, NUMMSG); 1597 checktype(int type,clientinfo * ci)1598 int checktype(int type, clientinfo *ci) 1599 { 1600 if(ci) 1601 { 1602 if(!ci->connected) switch(connectfilter[type]) 1603 { 1604 // allow only before authconnect 1605 case 1: return !ci->connectauth ? type : -1; 1606 // allow only during authconnect 1607 case 2: return ci->connectauth ? type : -1; 1608 // always allow 1609 case 3: return type; 1610 // never allow 1611 default: return -1; 1612 } 1613 if(ci->local) return type; 1614 } 1615 switch(msgfilter[type]) 1616 { 1617 // server-only messages 1618 case 1: return ci ? -1 : type; 1619 // only allowed in coop-edit 1620 case 2: if(m_edit) break; return -1; 1621 // only allowed in coop-edit, no overflow check 1622 case 3: return m_edit ? type : -1; 1623 // no overflow check 1624 case 4: return type; 1625 } 1626 if(ci && ++ci->overflow >= 200) return -2; 1627 return type; 1628 } 1629 1630 struct worldstate 1631 { 1632 int uses, len; 1633 uchar *data; 1634 worldstateserver::worldstate1635 worldstate() : uses(0), len(0), data(NULL) {} 1636 setupserver::worldstate1637 void setup(int n) { len = n; data = new uchar[n]; } cleanupserver::worldstate1638 void cleanup() { DELETEA(data); len = 0; } containsserver::worldstate1639 bool contains(const uchar *p) const { return p >= data && p < &data[len]; } 1640 }; 1641 vector<worldstate> worldstates; 1642 bool reliablemessages = false; 1643 cleanworldstate(ENetPacket * packet)1644 void cleanworldstate(ENetPacket *packet) 1645 { 1646 loopv(worldstates) 1647 { 1648 worldstate &ws = worldstates[i]; 1649 if(!ws.contains(packet->data)) continue; 1650 ws.uses--; 1651 if(ws.uses <= 0) 1652 { 1653 ws.cleanup(); 1654 worldstates.removeunordered(i); 1655 } 1656 break; 1657 } 1658 } 1659 flushclientposition(clientinfo & ci)1660 void flushclientposition(clientinfo &ci) 1661 { 1662 if(ci.position.empty() || (!hasnonlocalclients() && !demorecord)) return; 1663 packetbuf p(ci.position.length(), 0); 1664 p.put(ci.position.getbuf(), ci.position.length()); 1665 ci.position.setsize(0); 1666 sendpacket(-1, 0, p.finalize(), ci.ownernum); 1667 } 1668 sendpositions(worldstate & ws,ucharbuf & wsbuf)1669 static void sendpositions(worldstate &ws, ucharbuf &wsbuf) 1670 { 1671 if(wsbuf.empty()) return; 1672 int wslen = wsbuf.length(); 1673 recordpacket(0, wsbuf.buf, wslen); 1674 wsbuf.put(wsbuf.buf, wslen); 1675 loopv(clients) 1676 { 1677 clientinfo &ci = *clients[i]; 1678 if(ci.state.aitype != AI_NONE) continue; 1679 uchar *data = wsbuf.buf; 1680 int size = wslen; 1681 if(ci.wsdata >= wsbuf.buf) { data = ci.wsdata + ci.wslen; size -= ci.wslen; } 1682 if(size <= 0) continue; 1683 ENetPacket *packet = enet_packet_create(data, size, ENET_PACKET_FLAG_NO_ALLOCATE); 1684 sendpacket(ci.clientnum, 0, packet); 1685 if(packet->referenceCount) { ws.uses++; packet->freeCallback = cleanworldstate; } 1686 else enet_packet_destroy(packet); 1687 } 1688 wsbuf.offset(wsbuf.length()); 1689 } 1690 addposition(worldstate & ws,ucharbuf & wsbuf,int mtu,clientinfo & bi,clientinfo & ci)1691 static inline void addposition(worldstate &ws, ucharbuf &wsbuf, int mtu, clientinfo &bi, clientinfo &ci) 1692 { 1693 if(bi.position.empty()) return; 1694 if(wsbuf.length() + bi.position.length() > mtu) sendpositions(ws, wsbuf); 1695 int offset = wsbuf.length(); 1696 wsbuf.put(bi.position.getbuf(), bi.position.length()); 1697 bi.position.setsize(0); 1698 int len = wsbuf.length() - offset; 1699 if(ci.wsdata < wsbuf.buf) { ci.wsdata = &wsbuf.buf[offset]; ci.wslen = len; } 1700 else ci.wslen += len; 1701 } 1702 sendmessages(worldstate & ws,ucharbuf & wsbuf)1703 static void sendmessages(worldstate &ws, ucharbuf &wsbuf) 1704 { 1705 if(wsbuf.empty()) return; 1706 int wslen = wsbuf.length(); 1707 recordpacket(1, wsbuf.buf, wslen); 1708 wsbuf.put(wsbuf.buf, wslen); 1709 loopv(clients) 1710 { 1711 clientinfo &ci = *clients[i]; 1712 if(ci.state.aitype != AI_NONE) continue; 1713 uchar *data = wsbuf.buf; 1714 int size = wslen; 1715 if(ci.wsdata >= wsbuf.buf) { data = ci.wsdata + ci.wslen; size -= ci.wslen; } 1716 if(size <= 0) continue; 1717 ENetPacket *packet = enet_packet_create(data, size, (reliablemessages ? ENET_PACKET_FLAG_RELIABLE : 0) | ENET_PACKET_FLAG_NO_ALLOCATE); 1718 sendpacket(ci.clientnum, 1, packet); 1719 if(packet->referenceCount) { ws.uses++; packet->freeCallback = cleanworldstate; } 1720 else enet_packet_destroy(packet); 1721 } 1722 wsbuf.offset(wsbuf.length()); 1723 } 1724 addmessages(worldstate & ws,ucharbuf & wsbuf,int mtu,clientinfo & bi,clientinfo & ci)1725 static inline void addmessages(worldstate &ws, ucharbuf &wsbuf, int mtu, clientinfo &bi, clientinfo &ci) 1726 { 1727 if(bi.messages.empty()) return; 1728 if(wsbuf.length() + 10 + bi.messages.length() > mtu) sendmessages(ws, wsbuf); 1729 int offset = wsbuf.length(); 1730 putint(wsbuf, N_CLIENT); 1731 putint(wsbuf, bi.clientnum); 1732 putuint(wsbuf, bi.messages.length()); 1733 wsbuf.put(bi.messages.getbuf(), bi.messages.length()); 1734 bi.messages.setsize(0); 1735 int len = wsbuf.length() - offset; 1736 if(ci.wsdata < wsbuf.buf) { ci.wsdata = &wsbuf.buf[offset]; ci.wslen = len; } 1737 else ci.wslen += len; 1738 } 1739 buildworldstate()1740 bool buildworldstate() 1741 { 1742 int wsmax = 0; 1743 loopv(clients) 1744 { 1745 clientinfo &ci = *clients[i]; 1746 ci.overflow = 0; 1747 ci.wsdata = NULL; 1748 wsmax += ci.position.length(); 1749 if(ci.messages.length()) wsmax += 10 + ci.messages.length(); 1750 } 1751 if(wsmax <= 0) 1752 { 1753 reliablemessages = false; 1754 return false; 1755 } 1756 worldstate &ws = worldstates.add(); 1757 ws.setup(2*wsmax); 1758 int mtu = getservermtu() - 100; 1759 if(mtu <= 0) mtu = ws.len; 1760 ucharbuf wsbuf(ws.data, ws.len); 1761 loopv(clients) 1762 { 1763 clientinfo &ci = *clients[i]; 1764 if(ci.state.aitype != AI_NONE) continue; 1765 addposition(ws, wsbuf, mtu, ci, ci); 1766 loopvj(ci.bots) addposition(ws, wsbuf, mtu, *ci.bots[j], ci); 1767 } 1768 sendpositions(ws, wsbuf); 1769 loopv(clients) 1770 { 1771 clientinfo &ci = *clients[i]; 1772 if(ci.state.aitype != AI_NONE) continue; 1773 addmessages(ws, wsbuf, mtu, ci, ci); 1774 loopvj(ci.bots) addmessages(ws, wsbuf, mtu, *ci.bots[j], ci); 1775 } 1776 sendmessages(ws, wsbuf); 1777 reliablemessages = false; 1778 if(ws.uses) return true; 1779 ws.cleanup(); 1780 worldstates.drop(); 1781 return false; 1782 } 1783 sendpackets(bool force)1784 bool sendpackets(bool force) 1785 { 1786 if(clients.empty() || (!hasnonlocalclients() && !demorecord)) return false; 1787 enet_uint32 curtime = enet_time_get()-lastsend; 1788 if(curtime<33 && !force) return false; 1789 bool flush = buildworldstate(); 1790 lastsend += curtime - (curtime%33); 1791 return flush; 1792 } 1793 1794 template<class T> sendstate(gamestate & gs,T & p)1795 void sendstate(gamestate &gs, T &p) 1796 { 1797 putint(p, gs.lifesequence); 1798 putint(p, gs.health); 1799 putint(p, gs.maxhealth); 1800 putint(p, gs.armour); 1801 putint(p, gs.armourtype); 1802 putint(p, gs.gunselect); 1803 loopi(GUN_PISTOL-GUN_SG+1) putint(p, gs.ammo[GUN_SG+i]); 1804 } 1805 spawnstate(clientinfo * ci)1806 void spawnstate(clientinfo *ci) 1807 { 1808 gamestate &gs = ci->state; 1809 gs.spawnstate(gamemode); 1810 gs.lifesequence = (gs.lifesequence + 1)&0x7F; 1811 } 1812 sendspawn(clientinfo * ci)1813 void sendspawn(clientinfo *ci) 1814 { 1815 gamestate &gs = ci->state; 1816 spawnstate(ci); 1817 sendf(ci->ownernum, 1, "rii7v", N_SPAWNSTATE, ci->clientnum, gs.lifesequence, 1818 gs.health, gs.maxhealth, 1819 gs.armour, gs.armourtype, 1820 gs.gunselect, GUN_PISTOL-GUN_SG+1, &gs.ammo[GUN_SG]); 1821 gs.lastspawn = gamemillis; 1822 } 1823 sendwelcome(clientinfo * ci)1824 void sendwelcome(clientinfo *ci) 1825 { 1826 packetbuf p(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); 1827 int chan = welcomepacket(p, ci); 1828 sendpacket(ci->clientnum, chan, p.finalize()); 1829 } 1830 putinitclient(clientinfo * ci,packetbuf & p)1831 void putinitclient(clientinfo *ci, packetbuf &p) 1832 { 1833 if(ci->state.aitype != AI_NONE) 1834 { 1835 putint(p, N_INITAI); 1836 putint(p, ci->clientnum); 1837 putint(p, ci->ownernum); 1838 putint(p, ci->state.aitype); 1839 putint(p, ci->state.skill); 1840 putint(p, ci->playermodel); 1841 sendstring(ci->name, p); 1842 sendstring(ci->team, p); 1843 } 1844 else 1845 { 1846 putint(p, N_INITCLIENT); 1847 putint(p, ci->clientnum); 1848 sendstring(ci->name, p); 1849 sendstring(ci->team, p); 1850 putint(p, ci->playermodel); 1851 } 1852 } 1853 welcomeinitclient(packetbuf & p,int exclude=-1)1854 void welcomeinitclient(packetbuf &p, int exclude = -1) 1855 { 1856 loopv(clients) 1857 { 1858 clientinfo *ci = clients[i]; 1859 if(!ci->connected || ci->clientnum == exclude) continue; 1860 1861 putinitclient(ci, p); 1862 } 1863 } 1864 hasmap(clientinfo * ci)1865 bool hasmap(clientinfo *ci) 1866 { 1867 return (m_edit && (clients.length() > 0 || ci->local)) || 1868 (smapname[0] && (!m_timed || gamemillis < gamelimit || (ci->state.state==CS_SPECTATOR && !ci->privilege && !ci->local) || numclients(ci->clientnum, true, true, true))); 1869 } 1870 welcomepacket(packetbuf & p,clientinfo * ci)1871 int welcomepacket(packetbuf &p, clientinfo *ci) 1872 { 1873 putint(p, N_WELCOME); 1874 putint(p, N_MAPCHANGE); 1875 sendstring(smapname, p); 1876 putint(p, gamemode); 1877 putint(p, notgotitems ? 1 : 0); 1878 if(!ci || (m_timed && smapname[0])) 1879 { 1880 putint(p, N_TIMEUP); 1881 putint(p, gamemillis < gamelimit && !interm ? max((gamelimit - gamemillis)/1000, 1) : 0); 1882 } 1883 if(!notgotitems) 1884 { 1885 putint(p, N_ITEMLIST); 1886 loopv(sents) if(sents[i].spawned) 1887 { 1888 putint(p, i); 1889 putint(p, sents[i].type); 1890 } 1891 putint(p, -1); 1892 } 1893 bool hasmaster = false; 1894 if(mastermode != MM_OPEN) 1895 { 1896 putint(p, N_CURRENTMASTER); 1897 putint(p, mastermode); 1898 hasmaster = true; 1899 } 1900 loopv(clients) if(clients[i]->privilege >= PRIV_MASTER) 1901 { 1902 if(!hasmaster) 1903 { 1904 putint(p, N_CURRENTMASTER); 1905 putint(p, mastermode); 1906 hasmaster = true; 1907 } 1908 putint(p, clients[i]->clientnum); 1909 putint(p, clients[i]->privilege); 1910 } 1911 if(hasmaster) putint(p, -1); 1912 if(gamepaused) 1913 { 1914 putint(p, N_PAUSEGAME); 1915 putint(p, 1); 1916 putint(p, -1); 1917 } 1918 if(gamespeed != 100) 1919 { 1920 putint(p, N_GAMESPEED); 1921 putint(p, gamespeed); 1922 putint(p, -1); 1923 } 1924 if(m_teammode) 1925 { 1926 putint(p, N_TEAMINFO); 1927 enumerate(teaminfos, teaminfo, t, 1928 if(t.frags) { sendstring(t.team, p); putint(p, t.frags); } 1929 ); 1930 sendstring("", p); 1931 } 1932 if(ci) 1933 { 1934 putint(p, N_SETTEAM); 1935 putint(p, ci->clientnum); 1936 sendstring(ci->team, p); 1937 putint(p, -1); 1938 } 1939 if(ci && (m_demo || m_mp(gamemode)) && ci->state.state!=CS_SPECTATOR) 1940 { 1941 if(smode && !smode->canspawn(ci, true)) 1942 { 1943 ci->state.state = CS_DEAD; 1944 putint(p, N_FORCEDEATH); 1945 putint(p, ci->clientnum); 1946 sendf(-1, 1, "ri2x", N_FORCEDEATH, ci->clientnum, ci->clientnum); 1947 } 1948 else 1949 { 1950 gamestate &gs = ci->state; 1951 spawnstate(ci); 1952 putint(p, N_SPAWNSTATE); 1953 putint(p, ci->clientnum); 1954 sendstate(gs, p); 1955 gs.lastspawn = gamemillis; 1956 } 1957 } 1958 if(ci && ci->state.state==CS_SPECTATOR) 1959 { 1960 putint(p, N_SPECTATOR); 1961 putint(p, ci->clientnum); 1962 putint(p, 1); 1963 sendf(-1, 1, "ri3x", N_SPECTATOR, ci->clientnum, 1, ci->clientnum); 1964 } 1965 if(!ci || clients.length()>1) 1966 { 1967 putint(p, N_RESUME); 1968 loopv(clients) 1969 { 1970 clientinfo *oi = clients[i]; 1971 if(ci && oi->clientnum==ci->clientnum) continue; 1972 putint(p, oi->clientnum); 1973 putint(p, oi->state.state); 1974 putint(p, oi->state.frags); 1975 putint(p, oi->state.flags); 1976 putint(p, oi->state.deaths); 1977 putint(p, oi->state.quadmillis); 1978 sendstate(oi->state, p); 1979 } 1980 putint(p, -1); 1981 welcomeinitclient(p, ci ? ci->clientnum : -1); 1982 } 1983 if(smode) smode->initclient(ci, p, true); 1984 return 1; 1985 } 1986 restorescore(clientinfo * ci)1987 bool restorescore(clientinfo *ci) 1988 { 1989 //if(ci->local) return false; 1990 savedscore *sc = findscore(ci, false); 1991 if(sc) 1992 { 1993 sc->restore(ci->state); 1994 return true; 1995 } 1996 return false; 1997 } 1998 sendresume(clientinfo * ci)1999 void sendresume(clientinfo *ci) 2000 { 2001 gamestate &gs = ci->state; 2002 sendf(-1, 1, "ri3i4i6vi", N_RESUME, ci->clientnum, gs.state, 2003 gs.frags, gs.flags, gs.deaths, gs.quadmillis, 2004 gs.lifesequence, 2005 gs.health, gs.maxhealth, 2006 gs.armour, gs.armourtype, 2007 gs.gunselect, GUN_PISTOL-GUN_SG+1, &gs.ammo[GUN_SG], -1); 2008 } 2009 sendinitclient(clientinfo * ci)2010 void sendinitclient(clientinfo *ci) 2011 { 2012 packetbuf p(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); 2013 putinitclient(ci, p); 2014 sendpacket(-1, 1, p.finalize(), ci->clientnum); 2015 } 2016 loaditems()2017 void loaditems() 2018 { 2019 resetitems(); 2020 notgotitems = true; 2021 if(m_edit || !loadents(smapname, ments, &mcrc)) 2022 return; 2023 loopv(ments) if(canspawnitem(ments[i].type)) 2024 { 2025 server_entity se = { NOTUSED, 0, false }; 2026 while(sents.length()<=i) sents.add(se); 2027 sents[i].type = ments[i].type; 2028 if(m_mp(gamemode) && delayspawn(sents[i].type)) sents[i].spawntime = spawntime(sents[i].type); 2029 else sents[i].spawned = true; 2030 } 2031 notgotitems = false; 2032 } 2033 changemap(const char * s,int mode)2034 void changemap(const char *s, int mode) 2035 { 2036 stopdemo(); 2037 pausegame(false); 2038 changegamespeed(100); 2039 if(smode) smode->cleanup(); 2040 aiman::clearai(); 2041 2042 gamemode = mode; 2043 gamemillis = 0; 2044 gamelimit = 10*60000; 2045 interm = 0; 2046 nextexceeded = 0; 2047 copystring(smapname, s); 2048 loaditems(); 2049 scores.shrink(0); 2050 shouldcheckteamkills = false; 2051 teamkills.shrink(0); 2052 loopv(clients) 2053 { 2054 clientinfo *ci = clients[i]; 2055 ci->state.timeplayed += lastmillis - ci->state.lasttimeplayed; 2056 } 2057 2058 if(!m_mp(gamemode)) kicknonlocalclients(DISC_LOCAL); 2059 2060 sendf(-1, 1, "risii", N_MAPCHANGE, smapname, gamemode, 1); 2061 2062 if(m_capture) smode = &capturemode; 2063 else if(m_ctf) smode = &ctfmode; 2064 else if(m_collect) smode = &collectmode; 2065 else smode = NULL; 2066 2067 clearteaminfo(); 2068 if(m_teammode) autoteam(); 2069 2070 if(m_timed && smapname[0]) sendf(-1, 1, "ri2", N_TIMEUP, gamemillis < gamelimit && !interm ? max((gamelimit - gamemillis)/1000, 1) : 0); 2071 loopv(clients) 2072 { 2073 clientinfo *ci = clients[i]; 2074 ci->mapchange(); 2075 ci->state.lasttimeplayed = lastmillis; 2076 if(m_mp(gamemode) && ci->state.state!=CS_SPECTATOR) sendspawn(ci); 2077 } 2078 2079 aiman::changemap(); 2080 2081 if(m_demo) 2082 { 2083 if(clients.length()) setupdemoplayback(); 2084 } 2085 else 2086 { 2087 if(demonextmatch) setupdemorecord(); 2088 demonextmatch = autorecorddemo!=0; 2089 } 2090 2091 if(smode) smode->setup(); 2092 } 2093 rotatemap(bool next)2094 void rotatemap(bool next) 2095 { 2096 if(!maprotations.inrange(curmaprotation)) 2097 { 2098 changemap("", 1); 2099 return; 2100 } 2101 if(next) 2102 { 2103 curmaprotation = findmaprotation(gamemode, smapname); 2104 if(curmaprotation >= 0) nextmaprotation(); 2105 else curmaprotation = smapname[0] ? max(findmaprotation(gamemode, ""), 0) : 0; 2106 } 2107 maprotation &rot = maprotations[curmaprotation]; 2108 changemap(rot.map, rot.findmode(gamemode)); 2109 } 2110 2111 struct votecount 2112 { 2113 char *map; 2114 int mode, count; votecountserver::votecount2115 votecount() {} votecountserver::votecount2116 votecount(char *s, int n) : map(s), mode(n), count(0) {} 2117 }; 2118 checkvotes(bool force=false)2119 void checkvotes(bool force = false) 2120 { 2121 vector<votecount> votes; 2122 int maxvotes = 0; 2123 loopv(clients) 2124 { 2125 clientinfo *oi = clients[i]; 2126 if(oi->state.state==CS_SPECTATOR && !oi->privilege && !oi->local) continue; 2127 if(oi->state.aitype!=AI_NONE) continue; 2128 maxvotes++; 2129 if(!m_valid(oi->modevote)) continue; 2130 votecount *vc = NULL; 2131 loopvj(votes) if(!strcmp(oi->mapvote, votes[j].map) && oi->modevote==votes[j].mode) 2132 { 2133 vc = &votes[j]; 2134 break; 2135 } 2136 if(!vc) vc = &votes.add(votecount(oi->mapvote, oi->modevote)); 2137 vc->count++; 2138 } 2139 votecount *best = NULL; 2140 loopv(votes) if(!best || votes[i].count > best->count || (votes[i].count == best->count && rnd(2))) best = &votes[i]; 2141 if(force || (best && best->count > maxvotes/2)) 2142 { 2143 sendpackets(true); 2144 if(demorecord) enddemorecord(); 2145 if(best && (best->count > (force ? 1 : maxvotes/2))) 2146 { 2147 sendservmsg(force ? "vote passed by default" : "vote passed by majority"); 2148 changemap(best->map, best->mode); 2149 } 2150 else rotatemap(true); 2151 } 2152 } 2153 forcemap(const char * map,int mode)2154 void forcemap(const char *map, int mode) 2155 { 2156 stopdemo(); 2157 if(!map[0] && !m_check(mode, M_EDIT)) 2158 { 2159 int idx = findmaprotation(mode, smapname); 2160 if(idx < 0 && smapname[0]) idx = findmaprotation(mode, ""); 2161 if(idx < 0) return; 2162 map = maprotations[idx].map; 2163 } 2164 if(hasnonlocalclients()) sendservmsgf("local player forced %s on map %s", modename(mode), map[0] ? map : "[new map]"); 2165 changemap(map, mode); 2166 } 2167 vote(const char * map,int reqmode,int sender)2168 void vote(const char *map, int reqmode, int sender) 2169 { 2170 clientinfo *ci = getinfo(sender); 2171 if(!ci || (ci->state.state==CS_SPECTATOR && !ci->privilege && !ci->local) || (!ci->local && !m_mp(reqmode))) return; 2172 if(!m_valid(reqmode)) return; 2173 if(!map[0] && !m_check(reqmode, M_EDIT)) 2174 { 2175 int idx = findmaprotation(reqmode, smapname); 2176 if(idx < 0 && smapname[0]) idx = findmaprotation(reqmode, ""); 2177 if(idx < 0) return; 2178 map = maprotations[idx].map; 2179 } 2180 if(lockmaprotation && !ci->local && ci->privilege < (lockmaprotation > 1 ? PRIV_ADMIN : PRIV_MASTER) && findmaprotation(reqmode, map) < 0) 2181 { 2182 sendf(sender, 1, "ris", N_SERVMSG, "This server has locked the map rotation."); 2183 return; 2184 } 2185 copystring(ci->mapvote, map); 2186 ci->modevote = reqmode; 2187 if(ci->local || (ci->privilege && mastermode>=MM_VETO)) 2188 { 2189 sendpackets(true); 2190 if(demorecord) enddemorecord(); 2191 if(!ci->local || hasnonlocalclients()) 2192 sendservmsgf("%s forced %s on map %s", colorname(ci), modename(ci->modevote), ci->mapvote[0] ? ci->mapvote : "[new map]"); 2193 changemap(ci->mapvote, ci->modevote); 2194 } 2195 else 2196 { 2197 sendservmsgf("%s suggests %s on map %s (select map to vote)", colorname(ci), modename(reqmode), map[0] ? map : "[new map]"); 2198 checkvotes(); 2199 } 2200 } 2201 2202 VAR(overtime, 0, 0, 1); 2203 checkovertime()2204 bool checkovertime() 2205 { 2206 if(!m_timed || !overtime) return false; 2207 const char* topteam = NULL; 2208 int topscore = INT_MIN; 2209 bool tied = false; 2210 if(m_teammode) 2211 { 2212 vector<teamscore> scores; 2213 if(smode && smode->hidefrags()) smode->getteamscores(scores); 2214 loopv(clients) 2215 { 2216 clientinfo *ci = clients[i]; 2217 if(ci->state.state==CS_SPECTATOR || !ci->team[0]) continue; 2218 int score = 0; 2219 if(smode && smode->hidefrags()) 2220 { 2221 int idx = scores.htfind(ci->team); 2222 if(idx >= 0) score = scores[idx].score; 2223 } 2224 else if(teaminfo *ti = teaminfos.access(ci->team)) score = ti->frags; 2225 if(!topteam || score > topscore) { topteam = ci->team; topscore = score; tied = false; } 2226 else if(score == topscore && strcmp(ci->team, topteam)) tied = true; 2227 } 2228 } 2229 else 2230 { 2231 loopv(clients) 2232 { 2233 clientinfo *ci = clients[i]; 2234 if(ci->state.state==CS_SPECTATOR) continue; 2235 int score = ci->state.frags; 2236 if(score > topscore) { topscore = score; tied = false; } 2237 else if(score == topscore) tied = true; 2238 } 2239 } 2240 if(!tied) return false; 2241 sendservmsg("the game is tied with overtime"); 2242 gamelimit = max(gamemillis, gamelimit) + 2*60000; 2243 sendf(-1, 1, "ri2", N_TIMEUP, max((gamelimit - gamemillis)/1000, 1)); 2244 return true; 2245 } 2246 checkintermission(bool force=false)2247 void checkintermission(bool force = false) 2248 { 2249 if(gamemillis >= gamelimit && !interm && (force || !checkovertime())) 2250 { 2251 sendf(-1, 1, "ri2", N_TIMEUP, 0); 2252 if(smode) smode->intermission(); 2253 changegamespeed(100); 2254 interm = gamemillis + 10000; 2255 } 2256 } 2257 startintermission()2258 void startintermission() { gamelimit = min(gamelimit, gamemillis); checkintermission(true); } 2259 dodamage(clientinfo * target,clientinfo * actor,int damage,int gun,const vec & hitpush=vec (0,0,0))2260 void dodamage(clientinfo *target, clientinfo *actor, int damage, int gun, const vec &hitpush = vec(0, 0, 0)) 2261 { 2262 gamestate &ts = target->state; 2263 ts.dodamage(damage); 2264 if(target!=actor && !isteam(target->team, actor->team)) actor->state.damage += damage; 2265 sendf(-1, 1, "ri6", N_DAMAGE, target->clientnum, actor->clientnum, damage, ts.armour, ts.health); 2266 if(target==actor) target->setpushed(); 2267 else if(!hitpush.iszero()) 2268 { 2269 ivec v(vec(hitpush).rescale(DNF)); 2270 sendf(ts.health<=0 ? -1 : target->ownernum, 1, "ri7", N_HITPUSH, target->clientnum, gun, damage, v.x, v.y, v.z); 2271 target->setpushed(); 2272 } 2273 if(ts.health<=0) 2274 { 2275 target->state.deaths++; 2276 int fragvalue = smode ? smode->fragvalue(target, actor) : (target==actor || isteam(target->team, actor->team) ? -1 : 1); 2277 actor->state.frags += fragvalue; 2278 if(fragvalue>0) 2279 { 2280 int friends = 0, enemies = 0; // note: friends also includes the fragger 2281 if(m_teammode) loopv(clients) if(strcmp(clients[i]->team, actor->team)) enemies++; else friends++; 2282 else { friends = 1; enemies = clients.length()-1; } 2283 actor->state.effectiveness += fragvalue*friends/float(max(enemies, 1)); 2284 } 2285 teaminfo *t = m_teammode ? teaminfos.access(actor->team) : NULL; 2286 if(t) t->frags += fragvalue; 2287 sendf(-1, 1, "ri5", N_DIED, target->clientnum, actor->clientnum, actor->state.frags, t ? t->frags : 0); 2288 target->position.setsize(0); 2289 if(smode) smode->died(target, actor); 2290 ts.state = CS_DEAD; 2291 ts.lastdeath = gamemillis; 2292 if(actor!=target && isteam(actor->team, target->team)) 2293 { 2294 actor->state.teamkills++; 2295 addteamkill(actor, target, 1); 2296 } 2297 ts.deadflush = ts.lastdeath + DEATHMILLIS; 2298 // don't issue respawn yet until DEATHMILLIS has elapsed 2299 // ts.respawn(); 2300 } 2301 } 2302 suicide(clientinfo * ci)2303 void suicide(clientinfo *ci) 2304 { 2305 gamestate &gs = ci->state; 2306 if(gs.state!=CS_ALIVE) return; 2307 int fragvalue = smode ? smode->fragvalue(ci, ci) : -1; 2308 ci->state.frags += fragvalue; 2309 ci->state.deaths++; 2310 teaminfo *t = m_teammode ? teaminfos.access(ci->team) : NULL; 2311 if(t) t->frags += fragvalue; 2312 sendf(-1, 1, "ri5", N_DIED, ci->clientnum, ci->clientnum, gs.frags, t ? t->frags : 0); 2313 ci->position.setsize(0); 2314 if(smode) smode->died(ci, NULL); 2315 gs.state = CS_DEAD; 2316 gs.lastdeath = gamemillis; 2317 gs.respawn(); 2318 } 2319 process(clientinfo * ci)2320 void suicideevent::process(clientinfo *ci) 2321 { 2322 suicide(ci); 2323 } 2324 process(clientinfo * ci)2325 void explodeevent::process(clientinfo *ci) 2326 { 2327 gamestate &gs = ci->state; 2328 switch(gun) 2329 { 2330 case GUN_RL: 2331 if(!gs.rockets.remove(id)) return; 2332 break; 2333 2334 case GUN_GL: 2335 if(!gs.grenades.remove(id)) return; 2336 break; 2337 2338 default: 2339 return; 2340 } 2341 sendf(-1, 1, "ri4x", N_EXPLODEFX, ci->clientnum, gun, id, ci->ownernum); 2342 loopv(hits) 2343 { 2344 hitinfo &h = hits[i]; 2345 clientinfo *target = getinfo(h.target); 2346 if(!target || target->state.state!=CS_ALIVE || h.lifesequence!=target->state.lifesequence || h.dist<0 || h.dist>guns[gun].exprad) continue; 2347 2348 bool dup = false; 2349 loopj(i) if(hits[j].target==h.target) { dup = true; break; } 2350 if(dup) continue; 2351 2352 int damage = guns[gun].damage; 2353 if(gs.quadmillis) damage *= 4; 2354 damage = int(damage*(1-h.dist/EXP_DISTSCALE/guns[gun].exprad)); 2355 if(target==ci) damage /= EXP_SELFDAMDIV; 2356 dodamage(target, ci, damage, gun, h.dir); 2357 } 2358 } 2359 process(clientinfo * ci)2360 void shotevent::process(clientinfo *ci) 2361 { 2362 gamestate &gs = ci->state; 2363 int wait = millis - gs.lastshot; 2364 if(!gs.isalive(gamemillis) || 2365 wait<gs.gunwait || 2366 gun<GUN_FIST || gun>GUN_PISTOL || 2367 gs.ammo[gun]<=0 || (guns[gun].range && from.dist(to) > guns[gun].range + 1)) 2368 return; 2369 if(gun!=GUN_FIST) gs.ammo[gun]--; 2370 gs.lastshot = millis; 2371 gs.gunwait = guns[gun].attackdelay; 2372 sendf(-1, 1, "rii9x", N_SHOTFX, ci->clientnum, gun, id, 2373 int(from.x*DMF), int(from.y*DMF), int(from.z*DMF), 2374 int(to.x*DMF), int(to.y*DMF), int(to.z*DMF), 2375 ci->ownernum); 2376 gs.shotdamage += guns[gun].damage*(gs.quadmillis ? 4 : 1)*guns[gun].rays; 2377 switch(gun) 2378 { 2379 case GUN_RL: gs.rockets.add(id); break; 2380 case GUN_GL: gs.grenades.add(id); break; 2381 default: 2382 { 2383 int totalrays = 0, maxrays = guns[gun].rays; 2384 loopv(hits) 2385 { 2386 hitinfo &h = hits[i]; 2387 clientinfo *target = getinfo(h.target); 2388 if(!target || target->state.state!=CS_ALIVE || h.lifesequence!=target->state.lifesequence || h.rays<1 || h.dist > guns[gun].range + 1) continue; 2389 2390 totalrays += h.rays; 2391 if(totalrays>maxrays) continue; 2392 int damage = h.rays*guns[gun].damage; 2393 if(gs.quadmillis) damage *= 4; 2394 dodamage(target, ci, damage, gun, h.dir); 2395 } 2396 break; 2397 } 2398 } 2399 } 2400 process(clientinfo * ci)2401 void pickupevent::process(clientinfo *ci) 2402 { 2403 gamestate &gs = ci->state; 2404 if(m_mp(gamemode) && !gs.isalive(gamemillis)) return; 2405 pickup(ent, ci->clientnum); 2406 } 2407 flush(clientinfo * ci,int fmillis)2408 bool gameevent::flush(clientinfo *ci, int fmillis) 2409 { 2410 process(ci); 2411 return true; 2412 } 2413 flush(clientinfo * ci,int fmillis)2414 bool timedevent::flush(clientinfo *ci, int fmillis) 2415 { 2416 if(millis > fmillis) return false; 2417 else if(millis >= ci->lastevent) 2418 { 2419 ci->lastevent = millis; 2420 process(ci); 2421 } 2422 return true; 2423 } 2424 clearevent(clientinfo * ci)2425 void clearevent(clientinfo *ci) 2426 { 2427 delete ci->events.remove(0); 2428 } 2429 flushevents(clientinfo * ci,int millis)2430 void flushevents(clientinfo *ci, int millis) 2431 { 2432 while(ci->events.length()) 2433 { 2434 gameevent *ev = ci->events[0]; 2435 if(ev->flush(ci, millis)) clearevent(ci); 2436 else break; 2437 } 2438 } 2439 processevents()2440 void processevents() 2441 { 2442 loopv(clients) 2443 { 2444 clientinfo *ci = clients[i]; 2445 if(curtime>0 && ci->state.quadmillis) ci->state.quadmillis = max(ci->state.quadmillis-curtime, 0); 2446 flushevents(ci, gamemillis); 2447 } 2448 } 2449 cleartimedevents(clientinfo * ci)2450 void cleartimedevents(clientinfo *ci) 2451 { 2452 int keep = 0; 2453 loopv(ci->events) 2454 { 2455 if(ci->events[i]->keepable()) 2456 { 2457 if(keep < i) 2458 { 2459 for(int j = keep; j < i; j++) delete ci->events[j]; 2460 ci->events.remove(keep, i - keep); 2461 i = keep; 2462 } 2463 keep = i+1; 2464 continue; 2465 } 2466 } 2467 while(ci->events.length() > keep) delete ci->events.pop(); 2468 ci->timesync = false; 2469 } 2470 serverupdate()2471 void serverupdate() 2472 { 2473 if(shouldstep && !gamepaused) 2474 { 2475 gamemillis += curtime; 2476 2477 if(m_demo) readdemo(); 2478 else if(!m_timed || gamemillis < gamelimit) 2479 { 2480 processevents(); 2481 if(curtime) 2482 { 2483 loopv(sents) if(sents[i].spawntime) // spawn entities when timer reached 2484 { 2485 int oldtime = sents[i].spawntime; 2486 sents[i].spawntime -= curtime; 2487 if(sents[i].spawntime<=0) 2488 { 2489 sents[i].spawntime = 0; 2490 sents[i].spawned = true; 2491 sendf(-1, 1, "ri2", N_ITEMSPAWN, i); 2492 } 2493 else if(sents[i].spawntime<=10000 && oldtime>10000 && (sents[i].type==I_QUAD || sents[i].type==I_BOOST)) 2494 { 2495 sendf(-1, 1, "ri2", N_ANNOUNCE, sents[i].type); 2496 } 2497 } 2498 } 2499 aiman::checkai(); 2500 if(smode) smode->update(); 2501 } 2502 } 2503 2504 while(bannedips.length() && bannedips[0].expire-totalmillis <= 0) bannedips.remove(0); 2505 loopv(connects) if(totalmillis-connects[i]->connectmillis>15000) disconnect_client(connects[i]->clientnum, DISC_TIMEOUT); 2506 2507 if(nextexceeded && gamemillis > nextexceeded && (!m_timed || gamemillis < gamelimit)) 2508 { 2509 nextexceeded = 0; 2510 loopvrev(clients) 2511 { 2512 clientinfo &c = *clients[i]; 2513 if(c.state.aitype != AI_NONE) continue; 2514 if(c.checkexceeded()) disconnect_client(c.clientnum, DISC_MSGERR); 2515 else c.scheduleexceeded(); 2516 } 2517 } 2518 2519 if(shouldcheckteamkills) checkteamkills(); 2520 2521 if(shouldstep && !gamepaused) 2522 { 2523 if(m_timed && smapname[0] && gamemillis-curtime>0) checkintermission(); 2524 if(interm > 0 && gamemillis>interm) 2525 { 2526 if(demorecord) enddemorecord(); 2527 interm = -1; 2528 checkvotes(true); 2529 } 2530 } 2531 2532 shouldstep = clients.length() > 0; 2533 } 2534 forcespectator(clientinfo * ci)2535 void forcespectator(clientinfo *ci) 2536 { 2537 if(ci->state.state==CS_ALIVE) suicide(ci); 2538 if(smode) smode->leavegame(ci); 2539 ci->state.state = CS_SPECTATOR; 2540 ci->state.timeplayed += lastmillis - ci->state.lasttimeplayed; 2541 if(!ci->local && (!ci->privilege || ci->warned)) aiman::removeai(ci); 2542 sendf(-1, 1, "ri3", N_SPECTATOR, ci->clientnum, 1); 2543 } 2544 2545 struct crcinfo 2546 { 2547 int crc, matches; 2548 crcinfoserver::crcinfo2549 crcinfo() {} crcinfoserver::crcinfo2550 crcinfo(int crc, int matches) : crc(crc), matches(matches) {} 2551 compareserver::crcinfo2552 static bool compare(const crcinfo &x, const crcinfo &y) { return x.matches > y.matches; } 2553 }; 2554 2555 VAR(modifiedmapspectator, 0, 1, 2); 2556 checkmaps(int req=-1)2557 void checkmaps(int req = -1) 2558 { 2559 if(m_edit || !smapname[0]) return; 2560 vector<crcinfo> crcs; 2561 int total = 0, unsent = 0, invalid = 0; 2562 if(mcrc) crcs.add(crcinfo(mcrc, clients.length() + 1)); 2563 loopv(clients) 2564 { 2565 clientinfo *ci = clients[i]; 2566 if(ci->state.state==CS_SPECTATOR || ci->state.aitype != AI_NONE) continue; 2567 total++; 2568 if(!ci->clientmap[0]) 2569 { 2570 if(ci->mapcrc < 0) invalid++; 2571 else if(!ci->mapcrc) unsent++; 2572 } 2573 else 2574 { 2575 crcinfo *match = NULL; 2576 loopvj(crcs) if(crcs[j].crc == ci->mapcrc) { match = &crcs[j]; break; } 2577 if(!match) crcs.add(crcinfo(ci->mapcrc, 1)); 2578 else match->matches++; 2579 } 2580 } 2581 if(!mcrc && total - unsent < min(total, 4)) return; 2582 crcs.sort(crcinfo::compare); 2583 string msg; 2584 loopv(clients) 2585 { 2586 clientinfo *ci = clients[i]; 2587 if(ci->state.state==CS_SPECTATOR || ci->state.aitype != AI_NONE || ci->clientmap[0] || ci->mapcrc >= 0 || (req < 0 && ci->warned)) continue; 2588 formatstring(msg, "%s has modified map \"%s\"", colorname(ci), smapname); 2589 sendf(req, 1, "ris", N_SERVMSG, msg); 2590 if(req < 0) ci->warned = true; 2591 } 2592 if(crcs.length() >= 2) loopv(crcs) 2593 { 2594 crcinfo &info = crcs[i]; 2595 if(i || info.matches <= crcs[i+1].matches) loopvj(clients) 2596 { 2597 clientinfo *ci = clients[j]; 2598 if(ci->state.state==CS_SPECTATOR || ci->state.aitype != AI_NONE || !ci->clientmap[0] || ci->mapcrc != info.crc || (req < 0 && ci->warned)) continue; 2599 formatstring(msg, "%s has modified map \"%s\"", colorname(ci), smapname); 2600 sendf(req, 1, "ris", N_SERVMSG, msg); 2601 if(req < 0) ci->warned = true; 2602 } 2603 } 2604 if(req < 0 && modifiedmapspectator && (mcrc || modifiedmapspectator > 1)) loopv(clients) 2605 { 2606 clientinfo *ci = clients[i]; 2607 if(!ci->local && ci->warned && ci->state.state != CS_SPECTATOR) forcespectator(ci); 2608 } 2609 } 2610 shouldspectate(clientinfo * ci)2611 bool shouldspectate(clientinfo *ci) 2612 { 2613 return !ci->local && ci->warned && modifiedmapspectator && (mcrc || modifiedmapspectator > 1); 2614 } 2615 unspectate(clientinfo * ci)2616 void unspectate(clientinfo *ci) 2617 { 2618 if(shouldspectate(ci)) return; 2619 ci->state.state = CS_DEAD; 2620 ci->state.respawn(); 2621 ci->state.lasttimeplayed = lastmillis; 2622 aiman::addclient(ci); 2623 sendf(-1, 1, "ri3", N_SPECTATOR, ci->clientnum, 0); 2624 if(ci->clientmap[0] || ci->mapcrc) checkmaps(); 2625 if(!hasmap(ci)) rotatemap(true); 2626 } 2627 sendservinfo(clientinfo * ci)2628 void sendservinfo(clientinfo *ci) 2629 { 2630 sendf(ci->clientnum, 1, "ri5ss", N_SERVINFO, ci->clientnum, PROTOCOL_VERSION, ci->sessionid, serverpass[0] ? 1 : 0, serverdesc, serverauth); 2631 } 2632 noclients()2633 void noclients() 2634 { 2635 bannedips.shrink(0); 2636 aiman::clearai(); 2637 } 2638 localconnect(int n)2639 void localconnect(int n) 2640 { 2641 clientinfo *ci = getinfo(n); 2642 ci->clientnum = ci->ownernum = n; 2643 ci->connectmillis = totalmillis; 2644 ci->sessionid = (rnd(0x1000000)*((totalmillis%10000)+1))&0xFFFFFF; 2645 ci->local = true; 2646 2647 connects.add(ci); 2648 sendservinfo(ci); 2649 } 2650 localdisconnect(int n)2651 void localdisconnect(int n) 2652 { 2653 if(m_demo) enddemoplayback(); 2654 clientdisconnect(n); 2655 } 2656 clientconnect(int n,uint ip)2657 int clientconnect(int n, uint ip) 2658 { 2659 clientinfo *ci = getinfo(n); 2660 ci->clientnum = ci->ownernum = n; 2661 ci->connectmillis = totalmillis; 2662 ci->sessionid = (rnd(0x1000000)*((totalmillis%10000)+1))&0xFFFFFF; 2663 2664 connects.add(ci); 2665 if(!m_mp(gamemode)) return DISC_LOCAL; 2666 sendservinfo(ci); 2667 return DISC_NONE; 2668 } 2669 clientdisconnect(int n)2670 void clientdisconnect(int n) 2671 { 2672 clientinfo *ci = getinfo(n); 2673 loopv(clients) if(clients[i]->authkickvictim == ci->clientnum) clients[i]->cleanauth(); 2674 if(ci->connected) 2675 { 2676 if(ci->privilege) setmaster(ci, false); 2677 if(smode) smode->leavegame(ci, true); 2678 ci->state.timeplayed += lastmillis - ci->state.lasttimeplayed; 2679 savescore(ci); 2680 sendf(-1, 1, "ri2", N_CDIS, n); 2681 clients.removeobj(ci); 2682 aiman::removeai(ci); 2683 if(!numclients(-1, false, true)) noclients(); // bans clear when server empties 2684 if(ci->local) checkpausegame(); 2685 } 2686 else connects.removeobj(ci); 2687 } 2688 reserveclients()2689 int reserveclients() { return 3; } 2690 2691 extern void verifybans(); 2692 2693 struct banlist 2694 { 2695 vector<ipmask> bans; 2696 clearserver::banlist2697 void clear() { bans.shrink(0); } 2698 checkserver::banlist2699 bool check(uint ip) 2700 { 2701 loopv(bans) if(bans[i].check(ip)) return true; 2702 return false; 2703 } 2704 addserver::banlist2705 void add(const char *ipname) 2706 { 2707 ipmask ban; 2708 ban.parse(ipname); 2709 bans.add(ban); 2710 2711 verifybans(); 2712 } 2713 } ipbans, gbans; 2714 checkbans(uint ip)2715 bool checkbans(uint ip) 2716 { 2717 loopv(bannedips) if(bannedips[i].ip==ip) return true; 2718 return ipbans.check(ip) || gbans.check(ip); 2719 } 2720 verifybans()2721 void verifybans() 2722 { 2723 loopvrev(clients) 2724 { 2725 clientinfo *ci = clients[i]; 2726 if(ci->state.aitype != AI_NONE || ci->local || ci->privilege >= PRIV_ADMIN) continue; 2727 if(checkbans(getclientip(ci->clientnum))) disconnect_client(ci->clientnum, DISC_IPBAN); 2728 } 2729 } 2730 2731 ICOMMAND(clearipbans, "", (), ipbans.clear()); 2732 ICOMMAND(ipban, "s", (const char *ipname), ipbans.add(ipname)); 2733 allowconnect(clientinfo * ci,const char * pwd="")2734 int allowconnect(clientinfo *ci, const char *pwd = "") 2735 { 2736 if(ci->local) return DISC_NONE; 2737 if(!m_mp(gamemode)) return DISC_LOCAL; 2738 if(serverpass[0]) 2739 { 2740 if(!checkpassword(ci, serverpass, pwd)) return DISC_PASSWORD; 2741 return DISC_NONE; 2742 } 2743 if(adminpass[0] && checkpassword(ci, adminpass, pwd)) return DISC_NONE; 2744 if(numclients(-1, false, true)>=maxclients) return DISC_MAXCLIENTS; 2745 uint ip = getclientip(ci->clientnum); 2746 if(checkbans(ip)) return DISC_IPBAN; 2747 if(mastermode>=MM_PRIVATE && allowedips.find(ip)<0) return DISC_PRIVATE; 2748 return DISC_NONE; 2749 } 2750 allowbroadcast(int n)2751 bool allowbroadcast(int n) 2752 { 2753 clientinfo *ci = getinfo(n); 2754 return ci && ci->connected; 2755 } 2756 findauth(uint id)2757 clientinfo *findauth(uint id) 2758 { 2759 loopv(clients) if(clients[i]->authreq == id) return clients[i]; 2760 return NULL; 2761 } 2762 2763 authfailed(clientinfo * ci)2764 void authfailed(clientinfo *ci) 2765 { 2766 if(!ci) return; 2767 ci->cleanauth(); 2768 if(ci->connectauth) disconnect_client(ci->clientnum, ci->connectauth); 2769 } 2770 authfailed(uint id)2771 void authfailed(uint id) 2772 { 2773 authfailed(findauth(id)); 2774 } 2775 authsucceeded(uint id)2776 void authsucceeded(uint id) 2777 { 2778 clientinfo *ci = findauth(id); 2779 if(!ci) return; 2780 ci->cleanauth(ci->connectauth!=0); 2781 if(ci->connectauth) connected(ci); 2782 if(ci->authkickvictim >= 0) 2783 { 2784 if(setmaster(ci, true, "", ci->authname, NULL, PRIV_AUTH, false, true)) 2785 trykick(ci, ci->authkickvictim, ci->authkickreason, ci->authname, NULL, PRIV_AUTH); 2786 ci->cleanauthkick(); 2787 } 2788 else setmaster(ci, true, "", ci->authname, NULL, PRIV_AUTH); 2789 } 2790 authchallenged(uint id,const char * val,const char * desc="")2791 void authchallenged(uint id, const char *val, const char *desc = "") 2792 { 2793 clientinfo *ci = findauth(id); 2794 if(!ci) return; 2795 sendf(ci->clientnum, 1, "risis", N_AUTHCHAL, desc, id, val); 2796 } 2797 2798 uint nextauthreq = 0; 2799 tryauth(clientinfo * ci,const char * user,const char * desc)2800 bool tryauth(clientinfo *ci, const char *user, const char *desc) 2801 { 2802 ci->cleanauth(); 2803 if(!nextauthreq) nextauthreq = 1; 2804 ci->authreq = nextauthreq++; 2805 filtertext(ci->authname, user, false, false, 100); 2806 copystring(ci->authdesc, desc); 2807 if(ci->authdesc[0]) 2808 { 2809 userinfo *u = users.access(userkey(ci->authname, ci->authdesc)); 2810 if(u) 2811 { 2812 uint seed[3] = { ::hthash(serverauth) + detrnd(size_t(ci) + size_t(user) + size_t(desc), 0x10000), uint(totalmillis), randomMT() }; 2813 vector<char> buf; 2814 ci->authchallenge = genchallenge(u->pubkey, seed, sizeof(seed), buf); 2815 sendf(ci->clientnum, 1, "risis", N_AUTHCHAL, desc, ci->authreq, buf.getbuf()); 2816 } 2817 else ci->cleanauth(); 2818 } 2819 else if(!requestmasterf("reqauth %u %s\n", ci->authreq, ci->authname)) 2820 { 2821 ci->cleanauth(); 2822 sendf(ci->clientnum, 1, "ris", N_SERVMSG, "not connected to authentication server"); 2823 } 2824 if(ci->authreq) return true; 2825 if(ci->connectauth) disconnect_client(ci->clientnum, ci->connectauth); 2826 return false; 2827 } 2828 answerchallenge(clientinfo * ci,uint id,char * val,const char * desc)2829 bool answerchallenge(clientinfo *ci, uint id, char *val, const char *desc) 2830 { 2831 if(ci->authreq != id || strcmp(ci->authdesc, desc)) 2832 { 2833 ci->cleanauth(); 2834 return !ci->connectauth; 2835 } 2836 for(char *s = val; *s; s++) 2837 { 2838 if(!isxdigit(*s)) { *s = '\0'; break; } 2839 } 2840 if(desc[0]) 2841 { 2842 if(ci->authchallenge && checkchallenge(val, ci->authchallenge)) 2843 { 2844 userinfo *u = users.access(userkey(ci->authname, ci->authdesc)); 2845 if(u) 2846 { 2847 if(ci->connectauth) connected(ci); 2848 if(ci->authkickvictim >= 0) 2849 { 2850 if(setmaster(ci, true, "", ci->authname, ci->authdesc, u->privilege, false, true)) 2851 trykick(ci, ci->authkickvictim, ci->authkickreason, ci->authname, ci->authdesc, u->privilege); 2852 } 2853 else setmaster(ci, true, "", ci->authname, ci->authdesc, u->privilege); 2854 } 2855 } 2856 ci->cleanauth(); 2857 } 2858 else if(!requestmasterf("confauth %u %s\n", id, val)) 2859 { 2860 ci->cleanauth(); 2861 sendf(ci->clientnum, 1, "ris", N_SERVMSG, "not connected to authentication server"); 2862 } 2863 return ci->authreq || !ci->connectauth; 2864 } 2865 masterconnected()2866 void masterconnected() 2867 { 2868 } 2869 masterdisconnected()2870 void masterdisconnected() 2871 { 2872 loopvrev(clients) 2873 { 2874 clientinfo *ci = clients[i]; 2875 if(ci->authreq) authfailed(ci); 2876 } 2877 } 2878 processmasterinput(const char * cmd,int cmdlen,const char * args)2879 void processmasterinput(const char *cmd, int cmdlen, const char *args) 2880 { 2881 uint id; 2882 string val; 2883 if(sscanf(cmd, "failauth %u", &id) == 1) 2884 authfailed(id); 2885 else if(sscanf(cmd, "succauth %u", &id) == 1) 2886 authsucceeded(id); 2887 else if(sscanf(cmd, "chalauth %u %255s", &id, val) == 2) 2888 authchallenged(id, val); 2889 else if(matchstring(cmd, cmdlen, "cleargbans")) 2890 gbans.clear(); 2891 else if(sscanf(cmd, "addgban %100s", val) == 1) 2892 gbans.add(val); 2893 } 2894 receivefile(int sender,uchar * data,int len)2895 void receivefile(int sender, uchar *data, int len) 2896 { 2897 if(!m_edit || len <= 0 || len > 4*1024*1024) return; 2898 clientinfo *ci = getinfo(sender); 2899 if(ci->state.state==CS_SPECTATOR && !ci->privilege && !ci->local) return; 2900 if(mapdata) DELETEP(mapdata); 2901 mapdata = opentempfile("mapdata", "w+b"); 2902 if(!mapdata) { sendf(sender, 1, "ris", N_SERVMSG, "failed to open temporary file for map"); return; } 2903 mapdata->write(data, len); 2904 sendservmsgf("[%s sent a map to server, \"/getmap\" to receive it]", colorname(ci)); 2905 } 2906 sendclipboard(clientinfo * ci)2907 void sendclipboard(clientinfo *ci) 2908 { 2909 if(!ci->lastclipboard || !ci->clipboard) return; 2910 bool flushed = false; 2911 loopv(clients) 2912 { 2913 clientinfo &e = *clients[i]; 2914 if(e.clientnum != ci->clientnum && e.needclipboard - ci->lastclipboard >= 0) 2915 { 2916 if(!flushed) { flushserver(true); flushed = true; } 2917 sendpacket(e.clientnum, 1, ci->clipboard); 2918 } 2919 } 2920 } 2921 connected(clientinfo * ci)2922 void connected(clientinfo *ci) 2923 { 2924 if(m_demo) enddemoplayback(); 2925 2926 if(!hasmap(ci)) rotatemap(false); 2927 2928 shouldstep = true; 2929 2930 connects.removeobj(ci); 2931 clients.add(ci); 2932 2933 ci->connectauth = 0; 2934 ci->connected = true; 2935 ci->needclipboard = totalmillis ? totalmillis : 1; 2936 if(mastermode>=MM_LOCKED) ci->state.state = CS_SPECTATOR; 2937 ci->state.lasttimeplayed = lastmillis; 2938 2939 const char *worst = m_teammode ? chooseworstteam(NULL, ci) : NULL; 2940 copystring(ci->team, worst ? worst : "good", MAXTEAMLEN+1); 2941 2942 sendwelcome(ci); 2943 if(restorescore(ci)) sendresume(ci); 2944 sendinitclient(ci); 2945 2946 aiman::addclient(ci); 2947 2948 if(m_demo) setupdemoplayback(); 2949 2950 if(servermotd[0]) sendf(ci->clientnum, 1, "ris", N_SERVMSG, servermotd); 2951 } 2952 parsepacket(int sender,int chan,packetbuf & p)2953 void parsepacket(int sender, int chan, packetbuf &p) // has to parse exactly each byte of the packet 2954 { 2955 if(sender<0 || p.packet->flags&ENET_PACKET_FLAG_UNSEQUENCED || chan > 2) return; 2956 char text[MAXTRANS]; 2957 int type; 2958 clientinfo *ci = sender>=0 ? getinfo(sender) : NULL, *cq = ci, *cm = ci; 2959 if(ci && !ci->connected) 2960 { 2961 if(chan==0) return; 2962 else if(chan!=1) { disconnect_client(sender, DISC_MSGERR); return; } 2963 else while(p.length() < p.maxlen) switch(checktype(getint(p), ci)) 2964 { 2965 case N_CONNECT: 2966 { 2967 getstring(text, p); 2968 filtertext(text, text, false, false, MAXNAMELEN); 2969 if(!text[0]) copystring(text, "unnamed"); 2970 copystring(ci->name, text, MAXNAMELEN+1); 2971 ci->playermodel = getint(p); 2972 2973 string password, authdesc, authname; 2974 getstring(password, p, sizeof(password)); 2975 getstring(authdesc, p, sizeof(authdesc)); 2976 getstring(authname, p, sizeof(authname)); 2977 int disc = allowconnect(ci, password); 2978 if(disc) 2979 { 2980 if(disc == DISC_LOCAL || !serverauth[0] || strcmp(serverauth, authdesc) || !tryauth(ci, authname, authdesc)) 2981 { 2982 disconnect_client(sender, disc); 2983 return; 2984 } 2985 ci->connectauth = disc; 2986 } 2987 else connected(ci); 2988 break; 2989 } 2990 2991 case N_AUTHANS: 2992 { 2993 string desc, ans; 2994 getstring(desc, p, sizeof(desc)); 2995 uint id = (uint)getint(p); 2996 getstring(ans, p, sizeof(ans)); 2997 if(!answerchallenge(ci, id, ans, desc)) 2998 { 2999 disconnect_client(sender, ci->connectauth); 3000 return; 3001 } 3002 break; 3003 } 3004 3005 case N_PING: 3006 getint(p); 3007 break; 3008 3009 default: 3010 disconnect_client(sender, DISC_MSGERR); 3011 return; 3012 } 3013 return; 3014 } 3015 else if(chan==2) 3016 { 3017 receivefile(sender, p.buf, p.maxlen); 3018 return; 3019 } 3020 3021 if(p.packet->flags&ENET_PACKET_FLAG_RELIABLE) reliablemessages = true; 3022 #define QUEUE_AI clientinfo *cm = cq; 3023 #define QUEUE_MSG { if(cm && (!cm->local || demorecord || hasnonlocalclients())) while(curmsg<p.length()) cm->messages.add(p.buf[curmsg++]); } 3024 #define QUEUE_BUF(body) { \ 3025 if(cm && (!cm->local || demorecord || hasnonlocalclients())) \ 3026 { \ 3027 curmsg = p.length(); \ 3028 { body; } \ 3029 } \ 3030 } 3031 #define QUEUE_INT(n) QUEUE_BUF(putint(cm->messages, n)) 3032 #define QUEUE_UINT(n) QUEUE_BUF(putuint(cm->messages, n)) 3033 #define QUEUE_STR(text) QUEUE_BUF(sendstring(text, cm->messages)) 3034 int curmsg; 3035 while((curmsg = p.length()) < p.maxlen) switch(type = checktype(getint(p), ci)) 3036 { 3037 case N_POS: 3038 { 3039 int pcn = getuint(p); 3040 p.get(); 3041 uint flags = getuint(p); 3042 clientinfo *cp = getinfo(pcn); 3043 if(cp && pcn != sender && cp->ownernum != sender) cp = NULL; 3044 vec pos; 3045 loopk(3) 3046 { 3047 int n = p.get(); n |= p.get()<<8; if(flags&(1<<k)) { n |= p.get()<<16; if(n&0x800000) n |= ~0U<<24; } 3048 pos[k] = n/DMF; 3049 } 3050 loopk(3) p.get(); 3051 int mag = p.get(); if(flags&(1<<3)) mag |= p.get()<<8; 3052 int dir = p.get(); dir |= p.get()<<8; 3053 vec vel = vec((dir%360)*RAD, (clamp(dir/360, 0, 180)-90)*RAD).mul(mag/DVELF); 3054 if(flags&(1<<4)) 3055 { 3056 p.get(); if(flags&(1<<5)) p.get(); 3057 if(flags&(1<<6)) loopk(2) p.get(); 3058 } 3059 if(cp) 3060 { 3061 if((!ci->local || demorecord || hasnonlocalclients()) && (cp->state.state==CS_ALIVE || cp->state.state==CS_EDITING)) 3062 { 3063 if(!ci->local && !m_edit && max(vel.magnitude2(), (float)fabs(vel.z)) >= 180) 3064 cp->setexceeded(); 3065 cp->position.setsize(0); 3066 while(curmsg<p.length()) cp->position.add(p.buf[curmsg++]); 3067 } 3068 if(smode && cp->state.state==CS_ALIVE) smode->moved(cp, cp->state.o, cp->gameclip, pos, (flags&0x80)!=0); 3069 cp->state.o = pos; 3070 cp->gameclip = (flags&0x80)!=0; 3071 } 3072 break; 3073 } 3074 3075 case N_TELEPORT: 3076 { 3077 int pcn = getint(p), teleport = getint(p), teledest = getint(p); 3078 clientinfo *cp = getinfo(pcn); 3079 if(cp && pcn != sender && cp->ownernum != sender) cp = NULL; 3080 if(cp && (!ci->local || demorecord || hasnonlocalclients()) && (cp->state.state==CS_ALIVE || cp->state.state==CS_EDITING)) 3081 { 3082 flushclientposition(*cp); 3083 sendf(-1, 0, "ri4x", N_TELEPORT, pcn, teleport, teledest, cp->ownernum); 3084 } 3085 break; 3086 } 3087 3088 case N_JUMPPAD: 3089 { 3090 int pcn = getint(p), jumppad = getint(p); 3091 clientinfo *cp = getinfo(pcn); 3092 if(cp && pcn != sender && cp->ownernum != sender) cp = NULL; 3093 if(cp && (!ci->local || demorecord || hasnonlocalclients()) && (cp->state.state==CS_ALIVE || cp->state.state==CS_EDITING)) 3094 { 3095 cp->setpushed(); 3096 flushclientposition(*cp); 3097 sendf(-1, 0, "ri3x", N_JUMPPAD, pcn, jumppad, cp->ownernum); 3098 } 3099 break; 3100 } 3101 3102 case N_FROMAI: 3103 { 3104 int qcn = getint(p); 3105 if(qcn < 0) cq = ci; 3106 else 3107 { 3108 cq = getinfo(qcn); 3109 if(cq && qcn != sender && cq->ownernum != sender) cq = NULL; 3110 } 3111 break; 3112 } 3113 3114 case N_EDITMODE: 3115 { 3116 int val = getint(p); 3117 if(!ci->local && !m_edit) break; 3118 if(val ? ci->state.state!=CS_ALIVE && ci->state.state!=CS_DEAD : ci->state.state!=CS_EDITING) break; 3119 if(smode) 3120 { 3121 if(val) smode->leavegame(ci); 3122 else smode->entergame(ci); 3123 } 3124 if(val) 3125 { 3126 ci->state.editstate = ci->state.state; 3127 ci->state.state = CS_EDITING; 3128 ci->events.setsize(0); 3129 ci->state.rockets.reset(); 3130 ci->state.grenades.reset(); 3131 } 3132 else ci->state.state = ci->state.editstate; 3133 QUEUE_MSG; 3134 break; 3135 } 3136 3137 case N_MAPCRC: 3138 { 3139 getstring(text, p); 3140 int crc = getint(p); 3141 if(!ci) break; 3142 if(strcmp(text, smapname)) 3143 { 3144 if(ci->clientmap[0]) 3145 { 3146 ci->clientmap[0] = '\0'; 3147 ci->mapcrc = 0; 3148 } 3149 else if(ci->mapcrc > 0) ci->mapcrc = 0; 3150 break; 3151 } 3152 copystring(ci->clientmap, text); 3153 ci->mapcrc = text[0] ? crc : 1; 3154 checkmaps(); 3155 if(cq && cq != ci && cq->ownernum != ci->clientnum) cq = NULL; 3156 break; 3157 } 3158 3159 case N_CHECKMAPS: 3160 checkmaps(sender); 3161 break; 3162 3163 case N_TRYSPAWN: 3164 if(!ci || !cq || cq->state.state!=CS_DEAD || cq->state.lastspawn>=0 || (smode && !smode->canspawn(cq))) break; 3165 if(!ci->clientmap[0] && !ci->mapcrc) 3166 { 3167 ci->mapcrc = -1; 3168 checkmaps(); 3169 if(ci == cq) { if(ci->state.state != CS_DEAD) break; } 3170 else if(cq->ownernum != ci->clientnum) { cq = NULL; break; } 3171 } 3172 if(cq->state.deadflush) 3173 { 3174 flushevents(cq, cq->state.deadflush); 3175 cq->state.respawn(); 3176 } 3177 cleartimedevents(cq); 3178 sendspawn(cq); 3179 break; 3180 3181 case N_GUNSELECT: 3182 { 3183 int gunselect = getint(p); 3184 if(!cq || cq->state.state!=CS_ALIVE) break; 3185 cq->state.gunselect = gunselect >= GUN_FIST && gunselect <= GUN_PISTOL ? gunselect : GUN_FIST; 3186 QUEUE_AI; 3187 QUEUE_MSG; 3188 break; 3189 } 3190 3191 case N_SPAWN: 3192 { 3193 int ls = getint(p), gunselect = getint(p); 3194 if(!cq || (cq->state.state!=CS_ALIVE && cq->state.state!=CS_DEAD && cq->state.state!=CS_EDITING) || ls!=cq->state.lifesequence || cq->state.lastspawn<0) break; 3195 cq->state.lastspawn = -1; 3196 cq->state.state = CS_ALIVE; 3197 cq->state.gunselect = gunselect >= GUN_FIST && gunselect <= GUN_PISTOL ? gunselect : GUN_FIST; 3198 cq->exceeded = 0; 3199 if(smode) smode->spawned(cq); 3200 QUEUE_AI; 3201 QUEUE_BUF({ 3202 putint(cm->messages, N_SPAWN); 3203 sendstate(cq->state, cm->messages); 3204 }); 3205 break; 3206 } 3207 3208 case N_SUICIDE: 3209 { 3210 if(cq) cq->addevent(new suicideevent); 3211 break; 3212 } 3213 3214 case N_SHOOT: 3215 { 3216 shotevent *shot = new shotevent; 3217 shot->id = getint(p); 3218 shot->millis = cq ? cq->geteventmillis(gamemillis, shot->id) : 0; 3219 shot->gun = getint(p); 3220 loopk(3) shot->from[k] = getint(p)/DMF; 3221 loopk(3) shot->to[k] = getint(p)/DMF; 3222 int hits = getint(p); 3223 loopk(hits) 3224 { 3225 if(p.overread()) break; 3226 hitinfo &hit = shot->hits.add(); 3227 hit.target = getint(p); 3228 hit.lifesequence = getint(p); 3229 hit.dist = getint(p)/DMF; 3230 hit.rays = getint(p); 3231 loopk(3) hit.dir[k] = getint(p)/DNF; 3232 } 3233 if(cq) 3234 { 3235 cq->addevent(shot); 3236 cq->setpushed(); 3237 } 3238 else delete shot; 3239 break; 3240 } 3241 3242 case N_EXPLODE: 3243 { 3244 explodeevent *exp = new explodeevent; 3245 int cmillis = getint(p); 3246 exp->millis = cq ? cq->geteventmillis(gamemillis, cmillis) : 0; 3247 exp->gun = getint(p); 3248 exp->id = getint(p); 3249 int hits = getint(p); 3250 loopk(hits) 3251 { 3252 if(p.overread()) break; 3253 hitinfo &hit = exp->hits.add(); 3254 hit.target = getint(p); 3255 hit.lifesequence = getint(p); 3256 hit.dist = getint(p)/DMF; 3257 hit.rays = getint(p); 3258 loopk(3) hit.dir[k] = getint(p)/DNF; 3259 } 3260 if(cq) cq->addevent(exp); 3261 else delete exp; 3262 break; 3263 } 3264 3265 case N_ITEMPICKUP: 3266 { 3267 int n = getint(p); 3268 if(!cq) break; 3269 pickupevent *pickup = new pickupevent; 3270 pickup->ent = n; 3271 cq->addevent(pickup); 3272 break; 3273 } 3274 3275 case N_TEXT: 3276 { 3277 QUEUE_AI; 3278 QUEUE_MSG; 3279 getstring(text, p); 3280 filtertext(text, text, true, true); 3281 QUEUE_STR(text); 3282 if(isdedicatedserver() && cq) logoutf("%s: %s", colorname(cq), text); 3283 break; 3284 } 3285 3286 case N_SAYTEAM: 3287 { 3288 getstring(text, p); 3289 if(!ci || !cq || (ci->state.state==CS_SPECTATOR && !ci->local && !ci->privilege) || !m_teammode || !cq->team[0]) break; 3290 filtertext(text, text, true, true); 3291 loopv(clients) 3292 { 3293 clientinfo *t = clients[i]; 3294 if(t==cq || t->state.state==CS_SPECTATOR || t->state.aitype != AI_NONE || strcmp(cq->team, t->team)) continue; 3295 sendf(t->clientnum, 1, "riis", N_SAYTEAM, cq->clientnum, text); 3296 } 3297 if(isdedicatedserver() && cq) logoutf("%s <%s>: %s", colorname(cq), cq->team, text); 3298 break; 3299 } 3300 3301 case N_SWITCHNAME: 3302 { 3303 QUEUE_MSG; 3304 getstring(text, p); 3305 filtertext(ci->name, text, false, false, MAXNAMELEN); 3306 if(!ci->name[0]) copystring(ci->name, "unnamed"); 3307 QUEUE_STR(ci->name); 3308 break; 3309 } 3310 3311 case N_SWITCHMODEL: 3312 { 3313 ci->playermodel = getint(p); 3314 QUEUE_MSG; 3315 break; 3316 } 3317 3318 case N_SWITCHTEAM: 3319 { 3320 getstring(text, p); 3321 filtertext(text, text, false, false, MAXTEAMLEN); 3322 if(m_teammode && text[0] && strcmp(ci->team, text) && (!smode || smode->canchangeteam(ci, ci->team, text)) && addteaminfo(text)) 3323 { 3324 if(ci->state.state==CS_ALIVE) suicide(ci); 3325 copystring(ci->team, text); 3326 aiman::changeteam(ci); 3327 sendf(-1, 1, "riisi", N_SETTEAM, sender, ci->team, ci->state.state==CS_SPECTATOR ? -1 : 0); 3328 } 3329 break; 3330 } 3331 3332 case N_MAPVOTE: 3333 { 3334 getstring(text, p); 3335 filtertext(text, text, false); 3336 fixmapname(text); 3337 int reqmode = getint(p); 3338 vote(text, reqmode, sender); 3339 break; 3340 } 3341 3342 case N_ITEMLIST: 3343 { 3344 if((ci->state.state==CS_SPECTATOR && !ci->privilege && !ci->local) || !notgotitems || strcmp(ci->clientmap, smapname)) { while(getint(p)>=0 && !p.overread()) getint(p); break; } 3345 int n; 3346 while((n = getint(p))>=0 && n<MAXENTS && !p.overread()) 3347 { 3348 server_entity se = { NOTUSED, 0, false }; 3349 while(sents.length()<=n) sents.add(se); 3350 sents[n].type = getint(p); 3351 if(canspawnitem(sents[n].type)) 3352 { 3353 if(m_mp(gamemode) && delayspawn(sents[n].type)) sents[n].spawntime = spawntime(sents[n].type); 3354 else sents[n].spawned = true; 3355 } 3356 } 3357 notgotitems = false; 3358 break; 3359 } 3360 3361 case N_EDITENT: 3362 { 3363 int i = getint(p); 3364 loopk(3) getint(p); 3365 int type = getint(p); 3366 loopk(5) getint(p); 3367 if(!ci || ci->state.state==CS_SPECTATOR) break; 3368 QUEUE_MSG; 3369 bool canspawn = canspawnitem(type); 3370 if(i<MAXENTS && (sents.inrange(i) || canspawnitem(type))) 3371 { 3372 server_entity se = { NOTUSED, 0, false }; 3373 while(sents.length()<=i) sents.add(se); 3374 sents[i].type = type; 3375 if(canspawn ? !sents[i].spawned : (sents[i].spawned || sents[i].spawntime)) 3376 { 3377 sents[i].spawntime = canspawn ? 1 : 0; 3378 sents[i].spawned = false; 3379 } 3380 } 3381 break; 3382 } 3383 3384 case N_EDITVAR: 3385 { 3386 int type = getint(p); 3387 getstring(text, p); 3388 switch(type) 3389 { 3390 case ID_VAR: getint(p); break; 3391 case ID_FVAR: getfloat(p); break; 3392 case ID_SVAR: getstring(text, p); 3393 } 3394 if(ci && ci->state.state!=CS_SPECTATOR) QUEUE_MSG; 3395 break; 3396 } 3397 3398 case N_PING: 3399 sendf(sender, 1, "i2", N_PONG, getint(p)); 3400 break; 3401 3402 case N_CLIENTPING: 3403 { 3404 int ping = getint(p); 3405 if(ci) 3406 { 3407 ci->ping = ping; 3408 loopv(ci->bots) ci->bots[i]->ping = ping; 3409 } 3410 QUEUE_MSG; 3411 break; 3412 } 3413 3414 case N_MASTERMODE: 3415 { 3416 int mm = getint(p); 3417 if((ci->privilege || ci->local) && mm>=MM_OPEN && mm<=MM_PRIVATE) 3418 { 3419 if((ci->privilege>=PRIV_ADMIN || ci->local) || (mastermask&(1<<mm))) 3420 { 3421 mastermode = mm; 3422 allowedips.shrink(0); 3423 if(mm>=MM_PRIVATE) 3424 { 3425 loopv(clients) allowedips.add(getclientip(clients[i]->clientnum)); 3426 } 3427 sendf(-1, 1, "rii", N_MASTERMODE, mastermode); 3428 //sendservmsgf("mastermode is now %s (%d)", mastermodename(mastermode), mastermode); 3429 } 3430 else 3431 { 3432 defformatstring(s, "mastermode %d is disabled on this server", mm); 3433 sendf(sender, 1, "ris", N_SERVMSG, s); 3434 } 3435 } 3436 break; 3437 } 3438 3439 case N_CLEARBANS: 3440 { 3441 if(ci->privilege || ci->local) 3442 { 3443 bannedips.shrink(0); 3444 sendservmsg("cleared all bans"); 3445 } 3446 break; 3447 } 3448 3449 case N_KICK: 3450 { 3451 int victim = getint(p); 3452 getstring(text, p); 3453 filtertext(text, text); 3454 trykick(ci, victim, text); 3455 break; 3456 } 3457 3458 case N_SPECTATOR: 3459 { 3460 int spectator = getint(p), val = getint(p); 3461 if(!ci->privilege && !ci->local && (spectator!=sender || (ci->state.state==CS_SPECTATOR && mastermode>=MM_LOCKED))) break; 3462 clientinfo *spinfo = (clientinfo *)getclientinfo(spectator); // no bots 3463 if(!spinfo || !spinfo->connected || (spinfo->state.state==CS_SPECTATOR ? val : !val)) break; 3464 3465 if(spinfo->state.state!=CS_SPECTATOR && val) forcespectator(spinfo); 3466 else if(spinfo->state.state==CS_SPECTATOR && !val) unspectate(spinfo); 3467 3468 if(cq && cq != ci && cq->ownernum != ci->clientnum) cq = NULL; 3469 break; 3470 } 3471 3472 case N_SETTEAM: 3473 { 3474 int who = getint(p); 3475 getstring(text, p); 3476 filtertext(text, text, false, false, MAXTEAMLEN); 3477 if(!ci->privilege && !ci->local) break; 3478 clientinfo *wi = getinfo(who); 3479 if(!m_teammode || !text[0] || !wi || !wi->connected || !strcmp(wi->team, text)) break; 3480 if((!smode || smode->canchangeteam(wi, wi->team, text)) && addteaminfo(text)) 3481 { 3482 if(wi->state.state==CS_ALIVE) suicide(wi); 3483 copystring(wi->team, text, MAXTEAMLEN+1); 3484 } 3485 aiman::changeteam(wi); 3486 sendf(-1, 1, "riisi", N_SETTEAM, who, wi->team, 1); 3487 break; 3488 } 3489 3490 case N_FORCEINTERMISSION: 3491 if(ci->local && !hasnonlocalclients()) startintermission(); 3492 break; 3493 3494 case N_RECORDDEMO: 3495 { 3496 int val = getint(p); 3497 if(ci->privilege < (restrictdemos ? PRIV_ADMIN : PRIV_MASTER) && !ci->local) break; 3498 if(!maxdemos || !maxdemosize) 3499 { 3500 sendf(ci->clientnum, 1, "ris", N_SERVMSG, "the server has disabled demo recording"); 3501 break; 3502 } 3503 demonextmatch = val!=0; 3504 sendservmsgf("demo recording is %s for next match", demonextmatch ? "enabled" : "disabled"); 3505 break; 3506 } 3507 3508 case N_STOPDEMO: 3509 { 3510 if(ci->privilege < (restrictdemos ? PRIV_ADMIN : PRIV_MASTER) && !ci->local) break; 3511 stopdemo(); 3512 break; 3513 } 3514 3515 case N_CLEARDEMOS: 3516 { 3517 int demo = getint(p); 3518 if(ci->privilege < (restrictdemos ? PRIV_ADMIN : PRIV_MASTER) && !ci->local) break; 3519 cleardemos(demo); 3520 break; 3521 } 3522 3523 case N_LISTDEMOS: 3524 if(!ci->privilege && !ci->local && ci->state.state==CS_SPECTATOR) break; 3525 listdemos(sender); 3526 break; 3527 3528 case N_GETDEMO: 3529 { 3530 int n = getint(p), tag = getint(p); 3531 if(!ci->privilege && !ci->local && ci->state.state==CS_SPECTATOR) break; 3532 senddemo(ci, n, tag); 3533 break; 3534 } 3535 3536 case N_GETMAP: 3537 if(!mapdata) sendf(sender, 1, "ris", N_SERVMSG, "no map to send"); 3538 else if(ci->getmap) sendf(sender, 1, "ris", N_SERVMSG, "already sending map"); 3539 else 3540 { 3541 sendservmsgf("[%s is getting the map]", colorname(ci)); 3542 if((ci->getmap = sendfile(sender, 2, mapdata, "ri", N_SENDMAP))) 3543 ci->getmap->freeCallback = freegetmap; 3544 ci->needclipboard = totalmillis ? totalmillis : 1; 3545 } 3546 break; 3547 3548 case N_NEWMAP: 3549 { 3550 int size = getint(p); 3551 if(!ci->privilege && !ci->local && ci->state.state==CS_SPECTATOR) break; 3552 if(size>=0) 3553 { 3554 smapname[0] = '\0'; 3555 resetitems(); 3556 notgotitems = false; 3557 if(smode) smode->newmap(); 3558 } 3559 QUEUE_MSG; 3560 break; 3561 } 3562 3563 case N_SETMASTER: 3564 { 3565 int mn = getint(p), val = getint(p); 3566 getstring(text, p); 3567 if(mn != ci->clientnum) 3568 { 3569 if(!ci->privilege && !ci->local) break; 3570 clientinfo *minfo = (clientinfo *)getclientinfo(mn); 3571 if(!minfo || !minfo->connected || (!ci->local && minfo->privilege >= ci->privilege) || (val && minfo->privilege)) break; 3572 setmaster(minfo, val!=0, "", NULL, NULL, PRIV_MASTER, true); 3573 } 3574 else setmaster(ci, val!=0, text); 3575 // don't broadcast the master password 3576 break; 3577 } 3578 3579 case N_ADDBOT: 3580 { 3581 aiman::reqadd(ci, getint(p)); 3582 break; 3583 } 3584 3585 case N_DELBOT: 3586 { 3587 aiman::reqdel(ci); 3588 break; 3589 } 3590 3591 case N_BOTLIMIT: 3592 { 3593 int limit = getint(p); 3594 if(ci) aiman::setbotlimit(ci, limit); 3595 break; 3596 } 3597 3598 case N_BOTBALANCE: 3599 { 3600 int balance = getint(p); 3601 if(ci) aiman::setbotbalance(ci, balance!=0); 3602 break; 3603 } 3604 3605 case N_AUTHTRY: 3606 { 3607 string desc, name; 3608 getstring(desc, p, sizeof(desc)); 3609 getstring(name, p, sizeof(name)); 3610 tryauth(ci, name, desc); 3611 break; 3612 } 3613 3614 case N_AUTHKICK: 3615 { 3616 string desc, name; 3617 getstring(desc, p, sizeof(desc)); 3618 getstring(name, p, sizeof(name)); 3619 int victim = getint(p); 3620 getstring(text, p); 3621 filtertext(text, text); 3622 int authpriv = PRIV_AUTH; 3623 if(desc[0]) 3624 { 3625 userinfo *u = users.access(userkey(name, desc)); 3626 if(u) authpriv = u->privilege; else break; 3627 } 3628 if(ci->local || ci->privilege >= authpriv) trykick(ci, victim, text); 3629 else if(trykick(ci, victim, text, name, desc, authpriv, true) && tryauth(ci, name, desc)) 3630 { 3631 ci->authkickvictim = victim; 3632 ci->authkickreason = newstring(text); 3633 } 3634 break; 3635 } 3636 3637 case N_AUTHANS: 3638 { 3639 string desc, ans; 3640 getstring(desc, p, sizeof(desc)); 3641 uint id = (uint)getint(p); 3642 getstring(ans, p, sizeof(ans)); 3643 answerchallenge(ci, id, ans, desc); 3644 break; 3645 } 3646 3647 case N_PAUSEGAME: 3648 { 3649 int val = getint(p); 3650 if(ci->privilege < (restrictpausegame ? PRIV_ADMIN : PRIV_MASTER) && !ci->local) break; 3651 pausegame(val > 0, ci); 3652 break; 3653 } 3654 3655 case N_GAMESPEED: 3656 { 3657 int val = getint(p); 3658 if(ci->privilege < (restrictgamespeed ? PRIV_ADMIN : PRIV_MASTER) && !ci->local) break; 3659 changegamespeed(val, ci); 3660 break; 3661 } 3662 3663 case N_COPY: 3664 ci->cleanclipboard(); 3665 ci->lastclipboard = totalmillis ? totalmillis : 1; 3666 goto genericmsg; 3667 3668 case N_PASTE: 3669 if(ci->state.state!=CS_SPECTATOR) sendclipboard(ci); 3670 goto genericmsg; 3671 3672 case N_CLIPBOARD: 3673 { 3674 int unpacklen = getint(p), packlen = getint(p); 3675 ci->cleanclipboard(false); 3676 if(ci->state.state==CS_SPECTATOR) 3677 { 3678 if(packlen > 0) p.subbuf(packlen); 3679 break; 3680 } 3681 if(packlen <= 0 || packlen > (1<<16) || unpacklen <= 0) 3682 { 3683 if(packlen > 0) p.subbuf(packlen); 3684 packlen = unpacklen = 0; 3685 } 3686 packetbuf q(32 + packlen, ENET_PACKET_FLAG_RELIABLE); 3687 putint(q, N_CLIPBOARD); 3688 putint(q, ci->clientnum); 3689 putint(q, unpacklen); 3690 putint(q, packlen); 3691 if(packlen > 0) p.get(q.subbuf(packlen).buf, packlen); 3692 ci->clipboard = q.finalize(); 3693 ci->clipboard->referenceCount++; 3694 break; 3695 } 3696 3697 case N_EDITT: 3698 case N_REPLACE: 3699 case N_EDITVSLOT: 3700 { 3701 int size = server::msgsizelookup(type); 3702 if(size<=0) { disconnect_client(sender, DISC_MSGERR); return; } 3703 loopi(size-1) getint(p); 3704 if(p.remaining() < 2) { disconnect_client(sender, DISC_MSGERR); return; } 3705 int extra = lilswap(*(const ushort *)p.pad(2)); 3706 if(p.remaining() < extra) { disconnect_client(sender, DISC_MSGERR); return; } 3707 p.pad(extra); 3708 if(ci && ci->state.state!=CS_SPECTATOR) QUEUE_MSG; 3709 break; 3710 } 3711 3712 case N_UNDO: 3713 case N_REDO: 3714 { 3715 int unpacklen = getint(p), packlen = getint(p); 3716 if(!ci || ci->state.state==CS_SPECTATOR || packlen <= 0 || packlen > (1<<16) || unpacklen <= 0) 3717 { 3718 if(packlen > 0) p.subbuf(packlen); 3719 break; 3720 } 3721 if(p.remaining() < packlen) { disconnect_client(sender, DISC_MSGERR); return; } 3722 packetbuf q(32 + packlen, ENET_PACKET_FLAG_RELIABLE); 3723 putint(q, type); 3724 putint(q, ci->clientnum); 3725 putint(q, unpacklen); 3726 putint(q, packlen); 3727 if(packlen > 0) p.get(q.subbuf(packlen).buf, packlen); 3728 sendpacket(-1, 1, q.finalize(), ci->clientnum); 3729 break; 3730 } 3731 3732 case N_SERVCMD: 3733 getstring(text, p); 3734 break; 3735 3736 #define PARSEMESSAGES 1 3737 #include "capture.h" 3738 #include "ctf.h" 3739 #include "collect.h" 3740 #undef PARSEMESSAGES 3741 3742 case -1: 3743 disconnect_client(sender, DISC_MSGERR); 3744 return; 3745 3746 case -2: 3747 disconnect_client(sender, DISC_OVERFLOW); 3748 return; 3749 3750 default: genericmsg: 3751 { 3752 int size = server::msgsizelookup(type); 3753 if(size<=0) { disconnect_client(sender, DISC_MSGERR); return; } 3754 loopi(size-1) getint(p); 3755 if(ci) switch(msgfilter[type]) 3756 { 3757 case 2: case 3: if(ci->state.state != CS_SPECTATOR) QUEUE_MSG; break; 3758 default: if(cq && (ci != cq || ci->state.state!=CS_SPECTATOR)) { QUEUE_AI; QUEUE_MSG; } break; 3759 } 3760 break; 3761 } 3762 } 3763 } 3764 laninfoport()3765 int laninfoport() { return SAUERBRATEN_LANINFO_PORT; } serverinfoport(int servport)3766 int serverinfoport(int servport) { return servport < 0 ? SAUERBRATEN_SERVINFO_PORT : servport+1; } serverport(int infoport)3767 int serverport(int infoport) { return infoport < 0 ? SAUERBRATEN_SERVER_PORT : infoport-1; } defaultmaster()3768 const char *defaultmaster() { return "master.sauerbraten.org"; } masterport()3769 int masterport() { return SAUERBRATEN_MASTER_PORT; } numchannels()3770 int numchannels() { return 3; } 3771 3772 #include "extinfo.h" 3773 serverinforeply(ucharbuf & req,ucharbuf & p)3774 void serverinforeply(ucharbuf &req, ucharbuf &p) 3775 { 3776 if(req.remaining() && !getint(req)) 3777 { 3778 extserverinforeply(req, p); 3779 return; 3780 } 3781 3782 putint(p, numclients(-1, false, true)); 3783 putint(p, gamepaused || gamespeed != 100 ? 7 : 5); // number of attrs following 3784 putint(p, PROTOCOL_VERSION); // generic attributes, passed back below 3785 putint(p, gamemode); 3786 putint(p, m_timed ? max((gamelimit - gamemillis)/1000, 0) : 0); 3787 putint(p, maxclients); 3788 putint(p, serverpass[0] ? MM_PASSWORD : (!m_mp(gamemode) ? MM_PRIVATE : (mastermode || mastermask&MM_AUTOAPPROVE ? mastermode : MM_AUTH))); 3789 if(gamepaused || gamespeed != 100) 3790 { 3791 putint(p, gamepaused ? 1 : 0); 3792 putint(p, gamespeed); 3793 } 3794 sendstring(smapname, p); 3795 sendstring(serverdesc, p); 3796 sendserverinforeply(p); 3797 } 3798 servercompatible(char * name,char * sdec,char * map,int ping,const vector<int> & attr,int np)3799 bool servercompatible(char *name, char *sdec, char *map, int ping, const vector<int> &attr, int np) 3800 { 3801 return attr.length() && attr[0]==PROTOCOL_VERSION; 3802 } 3803 3804 #include "aiman.h" 3805 } 3806 3807