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