1 #define GAMESERVER 1 2 #include "game.h" 3 4 namespace server 5 { 6 struct srventity 7 { 8 int type; 9 bool spawned; 10 int millis; 11 vector<int> attrs, kin; 12 srventityserver::srventity13 srventity() : type(NOTUSED), spawned(false), millis(0) { reset(); } ~srventityserver::srventity14 ~srventity() { reset(); } 15 resetserver::srventity16 void reset() 17 { 18 attrs.setsize(0); 19 kin.setsize(0); 20 } 21 }; 22 23 static const int DEATHMILLIS = 300; 24 25 struct clientinfo; 26 27 struct gameevent 28 { ~gameeventserver::gameevent29 virtual ~gameevent() {} 30 virtual bool flush(clientinfo *ci, int fmillis); processserver::gameevent31 virtual void process(clientinfo *ci) {} keepableserver::gameevent32 virtual bool keepable() const { return false; } 33 }; 34 35 struct timedevent : gameevent 36 { 37 int millis; 38 bool flush(clientinfo *ci, int fmillis); 39 }; 40 41 struct shotevent : timedevent 42 { 43 int id, weap, flags, power, num; 44 ivec from; 45 vector<ivec> shots; 46 void process(clientinfo *ci); 47 }; 48 49 struct switchevent : timedevent 50 { 51 int id, weap; 52 void process(clientinfo *ci); 53 }; 54 55 struct dropevent : timedevent 56 { 57 int id, weap; 58 void process(clientinfo *ci); 59 }; 60 61 struct reloadevent : timedevent 62 { 63 int id, weap; 64 void process(clientinfo *ci); 65 }; 66 67 struct hitset 68 { 69 int flags, target, id; 70 union 71 { 72 int rays; 73 int dist; 74 }; 75 ivec dir; 76 }; 77 78 struct destroyevent : timedevent 79 { 80 int id, weap, flags, radial; 81 vector<hitset> hits; keepableserver::destroyevent82 bool keepable() const { return true; } 83 void process(clientinfo *ci); 84 }; 85 86 struct suicideevent : gameevent 87 { 88 int flags; 89 void process(clientinfo *ci); 90 }; 91 92 struct useevent : timedevent 93 { 94 int id, ent; 95 void process(clientinfo *ci); 96 }; 97 98 struct projectilestate 99 { 100 vector<int> projs; projectilestateserver::projectilestate101 projectilestate() { reset(); } resetserver::projectilestate102 void reset() { projs.setsize(0); } addserver::projectilestate103 void add(int val) 104 { 105 projs.add(val); 106 } removeserver::projectilestate107 bool remove(int val) 108 { 109 loopv(projs) if(projs[i]==val) 110 { 111 projs.remove(i); 112 return true; 113 } 114 return false; 115 } findserver::projectilestate116 bool find(int val) 117 { 118 loopv(projs) if(projs[i]==val) return true; 119 return false; 120 } 121 }; 122 123 extern int gamemode, mutators; 124 struct servstate : gamestate 125 { 126 vec o; 127 int state; 128 projectilestate dropped, weapshots[WEAP_MAX][2]; 129 int score, frags, spree, rewards, flags, deaths, teamkills, shotdamage, damage; 130 int lasttimeplayed, timeplayed, aireinit, lastfireburn, lastfireowner; 131 vector<int> fraglog, fragmillis, cpnodes; 132 servstateserver::servstate133 servstate() : state(CS_SPECTATOR), aireinit(0), lastfireburn(0), lastfireowner(-1) {} 134 isaliveserver::servstate135 bool isalive(int millis) 136 { 137 return state == CS_ALIVE || ((state == CS_DEAD || state == CS_WAITING) && millis-lastdeath <= DEATHMILLIS); 138 } 139 resetserver::servstate140 void reset(bool change = false) 141 { 142 if(state != CS_SPECTATOR) state = CS_DEAD; 143 dropped.reset(); 144 loopi(WEAP_MAX) loopj(2) weapshots[i][j].reset(); 145 if(!change) score = timeplayed = 0; 146 else gamestate::mapchange(); 147 frags = spree = rewards = flags = deaths = teamkills = shotdamage = damage = 0; 148 fraglog.setsize(0); fragmillis.setsize(0); cpnodes.setsize(0); 149 respawn(0, m_health(server::gamemode, server::mutators)); 150 } 151 respawnserver::servstate152 void respawn(int millis, int heal) 153 { 154 lastfireburn = 0; lastfireowner = -1; 155 gamestate::respawn(millis, heal); 156 o = vec(-1e10f, -1e10f, -1e10f); 157 } 158 }; 159 160 struct savedscore 161 { 162 uint ip; 163 string name; 164 int points, score, frags, spree, rewards, flags, timeplayed, deaths, teamkills, shotdamage, damage; 165 saveserver::savedscore166 void save(servstate &gs) 167 { 168 points = gs.points; 169 score = gs.score; 170 frags = gs.frags; 171 spree = gs.spree; 172 rewards = gs.rewards; 173 flags = gs.flags; 174 deaths = gs.deaths; 175 teamkills = gs.teamkills; 176 shotdamage = gs.shotdamage; 177 damage = gs.damage; 178 timeplayed = gs.timeplayed; 179 } 180 restoreserver::savedscore181 void restore(servstate &gs) 182 { 183 gs.points = points; 184 gs.score = score; 185 gs.frags = frags; 186 gs.spree = spree; 187 gs.rewards = rewards; 188 gs.flags = flags; 189 gs.deaths = deaths; 190 gs.teamkills = teamkills; 191 gs.shotdamage = shotdamage; 192 gs.damage = damage; 193 gs.timeplayed = timeplayed; 194 } 195 }; 196 197 struct votecount 198 { 199 char *map; 200 int mode, muts, count; votecountserver::votecount201 votecount() {} votecountserver::votecount202 votecount(char *s, int n, int m) : map(s), mode(n), muts(m), count(0) {} 203 }; 204 205 struct clientinfo 206 { 207 int clientnum, connectmillis, sessionid, ping, team; 208 string name, mapvote; 209 int modevote, mutsvote, lastvote; 210 int privilege; 211 bool connected, local, timesync, online, wantsmap; 212 int gameoffset, lastevent; 213 servstate state; 214 vector<gameevent *> events; 215 vector<uchar> position, messages; 216 int posoff, msgoff, msglen; 217 uint authreq; 218 string authname; 219 string clientmap; 220 int mapcrc; 221 bool warned; 222 clientinfoserver::clientinfo223 clientinfo() { reset(); } ~clientinfoserver::clientinfo224 ~clientinfo() { events.deletecontentsp(); } 225 addeventserver::clientinfo226 void addevent(gameevent *e) 227 { 228 if(state.state==CS_SPECTATOR || events.length()>100) delete e; 229 else events.add(e); 230 } 231 mapchangeserver::clientinfo232 void mapchange(bool change = true) 233 { 234 mapvote[0] = 0; 235 state.reset(change); 236 events.deletecontentsp(); 237 timesync = false; 238 lastevent = gameoffset = lastvote = 0; 239 team = TEAM_NEUTRAL; 240 clientmap[0] = '\0'; 241 mapcrc = 0; 242 warned = false; 243 } 244 resetserver::clientinfo245 void reset() 246 { 247 ping = 0; 248 name[0] = 0; 249 privilege = PRIV_NONE; 250 connected = local = online = wantsmap = false; 251 authreq = 0; 252 position.setsizenodelete(0); 253 messages.setsizenodelete(0); 254 mapchange(false); 255 } 256 getmillisserver::clientinfo257 int getmillis(int millis, int id) 258 { 259 if(!timesync) 260 { 261 timesync = true; 262 gameoffset = millis-id; 263 return millis; 264 } 265 return gameoffset+id; 266 } 267 }; 268 269 struct worldstate 270 { 271 int uses; 272 vector<uchar> positions, messages; 273 }; 274 275 struct ban 276 { 277 int time; 278 uint ip; 279 }; 280 281 namespace aiman { 282 bool autooverride = false, dorefresh = false; 283 extern int findaiclient(int exclude = -1); 284 extern bool addai(int type, int ent, int skill, bool req = false); 285 extern void deleteai(clientinfo *ci); 286 extern bool delai(int type, bool req = false); 287 extern void removeai(clientinfo *ci, bool complete = false); 288 extern bool reassignai(int exclude = -1); 289 extern void checkskills(); 290 extern void clearai(int type = AI_BOT); 291 extern void checkai(); 292 extern void reqadd(clientinfo *ci, int skill); 293 extern void reqdel(clientinfo *ci); 294 } 295 296 bool hasgameinfo = false; 297 int gamemode = G_LOBBY, mutators = 0; 298 int gamemillis = 0, gamelimit = 0; 299 300 string smapname; 301 int interm = 0, minremain = -1, oldtimelimit = -1; 302 bool maprequest = false; 303 enet_uint32 lastsend = 0; 304 int mastermode = MM_OPEN, mastermask = MM_PRIVSERV; 305 bool masterupdate = false, mapsending = false, shouldcheckvotes = false; 306 stream *mapdata[3] = { NULL, NULL, NULL }; 307 308 vector<uint> allowedips; 309 vector<ban> bannedips; 310 vector<clientinfo *> clients, connects; 311 vector<worldstate *> worldstates; 312 bool reliablemessages = false; 313 314 struct demofile 315 { 316 string info; 317 uchar *data; 318 int len; 319 }; 320 321 #define MAXDEMOS 5 322 vector<demofile> demos; 323 324 bool demonextmatch = false; 325 stream *demotmp = NULL, *demorecord = NULL, *demoplayback = NULL; 326 int nextplayback = 0, triggerid = 0; 327 struct triggergrp 328 { 329 int id; 330 vector<int> ents; triggergrpserver::triggergrp331 triggergrp() { reset(); } resetserver::triggergrp332 void reset(int n = 0) { id = n; ents.setsize(0); } 333 } triggers[TRIGGERIDS+1]; 334 335 struct servmode 336 { servmodeserver::servmode337 servmode() {} ~servmodeserver::servmode338 virtual ~servmode() {} 339 entergameserver::servmode340 virtual void entergame(clientinfo *ci) {} leavegameserver::servmode341 virtual void leavegame(clientinfo *ci, bool disconnecting = false) {} 342 movedserver::servmode343 virtual void moved(clientinfo *ci, const vec &oldpos, const vec &newpos) {} canspawnserver::servmode344 virtual bool canspawn(clientinfo *ci, bool tryspawn = false) { return true; } spawnedserver::servmode345 virtual void spawned(clientinfo *ci) {} pointsserver::servmode346 virtual int points(clientinfo *victim, clientinfo *actor) 347 { 348 if(victim==actor || victim->team == actor->team) return -1; 349 return 1; 350 } diedserver::servmode351 virtual void died(clientinfo *victim, clientinfo *actor = NULL) {} changeteamserver::servmode352 virtual void changeteam(clientinfo *ci, int oldteam, int newteam) {} initclientserver::servmode353 virtual void initclient(clientinfo *ci, packetbuf &p, bool connecting) {} updateserver::servmode354 virtual void update() {} resetserver::servmode355 virtual void reset(bool empty) {} intermissionserver::servmode356 virtual void intermission() {} damageserver::servmode357 virtual bool damage(clientinfo *target, clientinfo *actor, int damage, int weap, int flags, const ivec &hitpush = ivec(0, 0, 0)) { return true; } regenserver::servmode358 virtual void regen(clientinfo *ci, int &total, int &amt, int &delay) {} 359 }; 360 361 vector<srventity> sents; 362 vector<savedscore> scores; 363 servmode *smode; 364 vector<servmode *> smuts; 365 #define mutate(a,b) loopvk(a) { servmode *mut = a[k]; { b; } } 366 367 SVAR(serverdesc, ""); 368 SVAR(servermotd, ""); 369 SVAR(serverpass, ""); 370 SVAR(adminpass, ""); 371 VARF(serveropen, 0, 1, 2, { 372 switch(serveropen) 373 { 374 case 0: default: mastermask = MM_PRIVSERV; break; 375 case 1: mastermask = MM_PUBSERV; break; 376 case 2: mastermask = MM_COOPSERV; break; 377 } 378 }); 379 VAR(modelimit, 0, G_DEATHMATCH, G_MAX-1); 380 VAR(modelock, 0, 3, 4); // 0 = off, 1 = master only (+1 admin only), 3 = non-admin can only set limited mode and higher, 4 = no mode selection 381 VAR(mapslock, 0, 2, 5); // 0 = off, 1 = master can select non-allow maps (+1 admin), 3 = master can select non-rotation maps (+1 admin), 5 = no map selection 382 VAR(varslock, 0, 1, 2); // 0 = master, 1 = admin only, 2 = nobody 383 VAR(votewait, 0, 3000, INT_MAX-1); 384 385 ICOMMAND(gameid, "", (), result(gameid())); 386 ICOMMAND(gamever, "", (), intret(gamever())); 387 resetgamevars(bool flush)388 void resetgamevars(bool flush) 389 { 390 string val; 391 enumerate(*idents, ident, id, { 392 if(id.flags&IDF_SERVER) // reset vars 393 { 394 val[0] = 0; 395 switch(id.type) 396 { 397 case ID_VAR: 398 { 399 setvar(id.name, id.def.i, true); 400 if(flush) formatstring(val)(id.flags&IDF_HEX ? (id.maxval==0xFFFFFF ? "0x%.6X" : "0x%X") : "%d", *id.storage.i); 401 break; 402 } 403 case ID_FVAR: 404 { 405 setfvar(id.name, id.def.f, true); 406 if(flush) formatstring(val)("%f", *id.storage.f); 407 break; 408 } 409 case ID_SVAR: 410 { 411 setsvar(id.name, id.def.s && *id.def.s ? id.def.s : "", true); 412 if(flush) formatstring(val)("%s", *id.storage.s); 413 break; 414 } 415 default: break; 416 } 417 if(flush) sendf(-1, 1, "ri2ss", SV_COMMAND, -1, &id.name[3], val); 418 } 419 }); 420 execfile("servexec.cfg", false); 421 } 422 ICOMMANDG(resetvars, "", (), resetgamevars(true)); 423 pickmap(const char * suggest,int mode,int muts)424 const char *pickmap(const char *suggest, int mode, int muts) 425 { 426 const char *map = GVAR(defaultmap); 427 if(!map || !*map) map = choosemap(suggest, mode, muts, m_story(gamemode) ? 1 : GVAR(maprotate)); 428 return map; 429 } 430 setpause(bool on=false)431 void setpause(bool on = false) 432 { 433 if(sv_gamepaused != on ? 1 : 0) 434 { 435 setvar("sv_gamepaused", on ? 1 : 0, true); 436 sendf(-1, 1, "ri2ss", SV_COMMAND, -1, "gamepaused", on ? 1 : 0); 437 } 438 } 439 cleanup()440 void cleanup() 441 { 442 setpause(false); 443 if(GVAR(resetvarsonend)) resetgamevars(true); 444 if(GVAR(resetbansonend)) bannedips.setsize(0); 445 changemap(); 446 } 447 start()448 void start() { cleanup(); } 449 newinfo()450 void *newinfo() { return new clientinfo; } deleteinfo(void * ci)451 void deleteinfo(void *ci) { delete (clientinfo *)ci; } 452 mastermodename(int type)453 const char *mastermodename(int type) 454 { 455 switch(type) 456 { 457 case MM_OPEN: return "open"; 458 case MM_VETO: return "veto"; 459 case MM_LOCKED: return "locked"; 460 case MM_PRIVATE: return "private"; 461 case MM_PASSWORD: return "password"; 462 default: return "unknown"; 463 } 464 } 465 privname(int type)466 const char *privname(int type) 467 { 468 switch(type) 469 { 470 case PRIV_ADMIN: return "admin"; 471 case PRIV_MASTER: return "master"; 472 case PRIV_MAX: return "local"; 473 default: return "alone"; 474 } 475 } 476 numclients(int exclude,bool nospec,int aitype)477 int numclients(int exclude, bool nospec, int aitype) 478 { 479 int n = 0; 480 loopv(clients) 481 { 482 if(clients[i]->clientnum >= 0 && clients[i]->name[0] && clients[i]->clientnum != exclude && 483 (!nospec || clients[i]->state.state != CS_SPECTATOR) && 484 (clients[i]->state.aitype < 0 || (aitype >= 0 && clients[i]->state.aitype <= aitype && clients[i]->state.ownernum >= 0))) 485 n++; 486 } 487 return n; 488 } 489 haspriv(clientinfo * ci,int flag,const char * msg=NULL)490 bool haspriv(clientinfo *ci, int flag, const char *msg = NULL) 491 { 492 if(ci->local || ci->privilege >= flag) return true; 493 else if(mastermask&MM_AUTOAPPROVE && flag <= PRIV_MASTER && !numclients(ci->clientnum, false, -1)) return true; 494 else if(msg) 495 srvmsgf(ci->clientnum, "\fraccess denied, you need to be %s to %s", privname(flag), msg); 496 return false; 497 } 498 duplicatename(clientinfo * ci,char * name)499 bool duplicatename(clientinfo *ci, char *name) 500 { 501 if(!name) name = ci->name; 502 loopv(clients) if(clients[i]!=ci && !strcmp(name, clients[i]->name)) return true; 503 return false; 504 } 505 colorname(clientinfo * ci,char * name=NULL,bool team=true,bool dupname=true)506 const char *colorname(clientinfo *ci, char *name = NULL, bool team = true, bool dupname = true) 507 { 508 if(!name) name = ci->name; 509 static string cname; 510 formatstring(cname)("\fs%s%s", teamtype[ci->team].chat, name); 511 if(!name[0] || ci->state.aitype >= 0 || (dupname && duplicatename(ci, name))) 512 { 513 defformatstring(s)(" [\fs\fc%s%d\fS]", ci->state.aitype >= 0 ? "\fe" : "", ci->clientnum); 514 concatstring(cname, s); 515 } 516 concatstring(cname, "\fS"); 517 return cname; 518 } 519 gameid()520 const char *gameid() { return GAMEID; } gamever()521 int gamever() { return GAMEVERSION; } gamename(int mode,int muts)522 const char *gamename(int mode, int muts) 523 { 524 if(!m_game(mode)) 525 { 526 mode = G_DEATHMATCH; 527 muts = gametype[mode].implied; 528 } 529 static string gname; 530 gname[0] = 0; 531 if(gametype[mode].mutators && muts) loopi(G_M_NUM) 532 { 533 if((gametype[mode].mutators&mutstype[i].type) && (muts&mutstype[i].type) && (!gametype[mode].implied || !(gametype[mode].implied&mutstype[i].type))) 534 { 535 defformatstring(name)("%s%s%s", *gname ? gname : "", *gname ? "-" : "", mutstype[i].name); 536 copystring(gname, name); 537 } 538 } 539 defformatstring(mname)("%s%s%s", *gname ? gname : "", *gname ? " " : "", gametype[mode].name); 540 copystring(gname, mname); 541 return gname; 542 } 543 ICOMMAND(gamename, "iii", (int *g, int *m), result(gamename(*g, *m))); 544 modecheck(int * mode,int * muts,int trying)545 void modecheck(int *mode, int *muts, int trying) 546 { 547 if(!m_game(*mode)) 548 { 549 *mode = G_DEATHMATCH; 550 *muts = gametype[*mode].implied; 551 } 552 #define modecheckreset(a) { if(*muts && ++count < G_M_NUM*4) { i = 0; a; } else { *muts = 0; break; } } 553 if(!gametype[*mode].mutators) *muts = G_M_NONE; 554 else 555 { 556 int count = 0; 557 if(gametype[*mode].implied) *muts |= gametype[*mode].implied; 558 if(*muts) loopi(G_M_NUM) 559 { 560 if(trying && !(gametype[*mode].mutators&mutstype[i].type) && (trying&mutstype[i].type)) trying &= ~mutstype[i].type; 561 if(!(gametype[*mode].mutators&mutstype[i].type) && (*muts&mutstype[i].type)) 562 { 563 *muts &= ~mutstype[i].type; 564 modecheckreset(continue); 565 } 566 if(*muts&mutstype[i].type) loopj(G_M_NUM) 567 { 568 if(mutstype[i].mutators && !(mutstype[i].mutators&mutstype[j].type) && (*muts&mutstype[j].type)) 569 { 570 if(trying && (trying&mutstype[j].type) && !(gametype[*mode].implied&mutstype[i].type)) *muts &= ~mutstype[i].type; 571 else *muts &= ~mutstype[j].type; 572 modecheckreset(break); 573 } 574 if(mutstype[i].implied && (mutstype[i].implied&mutstype[j].type) && !(*muts&mutstype[j].type)) 575 { 576 *muts |= mutstype[j].type; 577 modecheckreset(break); 578 } 579 } 580 } 581 } 582 } 583 mutscheck(int mode,int muts,int trying)584 int mutscheck(int mode, int muts, int trying) 585 { 586 int gm = mode, mt = muts; 587 modecheck(&gm, &mt, trying); 588 return mt; 589 } 590 ICOMMAND(mutscheck, "iii", (int *g, int *m, int *t), intret(mutscheck(*g, *m, *t))); 591 changemode(int * mode,int * muts)592 void changemode(int *mode, int *muts) 593 { 594 if(*mode < 0) 595 { 596 if(GVAR(defaultmode) >= G_START) *mode = GVAR(defaultmode); 597 else *mode = rnd(G_RAND)+G_FIGHT; 598 } 599 if(*muts < 0) 600 { 601 if(GVAR(defaultmuts) >= G_M_NONE) *muts = GVAR(defaultmuts); 602 else 603 { 604 *muts = G_M_NONE; 605 int num = rnd(G_M_NUM+1); 606 if(num) loopi(num) 607 { 608 int rmut = rnd(G_M_NUM+1); 609 if(rmut) *muts |= 1<<(rmut-1); 610 } 611 } 612 } 613 modecheck(mode, muts); 614 } 615 choosemap(const char * suggest,int mode,int muts,int force)616 const char *choosemap(const char *suggest, int mode, int muts, int force) 617 { 618 static string mapchosen; 619 if(suggest && *suggest) copystring(mapchosen, suggest); 620 else *mapchosen = 0; 621 int rotate = force ? force : GVAR(maprotate); 622 if(rotate) 623 { 624 const char *maplist = GVAR(mainmaps); 625 if(m_story(mode)) maplist = GVAR(storymaps); 626 else if(m_duel(mode, muts)) maplist = GVAR(duelmaps); 627 else if(m_stf(mode)) maplist = GVAR(stfmaps); 628 else if(m_ctf(mode)) maplist = m_multi(mode, muts) ? GVAR(mctfmaps) : GVAR(ctfmaps); 629 else if(m_trial(mode)) maplist = GVAR(trialmaps); 630 if(maplist && *maplist) 631 { 632 int n = listlen(maplist), p = -1, c = -1; 633 if(*mapchosen) 634 { 635 loopi(n) 636 { 637 char *maptxt = indexlist(maplist, i); 638 if(maptxt) 639 { 640 string maploc; 641 if(strpbrk(maptxt, "/\\")) copystring(maploc, maptxt); 642 else formatstring(maploc)("maps/%s", maptxt); 643 if(!strcmp(mapchosen, maptxt) || !strcmp(mapchosen, maploc)) 644 { 645 p = i; 646 if(rotate == 1) c = i >= 0 && i < n-1 ? i+1 : 0; 647 } 648 DELETEA(maptxt); 649 } 650 if(p >= 0) break; 651 } 652 } 653 if(c < 0) 654 { 655 c = n ? rnd(n) : 0; 656 if(c == p) c = p >= 0 && p < n-1 ? p+1 : 0; 657 } 658 char *mapidx = c >= 0 ? indexlist(maplist, c) : NULL; 659 if(mapidx) 660 { 661 copystring(mapchosen, mapidx); 662 DELETEA(mapidx); 663 } 664 } 665 } 666 return *mapchosen ? mapchosen : pickmap(suggest, mode, muts); 667 } 668 canload(const char * type)669 bool canload(const char *type) 670 { 671 if(!strcmp(type, gameid()) || !strcmp(type, "fps") || !strcmp(type, "bfg")) 672 return true; 673 return false; 674 } 675 startintermission()676 void startintermission() 677 { 678 setpause(false); 679 minremain = 0; 680 gamelimit = min(gamelimit, gamemillis); 681 if(smode) smode->intermission(); 682 mutate(smuts, mut->intermission()); 683 maprequest = false; 684 interm = gamemillis+GVAR(intermlimit); 685 sendf(-1, 1, "ri2", SV_TIMEUP, 0); 686 aiman::clearai(AI_START); 687 } 688 checklimits()689 void checklimits() 690 { 691 if(m_fight(gamemode)) 692 { 693 if(m_trial(gamemode)) 694 { 695 loopv(clients) if(clients[i]->state.cpmillis < 0 && gamemillis+clients[i]->state.cpmillis >= GVAR(triallimit)) 696 { 697 sendf(-1, 1, "ri3s", SV_ANNOUNCE, S_GUIBACK, CON_MESG, "\fctime trial wait period has timed out"); 698 startintermission(); 699 return; 700 } 701 } 702 if(GVAR(timelimit) != oldtimelimit || (gamemillis-curtime>0 && gamemillis/60000!=(gamemillis-curtime)/60000)) 703 { 704 if(GVAR(timelimit) != oldtimelimit) 705 { 706 if(GVAR(timelimit)) gamelimit += (GVAR(timelimit)-oldtimelimit)*60000; 707 oldtimelimit = GVAR(timelimit); 708 } 709 if(minremain) 710 { 711 if(GVAR(timelimit)) 712 { 713 if(gamemillis >= gamelimit) minremain = 0; 714 else minremain = (gamelimit-gamemillis+60000-1)/60000; 715 } 716 else minremain = -1; 717 if(!minremain) 718 { 719 sendf(-1, 1, "ri3s", SV_ANNOUNCE, S_GUIBACK, CON_MESG, "\fctime limit has been reached"); 720 startintermission(); 721 return; // bail 722 } 723 else 724 { 725 sendf(-1, 1, "ri2", SV_TIMEUP, minremain); 726 if(minremain == 1) sendf(-1, 1, "ri3s", SV_ANNOUNCE, S_V_ONEMINUTE, CON_MESG, "\fcone minute remains"); 727 } 728 } 729 } 730 if(GVAR(fraglimit) && !m_ctf(gamemode) && !m_stf(gamemode) && !m_trial(gamemode)) 731 { 732 if(m_team(gamemode, mutators)) 733 { 734 int teamscores[TEAM_NUM] = { 0, 0, 0, 0 }; 735 loopv(clients) if(clients[i]->state.aitype <= AI_BOT && clients[i]->team >= TEAM_FIRST && isteam(gamemode, mutators, clients[i]->team, TEAM_FIRST)) 736 teamscores[clients[i]->team-TEAM_FIRST] += clients[i]->state.frags; 737 int best = -1; 738 loopi(TEAM_NUM) if(best < 0 || teamscores[i] > teamscores[best]) 739 best = i; 740 if(best >= 0 && teamscores[best] >= GVAR(fraglimit)) 741 { 742 sendf(-1, 1, "ri3s", SV_ANNOUNCE, S_GUIBACK, CON_MESG, "\fcfrag limit has been reached"); 743 startintermission(); 744 return; // bail 745 } 746 } 747 else 748 { 749 int best = -1; 750 loopv(clients) if(clients[i]->state.aitype <= AI_BOT && (best < 0 || clients[i]->state.frags > clients[best]->state.frags)) 751 best = i; 752 if(best >= 0 && clients[best]->state.frags >= GVAR(fraglimit)) 753 { 754 sendf(-1, 1, "ri3s", SV_ANNOUNCE, S_GUIBACK, CON_MESG, "\fcfrag limit has been reached"); 755 startintermission(); 756 return; // bail 757 } 758 } 759 } 760 } 761 } 762 hasitem(int i)763 bool hasitem(int i) 764 { 765 if(m_noitems(gamemode, mutators)) return false; 766 switch(sents[i].type) 767 { 768 case WEAPON: 769 if((sents[i].attrs[3] > 0 && sents[i].attrs[3] != triggerid) || !m_check(sents[i].attrs[2], gamemode)) return false; 770 if(m_arena(gamemode, mutators) && sents[i].attrs[0] != WEAP_GRENADE) return false; 771 break; 772 default: break; 773 } 774 return true; 775 } 776 finditem(int i,bool spawned=true,bool timeit=false)777 bool finditem(int i, bool spawned = true, bool timeit = false) 778 { 779 if(!m_noitems(gamemode, mutators)) 780 { 781 if(sents[i].spawned) return true; 782 int sweap = m_weapon(gamemode, mutators); 783 if(sents[i].type != WEAPON || w_carry(w_attr(gamemode, sents[i].attrs[0], sweap), sweap)) 784 { 785 loopvk(clients) 786 { 787 clientinfo *ci = clients[k]; 788 if(ci->state.dropped.projs.find(i) >= 0 && (!spawned || (timeit && gamemillis < sents[i].millis))) 789 return true; 790 else loopj(WEAP_MAX) if(ci->state.entid[j] == i) return spawned; 791 } 792 } 793 if(spawned && timeit && gamemillis < sents[i].millis) return true; 794 } 795 return false; 796 } 797 sortitems(int * a,int * b)798 int sortitems(int *a, int *b) { return rnd(3)-1; } setupitems(bool update)799 void setupitems(bool update) 800 { 801 static vector<int> items; items.setsizenodelete(0); 802 loopv(sents) if(enttype[sents[i].type].usetype == EU_ITEM && hasitem(i)) 803 { 804 sents[i].millis += GVAR(itemspawndelay); 805 switch(GVAR(itemspawnstyle)) 806 { 807 case 1: items.add(i); break; 808 case 2: sents[i].millis += rnd(GVAR(itemspawntime)); break; 809 default: break; 810 } 811 } 812 if(!items.empty()) 813 { 814 items.sort(sortitems); 815 loopv(items) sents[items[i]].millis += GVAR(itemspawndelay)*i; 816 } 817 } 818 setuptriggers(bool update)819 void setuptriggers(bool update) 820 { 821 loopi(TRIGGERIDS+1) triggers[i].reset(i); 822 if(update) 823 { 824 loopv(sents) if(sents[i].type == TRIGGER && sents[i].attrs[4] >= 2 && sents[i].attrs[0] >= 0 && sents[i].attrs[0] <= TRIGGERIDS+1) 825 triggers[sents[i].attrs[0]].ents.add(i); 826 } 827 else triggerid = 0; 828 829 if(triggerid <= 0) 830 { 831 static vector<int> valid; valid.setsizenodelete(0); 832 loopi(TRIGGERIDS) if(!triggers[i+1].ents.empty()) valid.add(triggers[i+1].id); 833 if(!valid.empty()) triggerid = valid[rnd(valid.length())]; 834 } 835 836 if(triggerid > 0) loopi(TRIGGERIDS) if(triggers[i+1].id != triggerid) loopvk(triggers[i+1].ents) 837 { 838 bool spawn = sents[triggers[i+1].ents[k]].attrs[4]%2; 839 if(spawn != sents[triggers[i+1].ents[k]].spawned) 840 { 841 sents[triggers[i+1].ents[k]].spawned = spawn; 842 sents[triggers[i+1].ents[k]].millis = gamemillis; 843 } 844 sendf(-1, 1, "ri3", SV_TRIGGER, triggers[i+1].ents[k], 1+(spawn ? 2 : 1)); 845 loopvj(sents[triggers[i+1].ents[k]].kin) if(sents.inrange(sents[triggers[i+1].ents[k]].kin[j])) 846 { 847 sents[sents[triggers[i+1].ents[k]].kin[j]].spawned = sents[triggers[i+1].ents[k]].spawned; 848 sents[sents[triggers[i+1].ents[k]].kin[j]].millis = sents[triggers[i+1].ents[k]].millis; 849 } 850 } 851 } 852 853 struct spawn 854 { 855 int spawncycle; 856 vector<int> ents; 857 vector<int> cycle; 858 spawnserver::spawn859 spawn() { reset(); } ~spawnserver::spawn860 ~spawn() {} 861 resetserver::spawn862 void reset() 863 { 864 ents.setsize(0); 865 cycle.setsize(0); 866 spawncycle = 0; 867 } addserver::spawn868 void add(int n) 869 { 870 ents.add(n); 871 cycle.add(0); 872 } 873 } spawns[TEAM_LAST+1]; 874 int nplayers, totalspawns; 875 setupspawns(bool update,int players=0)876 void setupspawns(bool update, int players = 0) 877 { 878 nplayers = totalspawns = 0; 879 loopi(TEAM_LAST+1) spawns[i].reset(); 880 if(update) 881 { 882 int numt = numteams(gamemode, mutators), cplayers = 0; 883 if(m_fight(gamemode) && m_team(gamemode, mutators)) 884 { 885 loopk(3) 886 { 887 loopv(sents) if(sents[i].type == PLAYERSTART && (sents[i].attrs[4] == triggerid || !sents[i].attrs[4]) && m_check(sents[i].attrs[3], gamemode)) 888 { 889 if(!k && !isteam(gamemode, mutators, sents[i].attrs[0], TEAM_FIRST)) continue; 890 else if(k == 1 && sents[i].attrs[0] == TEAM_NEUTRAL) continue; 891 else if(k == 2 && sents[i].attrs[0] != TEAM_NEUTRAL) continue; 892 spawns[!k && m_team(gamemode, mutators) ? sents[i].attrs[0] : TEAM_NEUTRAL].add(i); 893 totalspawns++; 894 } 895 if(!k && m_team(gamemode, mutators)) 896 { 897 loopi(numt) if(spawns[i+TEAM_FIRST].ents.empty()) 898 { 899 loopj(TEAM_LAST+1) spawns[j].reset(); 900 totalspawns = 0; 901 break; 902 } 903 } 904 if(totalspawns) break; 905 } 906 } 907 else 908 { // use all neutral spawns 909 loopv(sents) if(sents[i].type == PLAYERSTART && sents[i].attrs[0] == TEAM_NEUTRAL && (sents[i].attrs[4] == triggerid || !sents[i].attrs[4]) && m_check(sents[i].attrs[3], gamemode)) 910 { 911 spawns[TEAM_NEUTRAL].add(i); 912 totalspawns++; 913 } 914 } 915 if(!totalspawns) 916 { // use all spawns 917 loopv(sents) if(sents[i].type == PLAYERSTART && (sents[i].attrs[4] == triggerid || !sents[i].attrs[4]) && m_check(sents[i].attrs[3], gamemode)) 918 { 919 spawns[TEAM_NEUTRAL].add(i); 920 totalspawns++; 921 } 922 } 923 924 if(totalspawns) cplayers = totalspawns/2; 925 else 926 { // we can cheat and use weapons for spawns 927 loopv(sents) if(sents[i].type == WEAPON) 928 { 929 spawns[TEAM_NEUTRAL].add(i); 930 totalspawns++; 931 } 932 cplayers = totalspawns/3; 933 } 934 nplayers = players > 0 ? players : cplayers; 935 if(m_fight(gamemode) && m_team(gamemode, mutators)) 936 { 937 int offt = nplayers%numt; 938 if(offt) nplayers += numt-offt; 939 } 940 } 941 } 942 pickspawn(clientinfo * ci)943 int pickspawn(clientinfo *ci) 944 { 945 if(ci->state.aitype >= AI_START) return ci->state.aientity; 946 else 947 { 948 if((m_story(gamemode) || m_trial(gamemode) || m_lobby(gamemode)) && !ci->state.cpnodes.empty()) 949 { 950 int checkpoint = ci->state.cpnodes.last(); 951 if(sents.inrange(checkpoint)) return checkpoint; 952 } 953 if(totalspawns && GVAR(spawnrotate)) 954 { 955 int cycle = -1, team = m_fight(gamemode) && m_team(gamemode, mutators) && !spawns[ci->team].ents.empty() ? ci->team : TEAM_NEUTRAL; 956 if(!spawns[team].ents.empty()) 957 { 958 switch(GVAR(spawnrotate)) 959 { 960 case 2: 961 { 962 int num = 0, lowest = -1; 963 loopv(spawns[team].cycle) if(lowest < 0 || spawns[team].cycle[i] < lowest) lowest = spawns[team].cycle[i]; 964 loopv(spawns[team].cycle) if(spawns[team].cycle[i] == lowest) num++; 965 if(num > 0) 966 { 967 int r = rnd(num), n = 0; 968 loopv(spawns[team].cycle) if(spawns[team].cycle[i] == lowest) 969 { 970 if(n == r) 971 { 972 spawns[team].cycle[i]++; 973 spawns[team].spawncycle = cycle = i; 974 break; 975 } 976 n++; 977 } 978 break; 979 } 980 // fall through if this fails.. 981 } 982 case 1: default: 983 { 984 if(++spawns[team].spawncycle >= spawns[team].ents.length()) spawns[team].spawncycle = 0; 985 cycle = spawns[team].spawncycle; 986 break; 987 } 988 } 989 if(spawns[team].ents.inrange(cycle)) return spawns[team].ents[cycle]; 990 } 991 } 992 } 993 return -1; 994 } 995 setupgameinfo(int np)996 void setupgameinfo(int np) 997 { 998 loopvk(clients) clients[k]->state.dropped.reset(); 999 setuptriggers(true); 1000 if(m_fight(gamemode)) setupitems(true); 1001 setupspawns(true, m_trial(gamemode) || m_lobby(gamemode) ? 0 : (m_story(gamemode) ? GVAR(storyplayers) : np)); 1002 hasgameinfo = aiman::dorefresh = true; 1003 } 1004 sendspawn(clientinfo * ci)1005 void sendspawn(clientinfo *ci) 1006 { 1007 servstate &gs = ci->state; 1008 int weap = m_weapon(gamemode, mutators), maxhealth = m_health(gamemode, mutators); 1009 bool grenades = GVAR(spawngrenades) >= (m_insta(gamemode, mutators) || m_trial(gamemode) ? 2 : 1), arena = m_arena(gamemode, mutators); 1010 if(ci->state.aitype >= AI_START) 1011 { 1012 weap = aistyle[ci->state.aitype].weap; 1013 if(!isweap(weap)) weap = rnd(WEAP_SUPER-1)+1; 1014 maxhealth = aistyle[ci->state.aitype].health; 1015 arena = grenades = false; 1016 } 1017 gs.spawnstate(weap, maxhealth, arena, grenades); 1018 int spawn = pickspawn(ci); 1019 sendf(ci->clientnum, 1, "ri8v", SV_SPAWNSTATE, ci->clientnum, spawn, gs.state, gs.frags, gs.health, gs.cptime, gs.weapselect, WEAP_MAX, &gs.ammo[0]); 1020 gs.lastrespawn = gs.lastspawn = gamemillis; 1021 } 1022 1023 template<class T> sendstate(servstate & gs,T & p)1024 void sendstate(servstate &gs, T &p) 1025 { 1026 putint(p, gs.state); 1027 putint(p, gs.frags); 1028 putint(p, gs.health); 1029 putint(p, gs.cptime); 1030 putint(p, gs.weapselect); 1031 loopi(WEAP_MAX) putint(p, gs.ammo[i]); 1032 } 1033 relayf(int r,const char * s,...)1034 void relayf(int r, const char *s, ...) 1035 { 1036 defvformatstring(str, s, s); 1037 string st; 1038 filtertext(st, str); 1039 #ifdef IRC 1040 ircoutf(r, "%s", st); 1041 #endif 1042 #ifdef STANDALONE 1043 printf("%s\n", st); 1044 #endif 1045 } 1046 srvmsgf(int cn,const char * s,...)1047 void srvmsgf(int cn, const char *s, ...) 1048 { 1049 if(cn < 0 || allowbroadcast(cn)) 1050 { 1051 defvformatstring(str, s, s); 1052 int conlevel = CON_MESG; 1053 switch(cn) 1054 { 1055 case -3: conlevel = CON_CHAT; break; 1056 case -2: conlevel = CON_EVENT; break; 1057 default: break; 1058 } 1059 sendf(cn, 1, "ri2s", SV_SERVMSG, conlevel, str); 1060 } 1061 } 1062 srvoutf(int r,const char * s,...)1063 void srvoutf(int r, const char *s, ...) 1064 { 1065 defvformatstring(str, s, s); 1066 srvmsgf(-1, "%s", str); 1067 relayf(r, "%s", str); 1068 } 1069 writedemo(int chan,void * data,int len)1070 void writedemo(int chan, void *data, int len) 1071 { 1072 if(!demorecord) return; 1073 int stamp[3] = { gamemillis, chan, len }; 1074 lilswap(stamp, 3); 1075 demorecord->write(stamp, sizeof(stamp)); 1076 demorecord->write(data, len); 1077 } 1078 recordpacket(int chan,void * data,int len)1079 void recordpacket(int chan, void *data, int len) 1080 { 1081 writedemo(chan, data, len); 1082 } 1083 listdemos(int cn)1084 void listdemos(int cn) 1085 { 1086 packetbuf p(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); 1087 putint(p, SV_SENDDEMOLIST); 1088 putint(p, demos.length()); 1089 loopv(demos) sendstring(demos[i].info, p); 1090 sendpacket(cn, 1, p.finalize()); 1091 } 1092 cleardemos(int n)1093 void cleardemos(int n) 1094 { 1095 if(!n) 1096 { 1097 loopv(demos) delete[] demos[i].data; 1098 demos.setsize(0); 1099 srvoutf(4, "cleared all demos"); 1100 } 1101 else if(demos.inrange(n-1)) 1102 { 1103 delete[] demos[n-1].data; 1104 demos.remove(n-1); 1105 srvoutf(4, "cleared demo %d", n); 1106 } 1107 } 1108 senddemo(int cn,int num)1109 void senddemo(int cn, int num) 1110 { 1111 if(!num) num = demos.length(); 1112 if(!demos.inrange(num-1)) return; 1113 demofile &d = demos[num-1]; 1114 sendf(cn, 2, "rim", SV_SENDDEMO, d.len, d.data); 1115 } 1116 1117 void sendwelcome(clientinfo *ci); 1118 int welcomepacket(packetbuf &p, clientinfo *ci); 1119 enddemoplayback()1120 void enddemoplayback() 1121 { 1122 if(!demoplayback) return; 1123 DELETEP(demoplayback); 1124 1125 loopv(clients) sendf(clients[i]->clientnum, 1, "ri3", SV_DEMOPLAYBACK, 0, clients[i]->clientnum); 1126 1127 srvoutf(4, "demo playback finished"); 1128 1129 loopv(clients) sendwelcome(clients[i]); 1130 } 1131 setupdemoplayback()1132 void setupdemoplayback() 1133 { 1134 demoheader hdr; 1135 string msg; 1136 msg[0] = '\0'; 1137 defformatstring(file)("%s.dmo", smapname); 1138 demoplayback = opengzfile(file, "rb"); 1139 if(!demoplayback) formatstring(msg)("could not read demo \"%s\"", file); 1140 else if(demoplayback->read(&hdr, sizeof(demoheader))!=sizeof(demoheader) || memcmp(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic))) 1141 formatstring(msg)("\"%s\" is not a demo file", file); 1142 else 1143 { 1144 lilswap(&hdr.version, 2); 1145 if(hdr.version!=DEMO_VERSION) formatstring(msg)("demo \"%s\" requires an %s version of Blood Frontier", file, hdr.version<DEMO_VERSION ? "older" : "newer"); 1146 else if(hdr.gamever!=GAMEVERSION) formatstring(msg)("demo \"%s\" requires an %s version of Blood Frontier", file, hdr.gamever<GAMEVERSION ? "older" : "newer"); 1147 } 1148 if(msg[0]) 1149 { 1150 DELETEP(demoplayback); 1151 srvoutf(4, "%s", msg); 1152 return; 1153 } 1154 1155 srvoutf(4, "playing demo \"%s\"", file); 1156 1157 sendf(-1, 1, "ri3", SV_DEMOPLAYBACK, 1, -1); 1158 1159 if(demoplayback->read(&nextplayback, sizeof(nextplayback))!=sizeof(nextplayback)) 1160 { 1161 enddemoplayback(); 1162 return; 1163 } 1164 lilswap(&nextplayback, 1); 1165 } 1166 readdemo()1167 void readdemo() 1168 { 1169 if(!demoplayback || paused) return; 1170 while(gamemillis>=nextplayback) 1171 { 1172 int chan, len; 1173 if(demoplayback->read(&chan, sizeof(chan))!=sizeof(chan) || 1174 demoplayback->read(&len, sizeof(len))!=sizeof(len)) 1175 { 1176 enddemoplayback(); 1177 return; 1178 } 1179 lilswap(&chan, 1); 1180 lilswap(&len, 1); 1181 ENetPacket *packet = enet_packet_create(NULL, len, 0); 1182 if(!packet || demoplayback->read(packet->data, len)!=len) 1183 { 1184 if(packet) enet_packet_destroy(packet); 1185 enddemoplayback(); 1186 return; 1187 } 1188 sendpacket(-1, chan, packet); 1189 if(!packet->referenceCount) enet_packet_destroy(packet); 1190 if(demoplayback->read(&nextplayback, sizeof(nextplayback))!=sizeof(nextplayback)) 1191 { 1192 enddemoplayback(); 1193 return; 1194 } 1195 lilswap(&nextplayback, 1); 1196 } 1197 } 1198 enddemorecord()1199 void enddemorecord() 1200 { 1201 if(!demorecord) return; 1202 1203 DELETEP(demorecord); 1204 1205 if(!demotmp) return; 1206 1207 int len = demotmp->size(); 1208 if(demos.length()>=MAXDEMOS) 1209 { 1210 delete[] demos[0].data; 1211 demos.remove(0); 1212 } 1213 demofile &d = demos.add(); 1214 time_t t = time(NULL); 1215 char *timestr = ctime(&t), *trim = timestr + strlen(timestr); 1216 while(trim>timestr && isspace(*--trim)) *trim = '\0'; 1217 formatstring(d.info)("%s: %s, %s, %.2f%s", timestr, gamename(gamemode, mutators), smapname, len > 1024*1024 ? len/(1024*1024.f) : len/1024.0f, len > 1024*1024 ? "MB" : "kB"); 1218 srvoutf(4, "demo \"%s\" recorded", d.info); 1219 d.data = new uchar[len]; 1220 d.len = len; 1221 demotmp->seek(0, SEEK_SET); 1222 demotmp->read(d.data, len); 1223 DELETEP(demotmp); 1224 } 1225 setupdemorecord()1226 void setupdemorecord() 1227 { 1228 if(m_demo(gamemode) || m_edit(gamemode)) return; 1229 1230 demotmp = opentempfile("demorecord", "w+b"); 1231 stream *f = opengzfile(NULL, "wb", demotmp); 1232 if(!f) { DELETEP(demotmp); return; } 1233 1234 srvoutf(4, "recording demo"); 1235 1236 demorecord = f; 1237 1238 demoheader hdr; 1239 memcpy(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic)); 1240 hdr.version = DEMO_VERSION; 1241 hdr.gamever = GAMEVERSION; 1242 lilswap(&hdr.version, 2); 1243 demorecord->write(&hdr, sizeof(demoheader)); 1244 1245 packetbuf p(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); 1246 welcomepacket(p, NULL); 1247 writedemo(1, p.buf, p.len); 1248 } 1249 endmatch()1250 void endmatch() 1251 { 1252 setpause(false); 1253 if(demorecord) enddemorecord(); 1254 if(GVAR(resetvarsonend) >= 2) resetgamevars(true); 1255 if(GVAR(resetbansonend) >= 2) bannedips.setsize(0); 1256 } 1257 checkvotes(bool force=false)1258 bool checkvotes(bool force = false) 1259 { 1260 shouldcheckvotes = false; 1261 1262 vector<votecount> votes; 1263 int maxvotes = 0; 1264 loopv(clients) 1265 { 1266 clientinfo *oi = clients[i]; 1267 if(oi->state.aitype >= 0) continue; 1268 maxvotes++; 1269 if(!oi->mapvote[0]) continue; 1270 votecount *vc = NULL; 1271 loopvj(votes) if(!strcmp(oi->mapvote, votes[j].map) && oi->modevote == votes[j].mode && oi->mutsvote == votes[j].muts) 1272 { 1273 vc = &votes[j]; 1274 break; 1275 } 1276 if(!vc) vc = &votes.add(votecount(oi->mapvote, oi->modevote, oi->mutsvote)); 1277 vc->count++; 1278 } 1279 1280 votecount *best = NULL; 1281 int morethanone = 0; 1282 loopv(votes) if(!best || votes[i].count >= best->count) 1283 { 1284 if(best && votes[i].count == best->count) 1285 morethanone++; 1286 else morethanone = 0; 1287 best = &votes[i]; 1288 } 1289 bool gotvotes = best && best->count >= min(max(maxvotes/2, force ? 1 : 2), force ? 1 : maxvotes); 1290 if(force && gotvotes && morethanone) 1291 { 1292 int c = best->count, r = rnd(morethanone), n = 0; 1293 loopv(votes) if(votes[i].count == c) 1294 { 1295 if(n != r) n++; 1296 else 1297 { 1298 best = &votes[i]; 1299 break; 1300 } 1301 } 1302 } 1303 if(force || gotvotes) 1304 { 1305 endmatch(); 1306 if(gotvotes) 1307 { 1308 srvoutf(3, "vote passed: \fs\fc%s\fS on map \fs\fc%s\fS", gamename(best->mode, best->muts), best->map); 1309 sendf(-1, 1, "ri2si3", SV_MAPCHANGE, 1, best->map, 0, best->mode, best->muts); 1310 changemap(best->map, best->mode, best->muts); 1311 } 1312 else 1313 { 1314 int mode = GVAR(defaultmode) >= 0 ? gamemode : -1, muts = GVAR(defaultmuts) >= 0 ? mutators : -1; 1315 changemode(&mode, &muts); 1316 const char *map = choosemap(smapname, mode, muts); 1317 srvoutf(3, "server chooses: \fs\fc%s\fS on map \fs\fc%s\fS", gamename(mode, muts), map); 1318 sendf(-1, 1, "ri2si3", SV_MAPCHANGE, 1, map, 0, mode, muts); 1319 changemap(map, mode, muts); 1320 } 1321 return true; 1322 } 1323 return false; 1324 } 1325 vote(char * map,int & reqmode,int & reqmuts,int sender)1326 bool vote(char *map, int &reqmode, int &reqmuts, int sender) 1327 { 1328 clientinfo *ci = (clientinfo *)getinfo(sender); modecheck(&reqmode, &reqmuts); 1329 if(!ci || !m_game(reqmode) || !map || !*map) return false; 1330 bool hasveto = haspriv(ci, PRIV_MASTER) && (mastermode >= MM_VETO || !numclients(ci->clientnum, false, -1)); 1331 if(!hasveto) 1332 { 1333 if(ci->lastvote && lastmillis-ci->lastvote <= votewait) return false; 1334 if(ci->modevote == reqmode && ci->mutsvote == reqmuts && !strcmp(ci->mapvote, map)) return false; 1335 } 1336 if(reqmode < G_LOBBY && !ci->local) 1337 { 1338 srvmsgf(ci->clientnum, "\fraccess denied, you must be a local client"); 1339 return false; 1340 } 1341 switch(modelock) 1342 { 1343 case 0: default: break; 1344 case 1: case 2: 1345 { 1346 if(!haspriv(ci, modelock == 1 ? PRIV_MASTER : PRIV_ADMIN, "change game modes")) 1347 return false; 1348 break; 1349 } 1350 case 3: case 4: 1351 { 1352 if(reqmode < modelimit && !haspriv(ci, modelock == 3 ? PRIV_ADMIN : PRIV_MAX, "change to a locked game mode")) 1353 return false; 1354 break; 1355 } 1356 } 1357 if(reqmode != G_EDITMODE && mapslock) 1358 { 1359 const char *maplist = NULL; 1360 switch(mapslock) 1361 { 1362 default: break; 1363 case 1: case 2: maplist = GVAR(allowmaps); break; 1364 case 3: case 4: 1365 { 1366 if(m_story(reqmode)) maplist = GVAR(storymaps); 1367 else if(m_duel(reqmode, reqmuts)) maplist = GVAR(duelmaps); 1368 else if(m_stf(reqmode)) maplist = GVAR(stfmaps); 1369 else if(m_ctf(reqmode)) maplist = m_multi(reqmode, reqmuts) ? GVAR(mctfmaps) : GVAR(ctfmaps); 1370 else if(m_trial(reqmode)) maplist = GVAR(trialmaps); 1371 else if(m_fight(reqmode)) maplist = GVAR(mainmaps); 1372 else maplist = GVAR(allowmaps); 1373 break; 1374 } 1375 case 5: if(!haspriv(ci, PRIV_MAX, "select a custom maps")) return false; break; 1376 } 1377 if(maplist && *maplist) 1378 { 1379 int n = listlen(maplist); 1380 bool found = false; 1381 string maploc; 1382 if(strpbrk(map, "/\\")) copystring(maploc, map); 1383 else formatstring(maploc)("maps/%s", map); 1384 loopi(n) 1385 { 1386 char *maptxt = indexlist(maplist, i); 1387 if(maptxt) 1388 { 1389 string cmapname; 1390 if(strpbrk(maptxt, "/\\")) copystring(cmapname, maptxt); 1391 else formatstring(cmapname)("maps/%s", maptxt); 1392 if(!strcmp(maploc, cmapname)) found = true; 1393 DELETEA(maptxt); 1394 } 1395 if(found) break; 1396 } 1397 if(!found && !haspriv(ci, mapslock%2 ? PRIV_MASTER : PRIV_ADMIN, "select a custom maps")) return false; 1398 } 1399 } 1400 copystring(ci->mapvote, map); 1401 ci->modevote = reqmode; 1402 ci->mutsvote = reqmuts; 1403 if(hasveto) 1404 { 1405 endmatch(); 1406 srvoutf(3, "%s forced: \fs\fc%s\fS on map \fs\fc%s\fS", colorname(ci), gamename(ci->modevote, ci->mutsvote), map); 1407 sendf(-1, 1, "ri2si3", SV_MAPCHANGE, 1, ci->mapvote, 0, ci->modevote, ci->mutsvote); 1408 changemap(ci->mapvote, ci->modevote, ci->mutsvote); 1409 return false; 1410 } 1411 return checkvotes() ? false : true; 1412 } 1413 1414 extern void waiting(clientinfo *ci, int doteam = 0, int drop = 2, bool exclude = false); 1415 setteam(clientinfo * ci,int team,bool reset=true,bool info=false)1416 void setteam(clientinfo *ci, int team, bool reset = true, bool info = false) 1417 { 1418 if(ci->team != team) 1419 { 1420 bool sm = false; 1421 if(reset) waiting(ci, 0, 1); 1422 else if(ci->state.state == CS_ALIVE) 1423 { 1424 if(smode) smode->leavegame(ci); 1425 mutate(smuts, mut->leavegame(ci)); 1426 sm = true; 1427 } 1428 ci->team = team; 1429 if(sm) 1430 { 1431 if(smode) smode->entergame(ci); 1432 mutate(smuts, mut->entergame(ci)); 1433 } 1434 if(ci->state.aitype < 0) aiman::dorefresh = true; // get the ai to reorganise 1435 } 1436 if(info) sendf(-1, 1, "ri3", SV_SETTEAM, ci->clientnum, ci->team); 1437 } 1438 1439 struct teamscore 1440 { 1441 int team; 1442 float score; 1443 int clients; 1444 teamscoreserver::teamscore1445 teamscore(int n) : team(n), score(0.f), clients(0) {} teamscoreserver::teamscore1446 teamscore(int n, float r) : team(n), score(r), clients(0) {} teamscoreserver::teamscore1447 teamscore(int n, int s) : team(n), score(s), clients(0) {} 1448 ~teamscoreserver::teamscore1449 ~teamscore() {} 1450 }; 1451 chooseteam(clientinfo * ci,int suggest=-1)1452 int chooseteam(clientinfo *ci, int suggest = -1) 1453 { 1454 if(ci->state.aitype >= AI_START) return TEAM_ENEMY; 1455 else if(m_fight(gamemode) && m_team(gamemode, mutators) && ci->state.state != CS_SPECTATOR && ci->state.state != CS_EDITING) 1456 { 1457 int team = isteam(gamemode, mutators, suggest, TEAM_FIRST) ? suggest : -1, balance = GVAR(teambalance); 1458 if(balance < 3 && ci->state.aitype >= 0) balance = 1; 1459 if(balance || team < 0) 1460 { 1461 teamscore teamscores[TEAM_NUM] = { 1462 teamscore(TEAM_ALPHA), teamscore(TEAM_BETA), teamscore(TEAM_GAMMA), teamscore(TEAM_DELTA) 1463 }; 1464 loopv(clients) 1465 { 1466 clientinfo *cp = clients[i]; 1467 if(!cp->team || cp == ci || cp->state.state == CS_SPECTATOR || cp->state.state == CS_EDITING) continue; 1468 if((cp->state.aitype >= 0 && cp->state.ownernum < 0) || cp->state.aitype >= AI_START) continue; 1469 if(ci->state.aitype >= 0 || (ci->state.aitype < 0 && cp->state.aitype < 0)) 1470 { // remember: ai just balance teams 1471 cp->state.timeplayed += lastmillis-cp->state.lasttimeplayed; 1472 cp->state.lasttimeplayed = lastmillis; 1473 teamscore &ts = teamscores[cp->team-TEAM_FIRST]; 1474 ts.score += cp->state.score/float(max(cp->state.timeplayed, 1)); 1475 ts.clients++; 1476 } 1477 } 1478 teamscore *worst = &teamscores[0]; 1479 if(balance != 3 || ci->state.aitype >= 0) 1480 { 1481 loopi(numteams(gamemode, mutators)) 1482 { 1483 teamscore &ts = teamscores[i]; 1484 switch(balance) 1485 { 1486 case 2: 1487 { 1488 if(ts.score < worst->score || (ts.score == worst->score && ts.clients < worst->clients)) 1489 worst = &ts; 1490 break; 1491 } 1492 case 3: 1493 { 1494 if(!i) 1495 { 1496 worst = &teamscores[1]; 1497 break; // don't use team alpha for bots in this case 1498 } 1499 } // fall through 1500 case 1: default: 1501 { 1502 if(ts.clients < worst->clients || (ts.clients == worst->clients && ts.score < worst->score)) 1503 worst = &ts; 1504 break; 1505 } 1506 } 1507 } 1508 } 1509 team = worst->team; 1510 } 1511 return team; 1512 } 1513 return TEAM_NEUTRAL; 1514 } 1515 stopdemo()1516 void stopdemo() 1517 { 1518 if(m_demo(gamemode)) enddemoplayback(); 1519 else enddemorecord(); 1520 } 1521 findscore(clientinfo * ci,bool insert)1522 savedscore &findscore(clientinfo *ci, bool insert) 1523 { 1524 uint ip = getclientip(ci->clientnum); 1525 if(!ip) return *(savedscore *)0; 1526 if(!insert) 1527 { 1528 loopv(clients) 1529 { 1530 clientinfo *oi = clients[i]; 1531 if(oi->clientnum != ci->clientnum && getclientip(oi->clientnum) == ip && !strcmp(oi->name, ci->name)) 1532 { 1533 oi->state.timeplayed += lastmillis-oi->state.lasttimeplayed; 1534 oi->state.lasttimeplayed = lastmillis; 1535 static savedscore curscore; 1536 curscore.save(oi->state); 1537 return curscore; 1538 } 1539 } 1540 } 1541 loopv(scores) 1542 { 1543 savedscore &sc = scores[i]; 1544 if(sc.ip == ip && !strcmp(sc.name, ci->name)) return sc; 1545 } 1546 if(!insert) return *(savedscore *)0; 1547 savedscore &sc = scores.add(); 1548 sc.ip = ip; 1549 copystring(sc.name, ci->name); 1550 return sc; 1551 } 1552 savescore(clientinfo * ci)1553 void savescore(clientinfo *ci) 1554 { 1555 savedscore &sc = findscore(ci, true); 1556 if(&sc) sc.save(ci->state); 1557 } 1558 givepoints(clientinfo * ci,int points)1559 void givepoints(clientinfo *ci, int points) 1560 { 1561 ci->state.score += points; ci->state.points += points; 1562 sendf(-1, 1, "ri4", SV_POINTS, ci->clientnum, points, ci->state.points); 1563 } 1564 1565 struct droplist { int weap, ent; }; dropitems(clientinfo * ci,int level=2)1566 void dropitems(clientinfo *ci, int level = 2) 1567 { 1568 if(ci->state.aitype >= AI_START) ci->state.weapreset(false); 1569 else 1570 { 1571 servstate &ts = ci->state; 1572 vector<droplist> drop; 1573 int sweap = m_weapon(gamemode, mutators); 1574 if(level >= 2 && GVAR(kamikaze) && (GVAR(kamikaze) > 2 || (ts.hasweap(WEAP_GRENADE, sweap) && (GVAR(kamikaze) > 1 || ts.weapselect == WEAP_GRENADE)))) 1575 { 1576 ts.weapshots[WEAP_GRENADE][0].add(-1); 1577 droplist &d = drop.add(); 1578 d.weap = WEAP_GRENADE; 1579 d.ent = -1; 1580 } 1581 if(!m_noitems(gamemode, mutators)) 1582 { 1583 loopi(WEAP_MAX) if(ts.hasweap(i, sweap, 1) && sents.inrange(ts.entid[i])) 1584 { 1585 sents[ts.entid[i]].millis = gamemillis; 1586 if(level && GVAR(itemdropping) && !(sents[ts.entid[i]].attrs[1]&WEAP_F_FORCED)) 1587 { 1588 ts.dropped.add(ts.entid[i]); 1589 droplist &d = drop.add(); 1590 d.weap = i; 1591 d.ent = ts.entid[i]; 1592 if(w_carry(i, sweap)) sents[ts.entid[i]].millis += GVAR(itemspawntime); 1593 } 1594 } 1595 } 1596 if(level && !drop.empty()) 1597 sendf(-1, 1, "ri3iv", SV_DROP, ci->clientnum, -1, drop.length(), drop.length()*sizeof(droplist)/sizeof(int), drop.getbuf()); 1598 ts.weapreset(false); 1599 } 1600 } 1601 1602 #include "stfmode.h" 1603 #include "ctfmode.h" 1604 #include "duelmut.h" 1605 #include "aiman.h" 1606 changemap(const char * name,int mode,int muts)1607 void changemap(const char *name, int mode, int muts) 1608 { 1609 hasgameinfo = maprequest = mapsending = shouldcheckvotes = aiman::autooverride = false; 1610 aiman::dorefresh = true; 1611 stopdemo(); 1612 gamemode = mode; mutators = muts; changemode(&gamemode, &mutators); 1613 nplayers = gamemillis = interm = 0; 1614 oldtimelimit = GVAR(timelimit); 1615 minremain = GVAR(timelimit) ? GVAR(timelimit) : -1; 1616 gamelimit = GVAR(timelimit) ? minremain*60000 : 0; 1617 sents.setsize(0); scores.setsize(0); 1618 setuptriggers(false); setupspawns(false); 1619 1620 const char *reqmap = name && *name ? name : pickmap(smapname, gamemode, mutators); 1621 #ifdef STANDALONE // interferes with savemap on clients, in which case we can just use the auto-request 1622 loopi(3) 1623 { 1624 if(mapdata[i]) DELETEP(mapdata[i]); 1625 const char *reqext = "xxx"; 1626 switch(i) 1627 { 1628 case 2: reqext = "cfg"; break; 1629 case 1: reqext = "png"; break; 1630 default: case 0: reqext = "bgz"; break; 1631 } 1632 defformatstring(reqfile)(strstr(reqmap, "maps/")==reqmap || strstr(reqmap, "maps\\")==reqmap ? "%s" : "maps/%s", reqmap); 1633 defformatstring(reqfext)("%s.%s", reqfile, reqext); 1634 if(!(mapdata[i] = openfile(reqfext, "rb")) && !i) 1635 { 1636 loopk(3) if(mapdata[k]) DELETEP(mapdata[k]); 1637 break; 1638 } 1639 } 1640 #endif 1641 copystring(smapname, reqmap); 1642 1643 // server modes 1644 if(m_stf(gamemode)) smode = &stfmode; 1645 else if(m_ctf(gamemode)) smode = &ctfmode; 1646 else smode = NULL; 1647 smuts.setsize(0); 1648 if(m_duke(gamemode, mutators)) smuts.add(&duelmutator); 1649 if(smode) smode->reset(false); 1650 mutate(smuts, mut->reset(false)); 1651 1652 if(m_demo(gamemode)) kicknonlocalclients(DISC_PRIVATE); 1653 loopv(clients) 1654 { 1655 clientinfo *ci = clients[i]; 1656 ci->mapchange(true); 1657 if(ci->state.state == CS_SPECTATOR) continue; 1658 else if(ci->state.aitype < 0 && m_play(gamemode)) 1659 { 1660 ci->state.state = CS_SPECTATOR; 1661 sendf(-1, 1, "ri3", SV_SPECTATOR, ci->clientnum, 1); 1662 setteam(ci, TEAM_NEUTRAL, false, true); 1663 } 1664 else 1665 { 1666 ci->state.state = CS_DEAD; 1667 waiting(ci, 2, 1); 1668 } 1669 } 1670 1671 if(m_fight(gamemode) && numclients()) sendf(-1, 1, "ri2", SV_TIMEUP, minremain); 1672 if(m_demo(gamemode)) setupdemoplayback(); 1673 else if(demonextmatch) 1674 { 1675 demonextmatch = false; 1676 setupdemorecord(); 1677 } 1678 } 1679 1680 struct crcinfo 1681 { 1682 int crc, matches; 1683 crcinfoserver::crcinfo1684 crcinfo(int crc, int matches) : crc(crc), matches(matches) {} 1685 compareserver::crcinfo1686 static int compare(const crcinfo *x, const crcinfo *y) 1687 { 1688 if(x->matches > y->matches) return -1; 1689 if(x->matches < y->matches) return 1; 1690 return 0; 1691 } 1692 }; 1693 checkmaps(int req=-1)1694 void checkmaps(int req = -1) 1695 { 1696 if(m_edit(gamemode) || !smapname[0]) return; 1697 vector<crcinfo> crcs; 1698 int total = 0, unsent = 0, invalid = 0; 1699 loopv(clients) 1700 { 1701 clientinfo *ci = clients[i]; 1702 if(ci->state.state==CS_SPECTATOR || ci->state.aitype >= 0) continue; 1703 total++; 1704 if(!ci->clientmap[0]) 1705 { 1706 if(ci->mapcrc < 0) invalid++; 1707 else if(!ci->mapcrc) unsent++; 1708 } 1709 else 1710 { 1711 crcinfo *match = NULL; 1712 loopvj(crcs) if(crcs[j].crc == ci->mapcrc) { match = &crcs[j]; break; } 1713 if(!match) crcs.add(crcinfo(ci->mapcrc, 1)); 1714 else match->matches++; 1715 } 1716 } 1717 if(total - unsent < min(total, 4)) return; 1718 crcs.sort(crcinfo::compare); 1719 loopv(clients) 1720 { 1721 clientinfo *ci = clients[i]; 1722 if(ci->state.state==CS_SPECTATOR || ci->state.aitype >= 0 || ci->clientmap[0] || ci->mapcrc >= 0 || (req < 0 && ci->warned)) continue; 1723 srvmsgf(req, "\fy\fzRe%s has modified map \"%s\"", colorname(ci), smapname); 1724 if(req < 0) ci->warned = true; 1725 } 1726 if(crcs.empty() || crcs.length() < 2) return; 1727 loopv(crcs) 1728 { 1729 crcinfo &info = crcs[i]; 1730 if(i || info.matches <= crcs[i+1].matches) loopvj(clients) 1731 { 1732 clientinfo *ci = clients[j]; 1733 if(ci->state.state==CS_SPECTATOR || ci->state.aitype >= 0 || !ci->clientmap[0] || ci->mapcrc != info.crc || (req < 0 && ci->warned)) continue; 1734 srvmsgf(req, "\fy\fzRe%s has modified map \"%s\"", colorname(ci), smapname); 1735 if(req < 0) ci->warned = true; 1736 } 1737 } 1738 } 1739 servcmd(int nargs,const char * cmd,const char * arg)1740 bool servcmd(int nargs, const char *cmd, const char *arg) 1741 { // incoming command from scripts 1742 ident *id = idents->access(cmd); 1743 if(id && id->flags&IDF_SERVER) 1744 { 1745 static string scmdval; scmdval[0] = 0; 1746 switch(id->type) 1747 { 1748 case ID_CCOMMAND: 1749 case ID_COMMAND: 1750 { 1751 string s; 1752 if(nargs <= 1 || !arg) formatstring(s)("%s", cmd); 1753 else formatstring(s)("%s %s", cmd, arg); 1754 char *ret = executeret(s); 1755 if(ret) 1756 { 1757 if(*ret) conoutft(CON_MESG, "\fg%s returned %s", cmd, ret); 1758 delete[] ret; 1759 } 1760 return true; 1761 } 1762 case ID_VAR: 1763 { 1764 if(nargs <= 1 || !arg) 1765 { 1766 conoutft(CON_MESG, id->flags&IDF_HEX ? (id->maxval==0xFFFFFF ? "\fg%s = 0x%.6X" : "\fg%s = 0x%X") : "\fg%s = %d", cmd, *id->storage.i); 1767 return true; 1768 } 1769 if(id->maxval < id->minval) 1770 { 1771 conoutft(CON_MESG, "\frcannot override variable: %s", cmd); 1772 return true; 1773 } 1774 int ret = atoi(arg); 1775 if(ret < id->minval || ret > id->maxval) 1776 { 1777 conoutft(CON_MESG, 1778 id->flags&IDF_HEX ? 1779 (id->minval <= 255 ? "\frvalid range for %s is %d..0x%X" : "\frvalid range for %s is 0x%X..0x%X") : 1780 "\frvalid range for %s is %d..%d", cmd, id->minval, id->maxval); 1781 return true; 1782 } 1783 *id->storage.i = ret; 1784 id->changed(); 1785 formatstring(scmdval)(id->flags&IDF_HEX ? (id->maxval==0xFFFFFF ? "0x%.6X" : "0x%X") : "%d", *id->storage.i); 1786 break; 1787 } 1788 case ID_FVAR: 1789 { 1790 if(nargs <= 1 || !arg) 1791 { 1792 conoutft(CON_MESG, "\fg%s = %s", cmd, floatstr(*id->storage.f)); 1793 return true; 1794 } 1795 float ret = atof(arg); 1796 if(ret < id->minvalf || ret > id->maxvalf) 1797 { 1798 conoutft(CON_MESG, "\frvalid range for %s is %s..%s", cmd, floatstr(id->minvalf), floatstr(id->maxvalf)); 1799 return true; 1800 } 1801 *id->storage.f = ret; 1802 id->changed(); 1803 formatstring(scmdval)("%s", floatstr(*id->storage.f)); 1804 break; 1805 } 1806 case ID_SVAR: 1807 { 1808 if(nargs <= 1 || !arg) 1809 { 1810 conoutft(CON_MESG, strchr(*id->storage.s, '"') ? "\fg%s = [%s]" : "\fg%s = \"%s\"", cmd, *id->storage.s); 1811 return true; 1812 } 1813 delete[] *id->storage.s; 1814 *id->storage.s = newstring(arg); 1815 id->changed(); 1816 formatstring(scmdval)("%s", *id->storage.s); 1817 break; 1818 } 1819 default: return false; 1820 } 1821 sendf(-1, 1, "ri2ss", SV_COMMAND, -1, &id->name[3], scmdval); 1822 arg = scmdval; 1823 return true; 1824 } 1825 return false; // parse will spit out "unknown command" in this case 1826 } 1827 parsecommand(clientinfo * ci,int nargs,const char * cmd,const char * arg)1828 void parsecommand(clientinfo *ci, int nargs, const char *cmd, const char *arg) 1829 { // incoming commands from clients 1830 defformatstring(cmdname)("sv_%s", cmd); 1831 ident *id = idents->access(cmdname); 1832 if(id && id->flags&IDF_SERVER) 1833 { 1834 mkstring(val); 1835 switch(id->type) 1836 { 1837 case ID_CCOMMAND: 1838 case ID_COMMAND: 1839 { 1840 if(!haspriv(ci, varslock >= 2 ? PRIV_MAX : (varslock ? PRIV_ADMIN : PRIV_MASTER), "change variables")) return; 1841 string s; 1842 if(nargs <= 1 || !arg) formatstring(s)("sv_%s", cmd); 1843 else formatstring(s)("sv_%s %s", cmd, arg); 1844 char *ret = executeret(s); 1845 if(ret && *ret) srvoutf(3, "\fg%s executed %s (returned: %s)", colorname(ci), cmd, ret); 1846 else srvoutf(3, "\fg%s executed %s", colorname(ci), cmd); 1847 if(ret) delete[] ret; 1848 return; 1849 } 1850 case ID_VAR: 1851 { 1852 if(nargs <= 1 || !arg) 1853 { 1854 srvmsgf(ci->clientnum, id->flags&IDF_HEX ? (id->maxval==0xFFFFFF ? "\fg%s = 0x%.6X" : "\fg%s = 0x%X") : "\fg%s = %d", cmd, *id->storage.i); 1855 return; 1856 } 1857 else if(!haspriv(ci, varslock >= 2 ? PRIV_MAX : (varslock ? PRIV_ADMIN : PRIV_MASTER), "change variables")) return; 1858 if(id->maxval < id->minval) 1859 { 1860 srvmsgf(ci->clientnum, "\frcannot override variable: %s", cmd); 1861 return; 1862 } 1863 int ret = atoi(arg); 1864 if(ret < id->minval || ret > id->maxval) 1865 { 1866 srvmsgf(ci->clientnum, 1867 id->flags&IDF_HEX ? 1868 (id->minval <= 255 ? "\frvalid range for %s is %d..0x%X" : "\frvalid range for %s is 0x%X..0x%X") : 1869 "\frvalid range for %s is %d..%d", cmd, id->minval, id->maxval); 1870 return; 1871 } 1872 *id->storage.i = ret; 1873 id->changed(); 1874 formatstring(val)(id->flags&IDF_HEX ? (id->maxval==0xFFFFFF ? "0x%.6X" : "0x%X") : "%d", *id->storage.i); 1875 break; 1876 } 1877 case ID_FVAR: 1878 { 1879 if(nargs <= 1 || !arg) 1880 { 1881 srvmsgf(ci->clientnum, "\fg%s = %s", cmd, floatstr(*id->storage.f)); 1882 return; 1883 } 1884 else if(!haspriv(ci, varslock >= 2 ? PRIV_MAX : (varslock ? PRIV_ADMIN : PRIV_MASTER), "change variables")) return; 1885 float ret = atof(arg); 1886 if(ret < id->minvalf || ret > id->maxvalf) 1887 { 1888 srvmsgf(ci->clientnum, "\frvalid range for %s is %s..%s", cmd, floatstr(id->minvalf), floatstr(id->maxvalf)); 1889 return; 1890 } 1891 *id->storage.f = ret; 1892 id->changed(); 1893 formatstring(val)("%s", floatstr(*id->storage.f)); 1894 break; 1895 } 1896 case ID_SVAR: 1897 { 1898 if(nargs <= 1 || !arg) 1899 { 1900 srvmsgf(ci->clientnum, strchr(*id->storage.s, '"') ? "\fg%s = [%s]" : "\fg%s = \"%s\"", cmd, *id->storage.s); 1901 return; 1902 } 1903 else if(!haspriv(ci, varslock >= 2 ? PRIV_MAX : (varslock ? PRIV_ADMIN : PRIV_MASTER), "change variables")) return; 1904 delete[] *id->storage.s; 1905 *id->storage.s = newstring(arg); 1906 id->changed(); 1907 formatstring(val)("%s", *id->storage.s); 1908 break; 1909 } 1910 default: return; 1911 } 1912 sendf(-1, 1, "ri2ss", SV_COMMAND, ci->clientnum, &id->name[3], val); 1913 relayf(3, "\fg%s set %s to %s", colorname(ci), &id->name[3], val); 1914 } 1915 else srvmsgf(ci->clientnum, "\frunknown command: %s", cmd); 1916 } 1917 choosebestclient()1918 clientinfo *choosebestclient() 1919 { 1920 clientinfo *best = NULL; 1921 loopv(clients) 1922 { 1923 clientinfo *cs = clients[i]; 1924 if(cs->state.aitype >= 0 || !cs->name[0] || !cs->online || cs->wantsmap) continue; 1925 if(!best || cs->state.timeplayed > best->state.timeplayed) best = cs; 1926 } 1927 return best; 1928 } 1929 sendservinit(clientinfo * ci)1930 void sendservinit(clientinfo *ci) 1931 { 1932 sendf(ci->clientnum, 1, "ri5", SV_SERVERINIT, ci->clientnum, GAMEVERSION, ci->sessionid, serverpass[0] ? 1 : 0); 1933 } 1934 restorescore(clientinfo * ci)1935 bool restorescore(clientinfo *ci) 1936 { 1937 savedscore &sc = findscore(ci, false); 1938 if(&sc) 1939 { 1940 sc.restore(ci->state); 1941 return true; 1942 } 1943 return false; 1944 } 1945 sendresume(clientinfo * ci)1946 void sendresume(clientinfo *ci) 1947 { 1948 servstate &gs = ci->state; 1949 sendf(-1, 1, "ri7vi", SV_RESUME, ci->clientnum, gs.state, gs.frags, gs.health, gs.cptime, gs.weapselect, WEAP_MAX, &gs.ammo[0], -1); 1950 } 1951 putinitclient(clientinfo * ci,packetbuf & p)1952 void putinitclient(clientinfo *ci, packetbuf &p) 1953 { 1954 if(ci->state.aitype >= 0) 1955 { 1956 if(ci->state.ownernum >= 0) 1957 { 1958 putint(p, SV_INITAI); 1959 putint(p, ci->clientnum); 1960 putint(p, ci->state.ownernum); 1961 putint(p, ci->state.aitype); 1962 putint(p, ci->state.aientity); 1963 putint(p, ci->state.skill); 1964 sendstring(ci->name, p); 1965 putint(p, ci->team); 1966 } 1967 } 1968 else 1969 { 1970 putint(p, SV_CLIENTINIT); 1971 putint(p, ci->clientnum); 1972 sendstring(ci->name, p); 1973 putint(p, ci->team); 1974 } 1975 } 1976 sendinitclient(clientinfo * ci)1977 void sendinitclient(clientinfo *ci) 1978 { 1979 packetbuf p(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); 1980 putinitclient(ci, p); 1981 sendpacket(-1, 1, p.finalize(), ci->clientnum); 1982 } 1983 welcomeinitclient(packetbuf & p,int exclude=-1)1984 void welcomeinitclient(packetbuf &p, int exclude = -1) 1985 { 1986 loopv(clients) 1987 { 1988 clientinfo *ci = clients[i]; 1989 if(!ci->connected || ci->clientnum == exclude) continue; 1990 1991 putinitclient(ci, p); 1992 } 1993 } 1994 sendwelcome(clientinfo * ci)1995 void sendwelcome(clientinfo *ci) 1996 { 1997 packetbuf p(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); 1998 int chan = welcomepacket(p, ci); 1999 sendpacket(ci->clientnum, chan, p.finalize()); 2000 } 2001 welcomepacket(packetbuf & p,clientinfo * ci)2002 int welcomepacket(packetbuf &p, clientinfo *ci) 2003 { 2004 putint(p, SV_WELCOME); 2005 putint(p, SV_MAPCHANGE); 2006 if(!smapname[0]) putint(p, 0); 2007 else 2008 { 2009 putint(p, 1); 2010 sendstring(smapname, p); 2011 } 2012 if(!ci) putint(p, 0); 2013 #if 0 2014 else if(!ci->online && m_edit(gamemode) && numclients(ci->clientnum, false, -1)) 2015 { 2016 ci->wantsmap = true; 2017 clientinfo *best = choosebestclient(); 2018 if(best) 2019 { 2020 loopi(3) if(mapdata[i]) DELETEP(mapdata[i]); 2021 mapsending = false; 2022 sendf(best->clientnum, 1, "ri", SV_GETMAP); 2023 putint(p, 1); 2024 } 2025 else putint(p, 0); 2026 } 2027 #endif 2028 else 2029 { 2030 ci->wantsmap = false; 2031 if(ci->online) putint(p, 2); // we got a temp map eh? 2032 else putint(p, 0); 2033 } 2034 putint(p, gamemode); 2035 putint(p, mutators); 2036 2037 enumerate(*idents, ident, id, { 2038 if(id.flags&IDF_SERVER) // reset vars 2039 { 2040 mkstring(val); 2041 switch(id.type) 2042 { 2043 case ID_VAR: 2044 { 2045 formatstring(val)(id.flags&IDF_HEX ? (id.maxval==0xFFFFFF ? "0x%.6X" : "0x%X") : "%d", *id.storage.i); 2046 break; 2047 } 2048 case ID_FVAR: 2049 { 2050 formatstring(val)("%s", floatstr(*id.storage.f)); 2051 break; 2052 } 2053 case ID_SVAR: 2054 { 2055 formatstring(val)("%s", *id.storage.s); 2056 break; 2057 } 2058 default: break; 2059 } 2060 putint(p, SV_COMMAND); 2061 putint(p, -1); 2062 sendstring(&id.name[3], p); 2063 sendstring(val, p); 2064 } 2065 }); 2066 2067 if(!ci || (m_fight(gamemode) && numclients())) 2068 { 2069 putint(p, SV_TIMEUP); 2070 putint(p, minremain); 2071 } 2072 2073 if(hasgameinfo) 2074 { 2075 putint(p, SV_GAMEINFO); 2076 loopv(sents) if(enttype[sents[i].type].usetype == EU_ITEM || sents[i].type == TRIGGER) 2077 { 2078 putint(p, i); 2079 if(enttype[sents[i].type].usetype == EU_ITEM) putint(p, finditem(i, false) ? 1 : 0); 2080 else putint(p, sents[i].spawned ? 1 : 0); 2081 } 2082 putint(p, -1); 2083 } 2084 2085 if(ci) 2086 { 2087 //if(m_play(gamemode) || mastermode >= MM_LOCKED) 2088 //{ 2089 ci->state.state = CS_SPECTATOR; 2090 ci->team = TEAM_NEUTRAL; 2091 putint(p, SV_SPECTATOR); 2092 putint(p, ci->clientnum); 2093 putint(p, 1); 2094 sendf(-1, 1, "ri3x", SV_SPECTATOR, ci->clientnum, 1, ci->clientnum); 2095 /* 2096 } 2097 else 2098 { 2099 ci->state.state = CS_DEAD; 2100 waiting(ci, 0, 1, true); 2101 putint(p, SV_WAITING); 2102 putint(p, ci->clientnum); 2103 if(!isteam(gamemode, mutators, ci->team, TEAM_FIRST)) ci->team = chooseteam(ci); 2104 } 2105 */ 2106 putint(p, SV_SETTEAM); 2107 putint(p, ci->clientnum); 2108 putint(p, ci->team); 2109 } 2110 if(!ci || clients.length() > 1) 2111 { 2112 putint(p, SV_RESUME); 2113 loopv(clients) 2114 { 2115 clientinfo *oi = clients[i]; 2116 if(ci && oi->clientnum == ci->clientnum) continue; 2117 putint(p, oi->clientnum); 2118 sendstate(oi->state, p); 2119 } 2120 putint(p, -1); 2121 welcomeinitclient(p, ci ? ci->clientnum : -1); 2122 } 2123 2124 if(*servermotd) 2125 { 2126 putint(p, SV_ANNOUNCE); 2127 putint(p, S_GUIACT); 2128 putint(p, CON_CHAT); 2129 sendstring(servermotd, p); 2130 } 2131 2132 if(smode) smode->initclient(ci, p, true); 2133 mutate(smuts, mut->initclient(ci, p, true)); 2134 if(ci) ci->online = true; 2135 aiman::dorefresh = true; 2136 return 1; 2137 } 2138 clearevent(clientinfo * ci)2139 void clearevent(clientinfo *ci) { delete ci->events.remove(0); } 2140 dodamage(clientinfo * target,clientinfo * actor,int damage,int weap,int flags,const ivec & hitpush=ivec (0,0,0))2141 void dodamage(clientinfo *target, clientinfo *actor, int damage, int weap, int flags, const ivec &hitpush = ivec(0, 0, 0)) 2142 { 2143 int realdamage = damage, realflags = flags, nodamage = 0; realflags &= ~HIT_SFLAGS; 2144 if((realflags&HIT_WAVE || (isweap(weap) && !weaptype[weap].explode[realflags&HIT_ALT ? 1 : 0])) && (realflags&HIT_FULL)) 2145 realflags &= ~HIT_FULL; 2146 if(smode && !smode->damage(target, actor, realdamage, weap, realflags, hitpush)) { nodamage++; } 2147 mutate(smuts, if(!mut->damage(target, actor, realdamage, weap, realflags, hitpush)) { nodamage++; }); 2148 if((actor == target && !GVAR(selfdamage)) || (m_trial(gamemode) && !GVAR(trialdamage))) nodamage++; 2149 else if(m_team(gamemode, mutators) && actor->team == target->team) 2150 { 2151 if(weap == WEAP_MELEE) nodamage++; 2152 else if(m_story(gamemode)) { if(target->team == TEAM_NEUTRAL) nodamage++; } 2153 else if(m_fight(gamemode)) switch(GVAR(teamdamage)) 2154 { 2155 case 2: default: break; 2156 case 1: if(actor == target || actor->state.aitype < 0) break; 2157 case 0: nodamage++; break; 2158 } 2159 } 2160 if(nodamage || !hithurts(realflags)) realflags = HIT_WAVE|(flags&HIT_ALT ? HIT_ALT : 0); // so it impacts, but not hurts 2161 else 2162 { 2163 target->state.dodamage(target->state.health -= realdamage); 2164 target->state.lastpain = gamemillis; 2165 actor->state.damage += realdamage; 2166 if(target->state.health <= 0) realflags |= HIT_KILL; 2167 if(GVAR(fireburntime) && doesburn(weap, flags)) 2168 { 2169 target->state.lastfire = target->state.lastfireburn = gamemillis; 2170 target->state.lastfireowner = actor->clientnum; 2171 } 2172 } 2173 sendf(-1, 1, "ri7i3", SV_DAMAGE, target->clientnum, actor->clientnum, weap, realflags, realdamage, target->state.health, hitpush.x, hitpush.y, hitpush.z); 2174 if(GVAR(vampire) && actor->state.state == CS_ALIVE) 2175 { 2176 int total = m_health(gamemode, mutators), amt = 0, delay = 0; 2177 if(smode) smode->regen(actor, total, amt, delay); 2178 if(total && actor->state.health < total) 2179 { 2180 int rgn = actor->state.health, heal = clamp(actor->state.health+realdamage, 0, total), eff = heal-rgn; 2181 actor->state.health = heal; actor->state.lastregen = gamemillis; 2182 sendf(-1, 1, "ri4", SV_REGEN, actor->clientnum, actor->state.health, eff); 2183 } 2184 } 2185 2186 if(realflags&HIT_KILL) 2187 { 2188 int fragvalue = target == actor || (m_team(gamemode, mutators) && target->team == actor->team) ? -1 : 1, 2189 pointvalue = smode ? smode->points(target, actor) : fragvalue, style = FRAG_NONE; 2190 if(!m_insta(gamemode, mutators) && (realflags&HIT_EXPLODE || realdamage > m_health(gamemode, mutators)*3/2)) 2191 style = FRAG_OBLITERATE; 2192 actor->state.frags += fragvalue; 2193 2194 if(m_team(gamemode, mutators) && actor->team == target->team) 2195 { 2196 actor->state.spree = 0; 2197 if(actor->state.aitype < AI_START) 2198 { 2199 if(actor != target) actor->state.teamkills++; 2200 pointvalue *= 2; 2201 } 2202 } 2203 else if(actor != target) 2204 { 2205 int logs = 0; 2206 target->state.spree = 0; 2207 if(actor->state.aitype < AI_START) 2208 { 2209 actor->state.spree++; 2210 actor->state.fraglog.add(target->clientnum); 2211 if((flags&HIT_PROJ) && (flags&HIT_HEAD)) 2212 { 2213 style |= FRAG_HEADSHOT; 2214 pointvalue *= 2; 2215 } 2216 if(m_fight(gamemode) && GVAR(multikilldelay)) 2217 { 2218 logs = 0; 2219 loopv(actor->state.fragmillis) 2220 { 2221 if(lastmillis-actor->state.fragmillis[i] > GVAR(multikilldelay)) actor->state.fragmillis.remove(i--); 2222 else logs++; 2223 } 2224 if(!logs) actor->state.rewards &= ~FRAG_MULTI; 2225 actor->state.fragmillis.add(lastmillis); logs++; 2226 if(logs >= 2) 2227 { 2228 int offset = clamp(logs-2, 0, 2), type = 1<<(FRAG_MKILL+offset); // double, triple, multi.. 2229 if(!(actor->state.rewards&type)) 2230 { 2231 style |= type; 2232 actor->state.rewards |= type; 2233 pointvalue *= offset+1; 2234 //loopv(actor->state.fragmillis) actor->state.fragmillis[i] = lastmillis; 2235 } 2236 } 2237 } 2238 if(actor->state.spree <= GVAR(spreecount)*FRAG_SPREES && !(actor->state.spree%GVAR(spreecount))) 2239 { 2240 int offset = clamp((actor->state.spree/GVAR(spreecount)), 1, int(FRAG_SPREES))-1, type = 1<<(FRAG_SPREE+offset); 2241 if(!(actor->state.rewards&type)) 2242 { 2243 style |= type; 2244 actor->state.rewards |= type; 2245 pointvalue *= offset+1; 2246 } 2247 } 2248 if(m_fight(gamemode)) 2249 { 2250 logs = 0; 2251 loopv(target->state.fraglog) if(target->state.fraglog[i] == actor->clientnum) { logs++; target->state.fraglog.remove(i--); } 2252 if(logs >= GVAR(dominatecount)) 2253 { 2254 style |= FRAG_REVENGE; 2255 pointvalue *= GVAR(dominatecount); 2256 } 2257 logs = 0; 2258 loopv(actor->state.fraglog) if(actor->state.fraglog[i] == target->clientnum) logs++; 2259 if(logs == GVAR(dominatecount)) 2260 { 2261 style |= FRAG_DOMINATE; 2262 pointvalue *= GVAR(dominatecount); 2263 } 2264 } 2265 } 2266 } 2267 else actor->state.spree = 0; 2268 target->state.deaths++; 2269 dropitems(target); givepoints(actor, pointvalue); 2270 sendf(-1, 1, "ri8", SV_DIED, target->clientnum, actor->clientnum, actor->state.frags, style, weap, realflags, realdamage); 2271 target->position.setsizenodelete(0); 2272 if(smode) smode->died(target, actor); 2273 mutate(smuts, mut->died(target, actor)); 2274 target->state.state = CS_DEAD; // don't issue respawn yet until DEATHMILLIS has elapsed 2275 target->state.lastdeath = gamemillis; 2276 } 2277 } 2278 process(clientinfo * ci)2279 void suicideevent::process(clientinfo *ci) 2280 { 2281 servstate &gs = ci->state; 2282 if(gs.state != CS_ALIVE) return; 2283 if(!(flags&HIT_DEATH) && !(flags&HIT_LOST)) 2284 { 2285 if(smode && !smode->damage(ci, ci, ci->state.health, -1, flags)) { return; } 2286 mutate(smuts, if(!mut->damage(ci, ci, ci->state.health, -1, flags)) { return; }); 2287 } 2288 int fragvalue = -1, pointvalue = smode ? smode->points(ci, ci) : fragvalue; 2289 ci->state.frags += fragvalue; 2290 ci->state.spree = 0; 2291 if(!flags) 2292 { 2293 ci->state.cpmillis = 0; 2294 ci->state.cpnodes.setsize(0); 2295 } 2296 ci->state.deaths++; 2297 dropitems(ci); givepoints(ci, pointvalue); 2298 if(!(flags&HIT_FULL)) flags |= HIT_FULL; 2299 if(GVAR(fireburntime) && (flags&HIT_MELT || flags&HIT_BURN)) 2300 { 2301 ci->state.lastfire = ci->state.lastfireburn = gamemillis; 2302 ci->state.lastfireowner = ci->clientnum; 2303 } 2304 sendf(-1, 1, "ri8", SV_DIED, ci->clientnum, ci->clientnum, ci->state.frags, 0, -1, flags, ci->state.health); 2305 ci->position.setsizenodelete(0); 2306 if(smode) smode->died(ci, NULL); 2307 mutate(smuts, mut->died(ci, NULL)); 2308 gs.state = CS_DEAD; 2309 gs.lastdeath = gamemillis; 2310 } 2311 calcdamage(int weap,int & flags,int radial,float size,float dist)2312 int calcdamage(int weap, int &flags, int radial, float size, float dist) 2313 { 2314 int damage = weaptype[weap].damage[flags&HIT_ALT ? 1 : 0]; 2315 if(radial) damage = int(damage*(1.f-dist/EXPLOSIONSCALE/max(size, 1e-3f))); 2316 if(!hithurts(flags)) flags = HIT_WAVE|(flags&HIT_ALT ? HIT_ALT : 0); // so it impacts, but not hurts 2317 else if((flags&HIT_FULL) && !weaptype[weap].explode[flags&HIT_ALT ? 1 : 0]) flags &= ~HIT_FULL; 2318 if(hithurts(flags)) 2319 { 2320 if(flags&HIT_FULL || flags&HIT_HEAD) damage = int(damage*GVAR(damagescale)); 2321 else if(flags&HIT_TORSO) damage = int(damage*0.5f*GVAR(damagescale)); 2322 else if(flags&HIT_LEGS) damage = int(damage*0.25f*GVAR(damagescale)); 2323 else damage = 0; 2324 } 2325 else damage = int(damage*GVAR(damagescale)); 2326 return damage; 2327 } 2328 process(clientinfo * ci)2329 void destroyevent::process(clientinfo *ci) 2330 { 2331 servstate &gs = ci->state; 2332 if(isweap(weap)) 2333 { 2334 if(gs.weapshots[weap][flags&HIT_ALT ? 1 : 0].find(id) < 0) return; 2335 if(hits.empty()) gs.weapshots[weap][flags&HIT_ALT ? 1 : 0].remove(id); 2336 else 2337 { 2338 loopv(hits) 2339 { 2340 hitset &h = hits[i]; 2341 int hflags = flags|h.flags; 2342 float size = radial ? (hflags&HIT_WAVE ? radial*GVAR(wavepusharea) : radial) : 0.f, dist = float(h.dist)/DMF; 2343 clientinfo *target = (clientinfo *)getinfo(h.target); 2344 if(!target || target->state.state != CS_ALIVE || (size && (dist<0 || dist>size)) || target->state.protect(gamemillis, m_protect(gamemode, mutators))) 2345 continue; 2346 int damage = calcdamage(weap, hflags, radial, size, dist); 2347 if(damage > 0 && (hithurts(hflags) || hflags&HIT_WAVE)) dodamage(target, ci, damage, weap, hflags, h.dir); 2348 } 2349 } 2350 } 2351 else if(weap == -1) 2352 { 2353 gs.dropped.remove(id); 2354 if(sents.inrange(id) && !finditem(id, false)) sents[id].millis = gamemillis; 2355 } 2356 } 2357 takeammo(clientinfo * ci,int weap,int amt=1)2358 void takeammo(clientinfo *ci, int weap, int amt = 1) 2359 { 2360 if(weap != WEAP_MELEE) ci->state.ammo[weap] = max(ci->state.ammo[weap]-amt, 0); 2361 } 2362 process(clientinfo * ci)2363 void shotevent::process(clientinfo *ci) 2364 { 2365 servstate &gs = ci->state; 2366 if(!gs.isalive(gamemillis) || !isweap(weap)) 2367 { 2368 if(GVAR(serverdebug) >= 3) srvmsgf(ci->clientnum, "sync error: shoot [%d] failed - unexpected message", weap); 2369 return; 2370 } 2371 if(!gs.canshoot(weap, flags, m_weapon(gamemode, mutators), millis)) 2372 { 2373 if(!gs.canshoot(weap, flags, m_weapon(gamemode, mutators), millis, (1<<WEAP_S_RELOAD))) 2374 { 2375 if(weaptype[weap].sub[flags&HIT_ALT ? 1 : 0] && weaptype[weap].max) 2376 ci->state.ammo[weap] = max(ci->state.ammo[weap]-weaptype[weap].sub[flags&HIT_ALT ? 1 : 0], 0); 2377 if(GVAR(serverdebug)) srvmsgf(ci->clientnum, "sync error: shoot [%d] failed - current state disallows it", weap); 2378 return; 2379 } 2380 else if(gs.weapload[weap] > 0) 2381 { 2382 takeammo(ci, weap, gs.weapload[weap]+weaptype[weap].sub[flags&HIT_ALT ? 1 : 0]); 2383 gs.weapload[weap] = -gs.weapload[weap]; 2384 sendf(-1, 1, "ri5", SV_RELOAD, ci->clientnum, weap, gs.weapload[weap], gs.ammo[weap]); 2385 } 2386 else return; 2387 } 2388 else takeammo(ci, weap, weaptype[weap].sub[flags&HIT_ALT ? 1 : 0]); 2389 gs.setweapstate(weap, WEAP_S_SHOOT, weaptype[weap].adelay[flags&HIT_ALT ? 1 : 0], millis); 2390 sendf(-1, 1, "ri8ivx", SV_SHOTFX, ci->clientnum, weap, flags, power, from[0], from[1], from[2], shots.length(), shots.length()*sizeof(ivec)/sizeof(int), shots.getbuf(), ci->clientnum); 2391 gs.weapshot[weap] = weaptype[weap].sub[flags&HIT_ALT ? 1 : 0]; 2392 gs.shotdamage += weaptype[weap].damage[flags&HIT_ALT ? 1 : 0]*shots.length(); 2393 loopv(shots) gs.weapshots[weap][flags&HIT_ALT ? 1 : 0].add(id); 2394 } 2395 process(clientinfo * ci)2396 void switchevent::process(clientinfo *ci) 2397 { 2398 servstate &gs = ci->state; 2399 if(!gs.isalive(gamemillis) || !isweap(weap)) 2400 { 2401 if(GVAR(serverdebug) >= 3) srvmsgf(ci->clientnum, "sync error: switch [%d] failed - unexpected message", weap); 2402 sendf(ci->clientnum, 1, "ri3", SV_WEAPSELECT, ci->clientnum, gs.weapselect); 2403 return; 2404 } 2405 if(!gs.canswitch(weap, m_weapon(gamemode, mutators), millis, (1<<WEAP_S_SWITCH))) 2406 { 2407 if(!gs.canswitch(weap, m_weapon(gamemode, mutators), millis, (1<<WEAP_S_RELOAD))) 2408 { 2409 if(GVAR(serverdebug)) srvmsgf(ci->clientnum, "sync error: switch [%d] failed - current state disallows it", weap); 2410 sendf(ci->clientnum, 1, "ri3", SV_WEAPSELECT, ci->clientnum, gs.weapselect); 2411 return; 2412 } 2413 else if(gs.weapload[gs.weapselect] > 0) 2414 { 2415 takeammo(ci, gs.weapselect, gs.weapload[gs.weapselect]); 2416 gs.weapload[gs.weapselect] = -gs.weapload[gs.weapselect]; 2417 sendf(-1, 1, "ri5", SV_RELOAD, ci->clientnum, gs.weapselect, gs.weapload[gs.weapselect], gs.ammo[gs.weapselect]); 2418 } 2419 else return; 2420 } 2421 gs.weapswitch(weap, millis); 2422 sendf(-1, 1, "ri3x", SV_WEAPSELECT, ci->clientnum, weap, ci->clientnum); 2423 } 2424 process(clientinfo * ci)2425 void dropevent::process(clientinfo *ci) 2426 { 2427 servstate &gs = ci->state; 2428 if(!gs.isalive(gamemillis) || !isweap(weap)) 2429 { 2430 if(GVAR(serverdebug) >= 3) srvmsgf(ci->clientnum, "sync error: drop [%d] failed - unexpected message", weap); 2431 return; 2432 } 2433 int sweap = m_weapon(gamemode, mutators); 2434 if(!gs.hasweap(weap, sweap, weap == WEAP_GRENADE ? 2 : 0) || (weap != WEAP_GRENADE && m_noitems(gamemode, mutators))) 2435 { 2436 if(GVAR(serverdebug)) srvmsgf(ci->clientnum, "sync error: drop [%d] failed - current state disallows it", weap); 2437 return; 2438 } 2439 if(weap == WEAP_GRENADE) 2440 { 2441 int nweap = -1; // try to keep this weapon 2442 gs.entid[weap] = -1; 2443 gs.weapshots[WEAP_GRENADE][0].add(-1); 2444 takeammo(ci, WEAP_GRENADE, 1); 2445 if(!gs.hasweap(weap, sweap)) 2446 { 2447 nweap = gs.bestweap(sweap, true); 2448 gs.weapswitch(nweap, millis); 2449 } 2450 else gs.setweapstate(weap, WEAP_S_SHOOT, weaptype[weap].adelay[0], millis); 2451 sendf(-1, 1, "ri6", SV_DROP, ci->clientnum, nweap, 1, weap, -1); 2452 return; 2453 } 2454 else if(!sents.inrange(gs.entid[weap]) || (sents[gs.entid[weap]].attrs[1]&WEAP_F_FORCED)) 2455 { 2456 if(GVAR(serverdebug)) srvmsgf(ci->clientnum, "sync error: drop [%d] failed - not droppable entity", weap); 2457 return; 2458 } 2459 int dropped = gs.entid[weap]; 2460 gs.ammo[weap] = gs.entid[weap] = -1; 2461 int nweap = gs.bestweap(sweap, true); // switch to best weapon 2462 if(w_carry(weap, sweap)) sents[dropped].millis = gamemillis+GVAR(itemspawntime); 2463 gs.dropped.add(dropped); 2464 gs.weapswitch(nweap, millis); 2465 sendf(-1, 1, "ri6", SV_DROP, ci->clientnum, nweap, 1, weap, dropped); 2466 } 2467 process(clientinfo * ci)2468 void reloadevent::process(clientinfo *ci) 2469 { 2470 servstate &gs = ci->state; 2471 if(!gs.isalive(gamemillis) || !isweap(weap)) 2472 { 2473 if(GVAR(serverdebug) >= 3) srvmsgf(ci->clientnum, "sync error: reload [%d] failed - unexpected message", weap); 2474 sendf(ci->clientnum, 1, "ri5", SV_RELOAD, ci->clientnum, weap, gs.weapload[weap], gs.ammo[weap]); 2475 return; 2476 } 2477 if(!gs.canreload(weap, m_weapon(gamemode, mutators), millis)) 2478 { 2479 if(GVAR(serverdebug)) srvmsgf(ci->clientnum, "sync error: reload [%d] failed - current state disallows it", weap); 2480 sendf(ci->clientnum, 1, "ri5", SV_RELOAD, ci->clientnum, weap, gs.weapload[weap], gs.ammo[weap]); 2481 return; 2482 } 2483 gs.setweapstate(weap, WEAP_S_RELOAD, weaptype[weap].rdelay, millis); 2484 int oldammo = gs.ammo[weap]; 2485 gs.ammo[weap] = min(max(gs.ammo[weap], 0) + weaptype[weap].add, weaptype[weap].max); 2486 gs.weapload[weap] = gs.ammo[weap]-oldammo; 2487 sendf(-1, 1, "ri5x", SV_RELOAD, ci->clientnum, weap, gs.weapload[weap], gs.ammo[weap], ci->clientnum); 2488 } 2489 process(clientinfo * ci)2490 void useevent::process(clientinfo *ci) 2491 { 2492 servstate &gs = ci->state; 2493 if(gs.state != CS_ALIVE || !sents.inrange(ent) || enttype[sents[ent].type].usetype != EU_ITEM || m_noitems(gamemode, mutators)) 2494 { 2495 if(GVAR(serverdebug) >= 3) srvmsgf(ci->clientnum, "sync error: use [%d] failed - unexpected message", ent); 2496 return; 2497 } 2498 int sweap = m_weapon(gamemode, mutators), attr = sents[ent].type == WEAPON ? w_attr(gamemode, sents[ent].attrs[0], sweap) : sents[ent].attrs[0]; 2499 if(!gs.canuse(sents[ent].type, attr, sents[ent].attrs, sweap, millis, (1<<WEAP_S_SWITCH))) 2500 { 2501 if(!gs.canuse(sents[ent].type, attr, sents[ent].attrs, sweap, millis, (1<<WEAP_S_RELOAD))) 2502 { 2503 if(GVAR(serverdebug)) srvmsgf(ci->clientnum, "sync error: use [%d] failed - current state disallows it", ent); 2504 return; 2505 } 2506 else if(gs.weapload[gs.weapselect] > 0) 2507 { 2508 takeammo(ci, gs.weapselect, gs.weapload[gs.weapselect]); 2509 gs.weapload[gs.weapselect] = -gs.weapload[gs.weapselect]; 2510 sendf(-1, 1, "ri5", SV_RELOAD, ci->clientnum, gs.weapselect, gs.weapload[gs.weapselect], gs.ammo[gs.weapselect]); 2511 } 2512 else return; 2513 } 2514 if(!sents[ent].spawned && !(sents[ent].attrs[1]&WEAP_F_FORCED)) 2515 { 2516 bool found = false; 2517 loopv(clients) 2518 { 2519 clientinfo *cp = clients[i]; 2520 if(cp->state.dropped.projs.find(ent) >= 0) 2521 { 2522 cp->state.dropped.remove(ent); 2523 found = true; 2524 } 2525 } 2526 if(!found) 2527 { 2528 if(GVAR(serverdebug)) srvmsgf(ci->clientnum, "sync error: use [%d] failed - doesn't seem to be spawned anywhere", ent); 2529 return; 2530 } 2531 } 2532 2533 int weap = -1, dropped = -1; 2534 if(sents[ent].type == WEAPON && gs.ammo[attr] < 0 && w_carry(attr, sweap) && gs.carry(sweap) >= GVAR(maxcarry)) 2535 weap = gs.drop(sweap, attr); 2536 if(weap != WEAP_MELEE && isweap(weap)) 2537 { 2538 dropped = gs.entid[weap]; 2539 gs.setweapstate(weap, WEAP_S_SWITCH, WEAPSWITCHDELAY, millis); 2540 gs.ammo[weap] = gs.entid[weap] = -1; 2541 } 2542 gs.useitem(ent, sents[ent].type, attr, sents[ent].attrs, sweap, millis); 2543 if(sents.inrange(dropped)) 2544 { 2545 gs.dropped.add(dropped); 2546 if(!(sents[dropped].attrs[1]&WEAP_F_FORCED)) 2547 { 2548 sents[dropped].spawned = false; 2549 sents[dropped].millis = gamemillis+GVAR(itemspawntime); 2550 } 2551 } 2552 if(!(sents[ent].attrs[1]&WEAP_F_FORCED)) 2553 { 2554 sents[ent].spawned = false; 2555 sents[ent].millis = gamemillis+GVAR(itemspawntime); 2556 } 2557 sendf(-1, 1, "ri6", SV_ITEMACC, ci->clientnum, ent, sents[ent].spawned ? 1 : 0, weap, dropped); 2558 } 2559 flush(clientinfo * ci,int fmillis)2560 bool gameevent::flush(clientinfo *ci, int fmillis) 2561 { 2562 process(ci); 2563 return true; 2564 } 2565 flush(clientinfo * ci,int fmillis)2566 bool timedevent::flush(clientinfo *ci, int fmillis) 2567 { 2568 if(millis > fmillis) return false; 2569 else if(millis >= ci->lastevent) 2570 { 2571 ci->lastevent = millis; 2572 process(ci); 2573 } 2574 return true; 2575 } 2576 flushevents(clientinfo * ci,int millis)2577 void flushevents(clientinfo *ci, int millis) 2578 { 2579 while(ci->events.length()) 2580 { 2581 gameevent *ev = ci->events[0]; 2582 if(ev->flush(ci, millis)) clearevent(ci); 2583 else break; 2584 } 2585 } 2586 processevents()2587 void processevents() 2588 { 2589 loopv(clients) 2590 { 2591 clientinfo *ci = clients[i]; 2592 flushevents(ci, gamemillis); 2593 } 2594 } 2595 cleartimedevents(clientinfo * ci)2596 void cleartimedevents(clientinfo *ci) 2597 { 2598 int keep = 0; 2599 loopv(ci->events) 2600 { 2601 if(ci->events[i]->keepable()) 2602 { 2603 if(keep < i) 2604 { 2605 for(int j = keep; j < i; j++) delete ci->events[j]; 2606 ci->events.remove(keep, i - keep); 2607 i = keep; 2608 } 2609 keep = i+1; 2610 continue; 2611 } 2612 } 2613 while(ci->events.length() > keep) delete ci->events.pop(); 2614 } 2615 waiting(clientinfo * ci,int doteam,int drop,bool exclude)2616 void waiting(clientinfo *ci, int doteam, int drop, bool exclude) 2617 { 2618 if(ci->state.state != CS_SPECTATOR && ci->state.state != CS_EDITING) 2619 { 2620 if(ci->state.state == CS_ALIVE) 2621 { 2622 dropitems(ci, drop); 2623 if(smode) smode->died(ci); 2624 mutate(smuts, mut->died(ci)); 2625 ci->state.lastdeath = gamemillis; 2626 } 2627 if(exclude) sendf(-1, 1, "ri2x", SV_WAITING, ci->clientnum, ci->clientnum); 2628 else sendf(-1, 1, "ri2", SV_WAITING, ci->clientnum); 2629 ci->state.state = CS_WAITING; 2630 ci->state.weapreset(false); 2631 if(m_arena(gamemode, mutators) && ci->state.arenaweap < 0 && ci->state.aitype < 0) sendf(ci->clientnum, 1, "ri", SV_ARENAWEAP); 2632 if(doteam && (doteam == 2 || !isteam(gamemode, mutators, ci->team, TEAM_FIRST))) 2633 setteam(ci, chooseteam(ci, ci->team), false, true); 2634 } 2635 } 2636 hashpassword(int cn,int sessionid,const char * pwd,char * result,int maxlen)2637 void hashpassword(int cn, int sessionid, const char *pwd, char *result, int maxlen) 2638 { 2639 char buf[2*sizeof(string)]; 2640 formatstring(buf)("%d %d ", cn, sessionid); 2641 copystring(&buf[strlen(buf)], pwd); 2642 if(!hashstring(buf, result, maxlen)) *result = '\0'; 2643 } 2644 checkpassword(clientinfo * ci,const char * wanted,const char * given)2645 bool checkpassword(clientinfo *ci, const char *wanted, const char *given) 2646 { 2647 string hash; 2648 hashpassword(ci->clientnum, ci->sessionid, wanted, hash, sizeof(string)); 2649 return !strcmp(hash, given); 2650 } 2651 2652 #include "auth.h" 2653 triggertime(int i)2654 int triggertime(int i) 2655 { 2656 if(sents.inrange(i)) switch(sents[i].type) 2657 { 2658 case TRIGGER: case MAPMODEL: case PARTICLES: case MAPSOUND: case TELEPORT: case PUSHER: return 1000; break; 2659 default: break; 2660 } 2661 return 0; 2662 } 2663 checkents()2664 void checkents() 2665 { 2666 loopv(sents) switch(sents[i].type) 2667 { 2668 case TRIGGER: 2669 { 2670 if(sents[i].attrs[1] == TR_LINK && sents[i].spawned && gamemillis >= sents[i].millis && (sents[i].attrs[4] == triggerid || !sents[i].attrs[4])) 2671 { 2672 sents[i].spawned = false; 2673 sents[i].millis = gamemillis+(triggertime(i)*2); 2674 sendf(-1, 1, "ri3", SV_TRIGGER, i, 0); 2675 loopvj(sents[i].kin) if(sents.inrange(sents[i].kin[j])) 2676 { 2677 sents[sents[i].kin[j]].spawned = sents[i].spawned; 2678 sents[sents[i].kin[j]].millis = sents[i].millis; 2679 } 2680 } 2681 break; 2682 } 2683 default: 2684 { 2685 if(enttype[sents[i].type].usetype == EU_ITEM && hasitem(i) && !finditem(i, true, true)) 2686 { 2687 loopvk(clients) 2688 { 2689 clientinfo *ci = clients[k]; 2690 ci->state.dropped.remove(i); 2691 loopj(WEAP_MAX) if(ci->state.entid[j] == i) 2692 ci->state.entid[j] = -1; 2693 } 2694 sents[i].spawned = true; 2695 sents[i].millis = gamemillis+GVAR(itemspawntime); 2696 sendf(-1, 1, "ri2", SV_ITEMSPAWN, i); 2697 } 2698 break; 2699 } 2700 } 2701 } 2702 checkclients()2703 void checkclients() 2704 { 2705 loopv(clients) if(clients[i]->name[0] && clients[i]->online) 2706 { 2707 clientinfo *ci = clients[i]; 2708 if(ci->state.state == CS_ALIVE) 2709 { 2710 if(GVAR(fireburntime) && ci->state.lastfire) 2711 { 2712 if(gamemillis-ci->state.lastfire <= GVAR(fireburntime)) 2713 { 2714 if(gamemillis-ci->state.lastfireburn >= GVAR(fireburndelay)) 2715 { 2716 clientinfo *co = (clientinfo *)getinfo(ci->state.lastfireowner); 2717 dodamage(ci, co ? co : ci, GVAR(fireburndamage), -1, HIT_BURN); 2718 ci->state.lastfireburn += GVAR(fireburndelay); 2719 } 2720 continue; 2721 } 2722 else ci->state.lastfire = ci->state.lastfireburn = 0; 2723 } 2724 if(!m_regen(gamemode, mutators) || ci->state.aitype >= AI_START) continue; 2725 int total = m_health(gamemode, mutators), amt = GVAR(regenhealth), delay = ci->state.lastregen ? GVAR(regentime) : GVAR(regendelay); 2726 if(smode) smode->regen(ci, total, amt, delay); 2727 if(delay && (ci->state.health < total || ci->state.health > total) && gamemillis-(ci->state.lastregen ? ci->state.lastregen : ci->state.lastpain) >= delay) 2728 { 2729 int low = 0; 2730 if(ci->state.health > total) { amt = -GVAR(regenhealth); total = ci->state.health; low = m_health(gamemode, mutators); } 2731 int rgn = ci->state.health, heal = clamp(ci->state.health+amt, low, total), eff = heal-rgn; 2732 if(eff) 2733 { 2734 ci->state.health = heal; ci->state.lastregen = gamemillis; 2735 sendf(-1, 1, "ri4", SV_REGEN, ci->clientnum, ci->state.health, eff); 2736 } 2737 } 2738 } 2739 else if(ci->state.state == CS_WAITING) 2740 { 2741 if(m_arena(gamemode, mutators) && ci->state.arenaweap < 0 && ci->state.aitype < 0) continue; 2742 if(m_trial(gamemode) && ci->state.cpmillis < 0) continue; 2743 int delay = m_delay(gamemode, mutators); 2744 if(ci->state.aitype >= AI_START) 2745 { 2746 if(ci->state.lastdeath) 2747 { 2748 if(m_story(gamemode)) continue; 2749 else if(!m_duke(gamemode, mutators)) delay = 30000; 2750 } 2751 } 2752 if(delay && ci->state.respawnwait(gamemillis, delay)) continue; 2753 int nospawn = 0; 2754 if(smode && !smode->canspawn(ci, false)) { nospawn++; } 2755 mutate(smuts, if (!mut->canspawn(ci, false)) { nospawn++; }); 2756 if(!nospawn) 2757 { 2758 if(ci->state.lastdeath) flushevents(ci, ci->state.lastdeath + DEATHMILLIS); 2759 cleartimedevents(ci); 2760 ci->state.state = CS_DEAD; // safety 2761 ci->state.respawn(gamemillis, m_health(gamemode, mutators)); 2762 sendspawn(ci); 2763 } 2764 } 2765 } 2766 } 2767 cleanbans()2768 void cleanbans() 2769 { 2770 while(bannedips.length() && bannedips[0].time-totalmillis>4*60*60000) 2771 bannedips.remove(0); 2772 } 2773 serverupdate()2774 void serverupdate() 2775 { 2776 if(numclients()) 2777 { 2778 if(!paused) gamemillis += curtime; 2779 if(m_demo(gamemode)) readdemo(); 2780 else if(!paused && !interm) 2781 { 2782 processevents(); 2783 checkents(); 2784 checklimits(); 2785 checkclients(); 2786 if(smode) smode->update(); 2787 mutate(smuts, mut->update()); 2788 } 2789 cleanbans(); 2790 loopv(connects) if(totalmillis-connects[i]->connectmillis > 15000) disconnect_client(connects[i]->clientnum, DISC_TIMEOUT); 2791 2792 if(masterupdate) 2793 { 2794 loopv(clients) sendf(-1, 1, "ri3", SV_CURRENTMASTER, clients[i]->clientnum, clients[i]->privilege); 2795 masterupdate = false; 2796 } 2797 2798 if(interm && gamemillis >= interm) // wait then call for next map 2799 { 2800 if(GVAR(votelimit) && !maprequest) 2801 { 2802 if(demorecord) enddemorecord(); 2803 sendf(-1, 1, "ri", SV_NEWGAME); 2804 maprequest = true; 2805 interm = gamemillis+GVAR(votelimit); 2806 } 2807 else 2808 { 2809 interm = 0; 2810 checkvotes(true); 2811 } 2812 } 2813 if(shouldcheckvotes) checkvotes(); 2814 } 2815 else if(!GVAR(resetbansonend)) cleanbans(); 2816 aiman::checkai(); 2817 auth::update(); 2818 } 2819 allowbroadcast(int n)2820 bool allowbroadcast(int n) 2821 { 2822 clientinfo *ci = (clientinfo *)getinfo(n); 2823 return ci && ci->connected && ci->state.aitype < 0; 2824 } 2825 peerowner(int n)2826 int peerowner(int n) 2827 { 2828 clientinfo *ci = (clientinfo *)getinfo(n); 2829 if(ci) return ci->state.aitype >= 0 ? ci->state.ownernum : ci->clientnum; 2830 return -1; 2831 } 2832 reserveclients()2833 int reserveclients() { return 3; } 2834 allowconnect(clientinfo * ci,const char * pwd)2835 int allowconnect(clientinfo *ci, const char *pwd) 2836 { 2837 if(ci->local) return DISC_NONE; 2838 if(m_demo(gamemode)) return DISC_PRIVATE; 2839 if(serverpass[0]) 2840 { 2841 if(!checkpassword(ci, serverpass, pwd)) return DISC_PRIVATE; 2842 return DISC_NONE; 2843 } 2844 if(adminpass[0] && checkpassword(ci, adminpass, pwd)) return DISC_NONE; 2845 if(numclients() >= serverclients) return DISC_MAXCLIENTS; 2846 uint ip = getclientip(ci->clientnum); 2847 loopv(bannedips) if(bannedips[i].ip == ip) return DISC_IPBAN; 2848 if(mastermode >= MM_PRIVATE && allowedips.find(ip) < 0) return DISC_PRIVATE; 2849 return DISC_NONE; 2850 } 2851 clientconnect(int n,uint ip,bool local)2852 int clientconnect(int n, uint ip, bool local) 2853 { 2854 clientinfo *ci = (clientinfo *)getinfo(n); 2855 ci->clientnum = n; 2856 ci->connectmillis = totalmillis; 2857 ci->sessionid = (rnd(0x1000000)*((totalmillis%10000)+1))&0xFFFFFF; 2858 ci->local = local; 2859 connects.add(ci); 2860 if(!local) 2861 { 2862 if(m_demo(gamemode) || servertype <= 0) return DISC_PRIVATE; 2863 #ifndef STANDALONE 2864 bool haslocal = false; 2865 loopv(clients) if(clients[i]->local) { haslocal = true; break; } 2866 if(!haslocal) return DISC_PRIVATE; 2867 #endif 2868 } 2869 sendservinit(ci); 2870 return DISC_NONE; 2871 } 2872 clientdisconnect(int n,bool local)2873 void clientdisconnect(int n, bool local) 2874 { 2875 clientinfo *ci = (clientinfo *)getinfo(n); 2876 bool complete = !numclients(n, false, -1); 2877 if(ci->connected) 2878 { 2879 loopv(clients) if(clients[i] != ci) 2880 { 2881 loopvk(clients[i]->state.fraglog) if(clients[i]->state.fraglog[k] == ci->clientnum) 2882 clients[i]->state.fraglog.remove(k--); 2883 } 2884 if(ci->state.state == CS_ALIVE) dropitems(ci, 0); 2885 if(ci->privilege) auth::setmaster(ci, false); 2886 if(smode) smode->leavegame(ci, true); 2887 mutate(smuts, mut->leavegame(ci, true)); 2888 ci->state.timeplayed += lastmillis-ci->state.lasttimeplayed; 2889 savescore(ci); 2890 sendf(-1, 1, "ri2", SV_DISCONNECT, n); 2891 ci->connected = false; 2892 if(ci->name[0]) relayf(2, "\fo%s has left the game", colorname(ci)); 2893 aiman::removeai(ci, complete); 2894 if(!complete) aiman::dorefresh = true; 2895 clients.removeobj(ci); 2896 } 2897 else connects.removeobj(ci); 2898 if(complete) cleanup(); 2899 else shouldcheckvotes = true; 2900 } 2901 2902 #include "extinfo.h" queryreply(ucharbuf & req,ucharbuf & p)2903 void queryreply(ucharbuf &req, ucharbuf &p) 2904 { 2905 if(!getint(req)) 2906 { 2907 extqueryreply(req, p); 2908 return; 2909 } 2910 putint(p, numclients()); 2911 putint(p, 6); // number of attrs following 2912 putint(p, GAMEVERSION); // 1 2913 putint(p, gamemode); // 2 2914 putint(p, mutators); // 3 2915 putint(p, minremain); // 4 2916 putint(p, serverclients); // 5 2917 putint(p, serverpass[0] ? MM_PASSWORD : (m_demo(gamemode) ? MM_PRIVATE : mastermode)); // 6 2918 sendstring(smapname, p); 2919 if(*serverdesc) sendstring(serverdesc, p); 2920 else 2921 { 2922 #ifdef STANDALONE 2923 sendstring("unnamed", p); 2924 #else 2925 const char *cname = client::getname(); 2926 if(!cname || !cname[0]) cname = "unnamed"; 2927 sendstring(cname, p); 2928 #endif 2929 } 2930 sendqueryreply(p); 2931 } 2932 receivefile(int sender,uchar * data,int len)2933 bool receivefile(int sender, uchar *data, int len) 2934 { 2935 clientinfo *ci = (clientinfo *)getinfo(sender); 2936 ucharbuf p(data, len); 2937 int type = getint(p), n = 0; 2938 data += p.length(); 2939 len -= p.length(); 2940 switch(type) 2941 { 2942 case SV_SENDMAPFILE: case SV_SENDMAPSHOT: case SV_SENDMAPCONFIG: 2943 n = type-SV_SENDMAPFILE; 2944 break; 2945 default: srvmsgf(sender, "bad map file type %d"); return false; 2946 } 2947 if(mapdata[n]) 2948 { 2949 if(ci != choosebestclient()) 2950 { 2951 srvmsgf(sender, "sorry, the map isn't needed from you"); 2952 return false; 2953 } 2954 DELETEP(mapdata[n]); 2955 } 2956 if(!len) 2957 { 2958 srvmsgf(sender, "you sent a zero length packet for map data"); 2959 return false; 2960 } 2961 mapdata[n] = opentempfile(((const char *[3]){ "mapdata", "mapshot", "mapconf" })[n], "w+b"); 2962 if(!mapdata[n]) 2963 { 2964 srvmsgf(sender, "failed to open temporary file for map"); 2965 return false; 2966 } 2967 mapsending = true; 2968 mapdata[n]->write(data, len); 2969 return n == 2; 2970 } 2971 checktype(int type,clientinfo * ci)2972 int checktype(int type, clientinfo *ci) 2973 { 2974 // only allow edit messages in coop-edit mode 2975 if(type >= SV_EDITENT && type <= SV_NEWMAP && !m_edit(gamemode)) return -1; 2976 // server only messages 2977 static int servtypes[] = { SV_SERVERINIT, SV_CLIENTINIT, SV_WELCOME, SV_NEWGAME, SV_MAPCHANGE, SV_SERVMSG, SV_DAMAGE, SV_SHOTFX, SV_DIED, SV_POINTS, SV_SPAWNSTATE, SV_ITEMACC, SV_ITEMSPAWN, SV_TIMEUP, SV_DISCONNECT, SV_CURRENTMASTER, SV_PONG, SV_RESUME, SV_SCORE, SV_FLAGINFO, SV_ANNOUNCE, SV_SENDDEMOLIST, SV_SENDDEMO, SV_DEMOPLAYBACK, SV_REGEN, SV_SCOREFLAG, SV_RETURNFLAG, SV_CLIENT, SV_AUTHCHAL }; 2978 if(ci) loopi(sizeof(servtypes)/sizeof(int)) if(type == servtypes[i]) return -1; 2979 return type; 2980 } 2981 freecallback(ENetPacket * packet)2982 static void freecallback(ENetPacket *packet) 2983 { 2984 extern void cleanworldstate(ENetPacket *packet); 2985 cleanworldstate(packet); 2986 } 2987 cleanworldstate(ENetPacket * packet)2988 void cleanworldstate(ENetPacket *packet) 2989 { 2990 loopv(worldstates) 2991 { 2992 worldstate *ws = worldstates[i]; 2993 if(ws->positions.inbuf(packet->data) || ws->messages.inbuf(packet->data)) ws->uses--; 2994 else continue; 2995 if(!ws->uses) 2996 { 2997 delete ws; 2998 worldstates.remove(i); 2999 } 3000 break; 3001 } 3002 } 3003 buildworldstate()3004 bool buildworldstate() 3005 { 3006 worldstate &ws = *new worldstate; 3007 loopv(clients) 3008 { 3009 clientinfo &ci = *clients[i]; 3010 if(ci.position.empty()) ci.posoff = -1; 3011 else 3012 { 3013 ci.posoff = ws.positions.length(); 3014 loopvj(ci.position) ws.positions.add(ci.position[j]); 3015 } 3016 if(ci.messages.empty()) ci.msgoff = -1; 3017 else 3018 { 3019 ci.msgoff = ws.messages.length(); 3020 ucharbuf p = ws.messages.reserve(16); 3021 putint(p, SV_CLIENT); 3022 putint(p, ci.clientnum); 3023 putuint(p, ci.messages.length()); 3024 ws.messages.addbuf(p); 3025 loopvj(ci.messages) ws.messages.add(ci.messages[j]); 3026 ci.msglen = ws.messages.length() - ci.msgoff; 3027 } 3028 } 3029 int psize = ws.positions.length(), msize = ws.messages.length(); 3030 if(psize) recordpacket(0, ws.positions.getbuf(), psize); 3031 if(msize) recordpacket(1, ws.messages.getbuf(), msize); 3032 loopi(psize) { uchar c = ws.positions[i]; ws.positions.add(c); } 3033 loopi(msize) { uchar c = ws.messages[i]; ws.messages.add(c); } 3034 ws.uses = 0; 3035 loopv(clients) 3036 { 3037 clientinfo &ci = *clients[i]; 3038 ENetPacket *packet; 3039 if(ci.state.aitype < 0 && psize && (ci.posoff<0 || psize-ci.position.length()>0)) 3040 { 3041 packet = enet_packet_create(&ws.positions[ci.posoff<0 ? 0 : ci.posoff+ci.position.length()], 3042 ci.posoff<0 ? psize : psize-ci.position.length(), 3043 ENET_PACKET_FLAG_NO_ALLOCATE); 3044 sendpacket(ci.clientnum, 0, packet); 3045 if(!packet->referenceCount) enet_packet_destroy(packet); 3046 else { ++ws.uses; packet->freeCallback = freecallback; } 3047 } 3048 ci.position.setsizenodelete(0); 3049 3050 if(ci.state.aitype < 0 && msize && (ci.msgoff<0 || msize-ci.msglen>0)) 3051 { 3052 packet = enet_packet_create(&ws.messages[ci.msgoff<0 ? 0 : ci.msgoff+ci.msglen], 3053 ci.msgoff<0 ? msize : msize-ci.msglen, 3054 (reliablemessages ? ENET_PACKET_FLAG_RELIABLE : 0) | ENET_PACKET_FLAG_NO_ALLOCATE); 3055 sendpacket(ci.clientnum, 1, packet); 3056 if(!packet->referenceCount) enet_packet_destroy(packet); 3057 else { ++ws.uses; packet->freeCallback = freecallback; } 3058 } 3059 ci.messages.setsizenodelete(0); 3060 } 3061 reliablemessages = false; 3062 if(!ws.uses) 3063 { 3064 delete &ws; 3065 return false; 3066 } 3067 else 3068 { 3069 worldstates.add(&ws); 3070 return true; 3071 } 3072 } 3073 sendpackets()3074 bool sendpackets() 3075 { 3076 if(clients.empty()) return false; 3077 enet_uint32 millis = enet_time_get()-lastsend; 3078 if(millis<33) return false; 3079 bool flush = buildworldstate(); 3080 lastsend += millis - (millis%33); 3081 return flush; 3082 } 3083 parsepacket(int sender,int chan,packetbuf & p)3084 void parsepacket(int sender, int chan, packetbuf &p) // has to parse exactly each byte of the packet 3085 { 3086 if(sender<0) return; 3087 char text[MAXTRANS]; 3088 int type = -1, prevtype = -1; 3089 clientinfo *ci = sender>=0 ? (clientinfo *)getinfo(sender) : NULL; 3090 if(ci && !ci->connected) 3091 { 3092 if(chan==0) return; 3093 else if(chan!=1 || getint(p)!=SV_CONNECT) { disconnect_client(sender, DISC_TAGT); return; } 3094 else 3095 { 3096 getstring(text, p); 3097 //filtertext(text, text, true, MAXNAMELEN); 3098 if(!text[0]) copystring(text, "unnamed"); 3099 filtertext(text, text, true, MAXNAMELEN); 3100 copystring(ci->name, text, MAXNAMELEN+1); 3101 3102 getstring(text, p); 3103 int disc = allowconnect(ci, text); 3104 if(disc) 3105 { 3106 disconnect_client(sender, disc); 3107 return; 3108 } 3109 3110 connects.removeobj(ci); 3111 clients.add(ci); 3112 3113 ci->connected = true; 3114 masterupdate = true; 3115 ci->state.lasttimeplayed = lastmillis; 3116 3117 sendwelcome(ci); 3118 if(restorescore(ci)) sendresume(ci); 3119 sendinitclient(ci); 3120 relayf(2, "\fg%s has joined the game", colorname(ci)); 3121 } 3122 } 3123 else if(chan==2) 3124 { 3125 if(receivefile(sender, p.buf, p.maxlen)) 3126 { 3127 mapsending = false; 3128 sendf(-1, 1, "ri", SV_SENDMAP); 3129 } 3130 return; 3131 } 3132 if(p.packet->flags&ENET_PACKET_FLAG_RELIABLE) reliablemessages = true; 3133 #define QUEUE_MSG { while(curmsg<p.length()) ci->messages.add(p.buf[curmsg++]); } 3134 #define QUEUE_BUF(body) { curmsg = p.length(); body; } 3135 #define QUEUE_INT(n) QUEUE_BUF(putint(ci->messages, n)) 3136 #define QUEUE_UINT(n) QUEUE_BUF(putuint(ci->messages, n)) 3137 #define QUEUE_FLT(n) QUEUE_BUF(putfloat(ci->messages, n)) 3138 #define QUEUE_STR(text) QUEUE_BUF(sendstring(text, ci->messages)) 3139 3140 int curmsg; 3141 while((curmsg = p.length()) < p.maxlen) 3142 { 3143 int curtype = getint(p); 3144 prevtype = type; 3145 switch(type = checktype(curtype, ci)) 3146 { 3147 case SV_POS: 3148 { 3149 int lcn = getint(p); 3150 if(lcn<0) 3151 { 3152 disconnect_client(sender, DISC_CN); 3153 return; 3154 } 3155 3156 bool havecn = true; 3157 clientinfo *cp = (clientinfo *)getinfo(lcn); 3158 if(!cp || (cp->clientnum != sender && cp->state.ownernum != sender)) 3159 havecn = false; 3160 3161 vec oldpos, pos; 3162 loopi(3) pos[i] = getuint(p)/DMF; 3163 if(havecn) 3164 { 3165 oldpos = cp->state.o; 3166 cp->state.o = pos; 3167 } 3168 getuint(p); 3169 loopi(5) getint(p); 3170 int physstate = getuint(p); 3171 if(physstate&0x20) loopi(2) getint(p); 3172 if(physstate&0x10) getint(p); 3173 int flags = getuint(p); 3174 if(flags&0x20) { getuint(p); getint(p); } 3175 if(havecn && (cp->state.state==CS_ALIVE || cp->state.state==CS_EDITING)) 3176 { 3177 cp->position.setsizenodelete(0); 3178 while(curmsg<p.length()) cp->position.add(p.buf[curmsg++]); 3179 } 3180 if(havecn && cp->state.state==CS_ALIVE) 3181 { 3182 if(smode) smode->moved(cp, oldpos, cp->state.o); 3183 mutate(smuts, mut->moved(cp, oldpos, cp->state.o)); 3184 } 3185 break; 3186 } 3187 3188 case SV_PHYS: 3189 { 3190 int lcn = getint(p), idx = getint(p); 3191 clientinfo *cp = (clientinfo *)getinfo(lcn); 3192 if(!cp || (cp->clientnum!=ci->clientnum && cp->state.ownernum!=ci->clientnum)) break; 3193 if(idx == SPHY_EXTINGUISH) 3194 { 3195 if(!cp->state.lastfire || gamemillis-cp->state.lastfire > GVAR(fireburntime)) 3196 break; 3197 cp->state.lastfire = cp->state.lastfireburn = 0; 3198 } 3199 QUEUE_MSG; 3200 break; 3201 } 3202 3203 case SV_EDITMODE: 3204 { 3205 int val = getint(p); 3206 if((!val && ci->state.state != CS_EDITING) || !m_edit(gamemode) || ci->state.aitype >= 0) break; 3207 //if(val && ci->state.state != CS_ALIVE) break; 3208 ci->state.dropped.reset(); 3209 loopk(WEAP_MAX) loopj(2) ci->state.weapshots[k][j].reset(); 3210 ci->state.editspawn(gamemillis, m_weapon(gamemode, mutators), m_health(gamemode, mutators), m_arena(gamemode, mutators), GVAR(spawngrenades) >= (m_insta(gamemode, mutators) ? 2 : 1)); 3211 if(val) 3212 { 3213 if(smode) smode->leavegame(ci); 3214 mutate(smuts, mut->leavegame(ci)); 3215 ci->state.state = CS_EDITING; 3216 ci->events.deletecontentsp(); 3217 } 3218 else 3219 { 3220 ci->state.state = CS_ALIVE; 3221 if(smode) smode->entergame(ci); 3222 mutate(smuts, mut->entergame(ci)); 3223 } 3224 QUEUE_MSG; 3225 break; 3226 } 3227 3228 case SV_MAPCRC: 3229 { 3230 getstring(text, p); 3231 int crc = getint(p); 3232 if(!ci) break; 3233 if(strcmp(text, smapname)) 3234 { 3235 if(ci->clientmap[0]) 3236 { 3237 ci->clientmap[0] = '\0'; 3238 ci->mapcrc = 0; 3239 } 3240 else if(ci->mapcrc > 0) ci->mapcrc = 0; 3241 break; 3242 } 3243 copystring(ci->clientmap, text); 3244 ci->mapcrc = text[0] ? crc : 1; 3245 checkmaps(); 3246 break; 3247 } 3248 3249 case SV_CHECKMAPS: 3250 checkmaps(sender); 3251 break; 3252 3253 case SV_TRYSPAWN: 3254 { 3255 int lcn = getint(p); 3256 clientinfo *cp = (clientinfo *)getinfo(lcn); 3257 if(!cp || (cp->clientnum!=ci->clientnum && cp->state.ownernum!=ci->clientnum)) break; 3258 if(cp->state.state != CS_DEAD || cp->state.lastrespawn >= 0 || gamemillis-cp->state.lastdeath <= DEATHMILLIS) break; 3259 if(!ci->clientmap[0] && !ci->mapcrc) 3260 { 3261 ci->mapcrc = -1; 3262 checkmaps(); 3263 } 3264 if(smode) smode->canspawn(cp, true); 3265 mutate(smuts, mut->canspawn(cp, true)); 3266 cp->state.state = CS_DEAD; 3267 waiting(cp, 1, 1); 3268 break; 3269 } 3270 3271 case SV_ARENAWEAP: 3272 { 3273 int lcn = getint(p), aweap = getint(p); 3274 clientinfo *cp = (clientinfo *)getinfo(lcn); 3275 if(!cp || (cp->clientnum!=ci->clientnum && cp->state.ownernum!=ci->clientnum)) break; 3276 cp->state.arenaweap = aweap; 3277 break; 3278 } 3279 3280 case SV_WEAPSELECT: 3281 { 3282 int lcn = getint(p), id = getint(p), weap = getint(p); 3283 clientinfo *cp = (clientinfo *)getinfo(lcn); 3284 if(!cp || (cp->clientnum!=ci->clientnum && cp->state.ownernum!=ci->clientnum)) break; 3285 switchevent *ev = new switchevent; 3286 ev->id = id; 3287 ev->weap = weap; 3288 ev->millis = cp->getmillis(gamemillis, ev->id); 3289 cp->addevent(ev); 3290 break; 3291 } 3292 3293 case SV_SPAWN: 3294 { 3295 int lcn = getint(p); 3296 clientinfo *cp = (clientinfo *)getinfo(lcn); 3297 if(!cp || (cp->clientnum!=ci->clientnum && cp->state.ownernum!=ci->clientnum)) break; 3298 if((cp->state.state!=CS_ALIVE && cp->state.state!=CS_DEAD && cp->state.state!=CS_WAITING) || cp->state.lastrespawn < 0) 3299 break; 3300 cp->state.lastrespawn = -1; 3301 cp->state.state = CS_ALIVE; 3302 if(smode) smode->spawned(cp); 3303 mutate(smuts, mut->spawned(cp);); 3304 QUEUE_BUF({ 3305 putint(ci->messages, SV_SPAWN); 3306 putint(ci->messages, cp->clientnum); 3307 sendstate(cp->state, ci->messages); 3308 }); 3309 break; 3310 } 3311 3312 case SV_SUICIDE: 3313 { 3314 int lcn = getint(p), flags = getint(p); 3315 clientinfo *cp = (clientinfo *)getinfo(lcn); 3316 if(!cp || (cp->clientnum!=ci->clientnum && cp->state.ownernum!=ci->clientnum)) break; 3317 suicideevent *ev = new suicideevent; 3318 ev->flags = flags; 3319 cp->addevent(ev); 3320 break; 3321 } 3322 3323 case SV_SHOOT: 3324 { 3325 int lcn = getint(p); 3326 clientinfo *cp = (clientinfo *)getinfo(lcn); 3327 bool havecn = (cp && (cp->clientnum == ci->clientnum || cp->state.ownernum == ci->clientnum)); 3328 shotevent *ev = new shotevent; 3329 ev->id = getint(p); 3330 ev->weap = getint(p); 3331 ev->flags = getint(p); 3332 ev->power = getint(p); 3333 if(havecn) ev->millis = cp->getmillis(gamemillis, ev->id); 3334 loopk(3) ev->from[k] = getint(p); 3335 ev->num = getint(p); 3336 loopj(ev->num) 3337 { 3338 if(p.overread() || !isweap(ev->weap)) break; 3339 ivec &dest = ev->shots.add(); 3340 loopk(3) dest[k] = getint(p); 3341 } 3342 if(havecn) cp->addevent(ev); 3343 else delete ev; 3344 break; 3345 } 3346 3347 case SV_DROP: 3348 { // gee this looks familiar 3349 int lcn = getint(p), id = getint(p), weap = getint(p); 3350 clientinfo *cp = (clientinfo *)getinfo(lcn); 3351 if(!cp || (cp->clientnum != ci->clientnum && cp->state.ownernum != ci->clientnum)) 3352 break; 3353 dropevent *ev = new dropevent; 3354 ev->id = id; 3355 ev->weap = weap; 3356 ev->millis = cp->getmillis(gamemillis, ev->id); 3357 cp->events.add(ev); 3358 break; 3359 } 3360 3361 case SV_RELOAD: 3362 { 3363 int lcn = getint(p), id = getint(p), weap = getint(p); 3364 clientinfo *cp = (clientinfo *)getinfo(lcn); 3365 if(!cp || (cp->clientnum != ci->clientnum && cp->state.ownernum != ci->clientnum)) 3366 break; 3367 reloadevent *ev = new reloadevent; 3368 ev->id = id; 3369 ev->weap = weap; 3370 ev->millis = cp->getmillis(gamemillis, ev->id); 3371 cp->events.add(ev); 3372 break; 3373 } 3374 3375 case SV_DESTROY: // cn millis weap id radial hits 3376 { 3377 int lcn = getint(p); 3378 clientinfo *cp = (clientinfo *)getinfo(lcn); 3379 bool havecn = (cp && (cp->clientnum == ci->clientnum || cp->state.ownernum == ci->clientnum)); 3380 destroyevent *ev = new destroyevent; 3381 ev->id = getint(p); 3382 if(havecn) ev->millis = cp->getmillis(gamemillis, ev->id); // this is the event millis 3383 ev->weap = getint(p); 3384 ev->flags = getint(p); 3385 ev->id = getint(p); // this is the actual id 3386 ev->radial = getint(p); 3387 int hits = getint(p); 3388 loopj(hits) 3389 { 3390 if(p.overread()) break; 3391 if(!havecn) 3392 { 3393 loopi(7) getint(p); 3394 continue; 3395 } 3396 hitset &hit = ev->hits.add(); 3397 hit.flags = getint(p); 3398 hit.target = getint(p); 3399 hit.id = getint(p); 3400 hit.dist = getint(p); 3401 loopk(3) hit.dir[k] = getint(p); 3402 } 3403 if(havecn) cp->events.add(ev); 3404 else delete ev; 3405 break; 3406 } 3407 3408 case SV_ITEMUSE: 3409 { 3410 int lcn = getint(p), id = getint(p), ent = getint(p); 3411 clientinfo *cp = (clientinfo *)getinfo(lcn); 3412 if(!cp || (cp->clientnum!=ci->clientnum && cp->state.ownernum!=ci->clientnum)) break; 3413 useevent *ev = new useevent; 3414 ev->id = id; 3415 ev->ent = ent; 3416 ev->millis = cp->getmillis(gamemillis, ev->id); 3417 cp->events.add(ev); 3418 break; 3419 } 3420 3421 case SV_TRIGGER: 3422 { 3423 int lcn = getint(p), ent = getint(p); 3424 clientinfo *cp = (clientinfo *)getinfo(lcn); 3425 if(!cp || (cp->clientnum!=ci->clientnum && cp->state.ownernum!=ci->clientnum)) break; 3426 if(sents.inrange(ent)) 3427 { 3428 if(sents[ent].type == CHECKPOINT) 3429 { 3430 if(cp->state.cpnodes.find(ent) < 0 && (sents[ent].attrs[4] == triggerid || !sents[ent].attrs[4]) && m_check(sents[ent].attrs[3], gamemode)) 3431 { 3432 if(m_trial(gamemode)) switch(sents[ent].attrs[5]) 3433 { 3434 case CP_LAST: case CP_FINISH: 3435 { 3436 int laptime = gamemillis-cp->state.cpmillis; 3437 if(cp->state.cptime <= 0 || laptime < cp->state.cptime) 3438 { 3439 cp->state.cptime = laptime; 3440 if(sents[ent].attrs[5] == CP_FINISH) { cp->state.cpmillis = -gamemillis; waiting(cp, 0, 0); } 3441 } 3442 sendf(-1, 1, "ri4", SV_CHECKPOINT, cp->clientnum, laptime, cp->state.cptime); 3443 } 3444 case CP_RESPAWN: if(sents[ent].attrs[5] == CP_RESPAWN && cp->state.cpmillis) break; 3445 case CP_START: 3446 { 3447 cp->state.cpmillis = gamemillis; 3448 cp->state.cpnodes.setsize(0); 3449 } 3450 default: break; 3451 } 3452 cp->state.cpnodes.add(ent); 3453 } 3454 } 3455 else if(sents[ent].type == TRIGGER) 3456 { 3457 bool commit = false, kin = false; 3458 if(sents[ent].attrs[4] == triggerid || !sents[ent].attrs[4]) switch(sents[ent].attrs[1]) 3459 { 3460 case TR_TOGGLE: 3461 { 3462 if(!sents[ent].spawned || sents[ent].attrs[2] != TA_AUTO) 3463 { 3464 sents[ent].millis = gamemillis+(triggertime(ent)*2); 3465 sents[ent].spawned = !sents[ent].spawned; 3466 commit = kin = true; 3467 } 3468 //else sendf(cp->clientnum, 1, "ri3", SV_TRIGGER, ent, sents[ent].spawned ? 1 : 0); 3469 break; 3470 } 3471 case TR_ONCE: if(sents[ent].spawned) break; 3472 case TR_LINK: 3473 { 3474 sents[ent].millis = gamemillis+(triggertime(ent)*2); kin = true; 3475 if(!sents[ent].spawned) 3476 { 3477 sents[ent].spawned = true; 3478 commit = true; 3479 } 3480 //else sendf(cp->clientnum, 1, "ri3", SV_TRIGGER, ent, sents[ent].spawned ? 1 : 0); 3481 break; 3482 } 3483 case TR_EXIT: 3484 { 3485 if(sents[ent].spawned) break; 3486 if(m_story(gamemode) || m_lobby(gamemode)) 3487 { 3488 sents[ent].spawned = true; 3489 startintermission(); 3490 } 3491 } 3492 } 3493 if(commit) sendf(-1, 1, "ri3", SV_TRIGGER, ent, sents[ent].spawned ? 1 : 0); 3494 if(kin) loopvj(sents[ent].kin) if(sents.inrange(sents[ent].kin[j])) 3495 { 3496 sents[sents[ent].kin[j]].spawned = sents[ent].spawned; 3497 sents[sents[ent].kin[j]].millis = sents[ent].millis; 3498 } 3499 } 3500 } 3501 else if(GVAR(serverdebug)) srvmsgf(cp->clientnum, "sync error: cannot trigger %d - not a trigger", ent); 3502 break; 3503 } 3504 3505 case SV_TEXT: 3506 { 3507 int lcn = getint(p), flags = getint(p); 3508 getstring(text, p); 3509 clientinfo *cp = (clientinfo *)getinfo(lcn); 3510 if(!cp || (cp->clientnum!=ci->clientnum && cp->state.ownernum!=ci->clientnum)) break; 3511 loopv(clients) 3512 { 3513 clientinfo *t = clients[i]; 3514 if(t == cp || !allowbroadcast(t->clientnum) || (flags&SAY_TEAM && cp->team != t->team)) continue; 3515 sendf(t->clientnum, 1, "ri3s", SV_TEXT, cp->clientnum, flags, text); 3516 } 3517 if(flags&SAY_ACTION) relayf(0, "\fm* \fs%s\fS \fs\fm%s\fS", colorname(cp), text); 3518 else relayf(0, "\fa<\fs\fw%s\fS> \fs\fw%s\fS", colorname(cp), text); 3519 break; 3520 } 3521 3522 case SV_COMMAND: 3523 { 3524 int lcn = getint(p), nargs = getint(p); 3525 clientinfo *cp = (clientinfo *)getinfo(lcn); 3526 string cmd; 3527 getstring(cmd, p); 3528 getstring(text, p); 3529 if(!cp || (cp->clientnum!=ci->clientnum && cp->state.ownernum!=ci->clientnum)) break; 3530 parsecommand(cp, nargs, cmd, text); 3531 break; 3532 } 3533 3534 case SV_SWITCHNAME: 3535 { 3536 QUEUE_MSG; 3537 getstring(text, p); 3538 if(!text[0]) copystring(text, "unnamed"); 3539 filtertext(text, text, true, MAXNAMELEN); 3540 copystring(ci->name, text, MAXNAMELEN+1); 3541 QUEUE_STR(ci->name); 3542 break; 3543 } 3544 3545 case SV_SWITCHTEAM: 3546 { 3547 int team = getint(p); 3548 if(((ci->state.state == CS_SPECTATOR || ci->state.state == CS_EDITING) && team != TEAM_NEUTRAL) || !isteam(gamemode, mutators, team, TEAM_FIRST) || ci->state.aitype >= AI_START) 3549 team = chooseteam(ci, team); 3550 if(ci->team != team) 3551 { 3552 setteam(ci, team); 3553 sendf(-1, 1, "ri3", SV_SETTEAM, sender, team); 3554 } 3555 break; 3556 } 3557 3558 case SV_MAPVOTE: 3559 { 3560 getstring(text, p); 3561 filtertext(text, text); 3562 int reqmode = getint(p), reqmuts = getint(p); 3563 if(vote(text, reqmode, reqmuts, sender)) 3564 { 3565 sendf(-1, 1, "ri2si2", SV_MAPVOTE, sender, text, reqmode, reqmuts); 3566 relayf(3, "\fc%s suggests: \fs\fw%s on map %s\fS", colorname(ci), gamename(reqmode, reqmuts), text); 3567 } 3568 break; 3569 } 3570 3571 case SV_GAMEINFO: 3572 { 3573 bool valid = !hasgameinfo && !strcmp(ci->clientmap, smapname); 3574 int n, np = getint(p); 3575 while((n = getint(p)) != -1) 3576 { 3577 int type = getint(p), numattr = getint(p), numkin = getint(p); 3578 if(valid && (enttype[type].usetype == EU_ITEM || type == PLAYERSTART || type == CHECKPOINT || type == ACTOR || type == TRIGGER)) 3579 { 3580 while(sents.length() <= n) sents.add(); 3581 sents[n].reset(); 3582 sents[n].type = type; 3583 sents[n].spawned = false; // wait a bit then load 'em up 3584 sents[n].millis = gamemillis; 3585 loopk(numattr) sents[n].attrs.add(getint(p)); 3586 if(numattr < 5) loopk(5-numattr) sents[n].attrs.add(0); 3587 loopk(numkin) sents[n].kin.add(getint(p)); 3588 } 3589 else 3590 { 3591 loopk(numattr) getint(p); 3592 loopk(numkin) getint(p); 3593 } 3594 } 3595 if(valid) setupgameinfo(np); 3596 break; 3597 } 3598 3599 case SV_SCORE: 3600 getint(p); 3601 getint(p); 3602 QUEUE_MSG; 3603 break; 3604 3605 case SV_FLAGINFO: 3606 getint(p); 3607 getint(p); 3608 getint(p); 3609 getint(p); 3610 QUEUE_MSG; 3611 break; 3612 3613 case SV_FLAGS: 3614 if(smode==&stfmode) stfmode.parseflags(p); 3615 break; 3616 3617 case SV_TAKEFLAG: 3618 { 3619 int lcn = getint(p), flag = getint(p); 3620 clientinfo *cp = (clientinfo *)getinfo(lcn); 3621 if(!cp || (cp->clientnum!=ci->clientnum && cp->state.ownernum!=ci->clientnum) || cp->state.state==CS_SPECTATOR) break; 3622 if(smode==&ctfmode) ctfmode.takeflag(cp, flag); 3623 break; 3624 } 3625 3626 case SV_RESETFLAG: 3627 { 3628 int flag = getint(p); 3629 if(!ci) break; 3630 if(smode==&ctfmode) ctfmode.resetflag(ci, flag); 3631 break; 3632 } 3633 3634 case SV_DROPFLAG: 3635 { 3636 int lcn = getint(p); 3637 vec droploc; 3638 loopk(3) droploc[k] = getint(p)/DMF; 3639 clientinfo *cp = (clientinfo *)getinfo(lcn); 3640 if(!cp || (cp->clientnum!=ci->clientnum && cp->state.ownernum!=ci->clientnum) || cp->state.state==CS_SPECTATOR) break; 3641 if(smode==&ctfmode) ctfmode.dropflag(cp, droploc); 3642 break; 3643 } 3644 3645 case SV_INITFLAGS: 3646 { 3647 if(smode==&ctfmode) ctfmode.parseflags(p); 3648 break; 3649 } 3650 3651 case SV_PING: 3652 sendf(sender, 1, "i2", SV_PONG, getint(p)); 3653 break; 3654 3655 case SV_CLIENTPING: 3656 { 3657 int ping = getint(p); 3658 if(ci) 3659 { 3660 ci->ping = ping; 3661 loopv(clients) if(clients[i]->state.ownernum == ci->clientnum) clients[i]->ping = ping; 3662 } 3663 QUEUE_MSG; 3664 break; 3665 } 3666 3667 case SV_MASTERMODE: 3668 { 3669 int mm = getint(p); 3670 if(haspriv(ci, PRIV_MASTER, "change mastermode") && mm >= MM_OPEN && mm <= MM_PRIVATE) 3671 { 3672 if(haspriv(ci, PRIV_ADMIN) || (mastermask&(1<<mm))) 3673 { 3674 mastermode = mm; 3675 allowedips.setsize(0); 3676 if(mm >= MM_PRIVATE) 3677 { 3678 loopv(clients) allowedips.add(getclientip(clients[i]->clientnum)); 3679 } 3680 srvoutf(3, "mastermode is now %d (%s)", mastermode, mastermodename(mm)); 3681 } 3682 else srvmsgf(sender, "mastermode %d (%s) is disabled on this server", mm, mastermodename(mm)); 3683 } 3684 break; 3685 } 3686 3687 case SV_CLEARBANS: 3688 { 3689 if(haspriv(ci, PRIV_MASTER, "clear bans")) 3690 { 3691 bannedips.setsize(0); 3692 srvoutf(3, "cleared all bans"); 3693 } 3694 break; 3695 } 3696 3697 case SV_KICK: 3698 { 3699 int victim = getint(p); 3700 if(haspriv(ci, PRIV_MASTER, "kick people") && victim>=0 && victim<getnumclients() && ci->clientnum!=victim && getinfo(victim)) 3701 { 3702 ban &b = bannedips.add(); 3703 b.time = totalmillis; 3704 b.ip = getclientip(victim); 3705 allowedips.removeobj(b.ip); 3706 disconnect_client(victim, DISC_KICK); 3707 } 3708 break; 3709 } 3710 3711 case SV_SPECTATOR: 3712 { 3713 int spectator = getint(p), val = getint(p); 3714 if(((mastermode >= MM_LOCKED && ci->state.state == CS_SPECTATOR) || spectator != sender) && !haspriv(ci, PRIV_MASTER, spectator != sender ? "spectate others" : "unspectate")) break; 3715 clientinfo *cp = (clientinfo *)getinfo(spectator); 3716 if(!cp || cp->state.aitype >= 0) break; 3717 if(cp->state.state != CS_SPECTATOR && val) 3718 { 3719 sendf(-1, 1, "ri3", SV_SPECTATOR, spectator, val); 3720 if(cp->state.state == CS_ALIVE) dropitems(cp, 1); 3721 if(smode) smode->leavegame(cp); 3722 mutate(smuts, mut->leavegame(cp)); 3723 cp->state.cpnodes.setsize(0); 3724 cp->state.cpmillis = 0; 3725 cp->state.state = CS_SPECTATOR; 3726 cp->state.timeplayed += lastmillis-cp->state.lasttimeplayed; 3727 setteam(cp, TEAM_NEUTRAL, false, true); 3728 aiman::dorefresh = true; 3729 } 3730 else if(cp->state.state == CS_SPECTATOR && !val) 3731 { 3732 cp->state.cpnodes.setsize(0); 3733 cp->state.cpmillis = 0; 3734 cp->state.state = CS_DEAD; 3735 cp->state.lasttimeplayed = lastmillis; 3736 waiting(cp, 2, 1); 3737 if(smode) smode->entergame(cp); 3738 mutate(smuts, mut->entergame(cp)); 3739 aiman::dorefresh = true; 3740 if(cp->clientmap[0] || cp->mapcrc) checkmaps(); 3741 } 3742 break; 3743 } 3744 3745 case SV_SETTEAM: 3746 { 3747 int who = getint(p), team = getint(p); 3748 if(who<0 || who>=getnumclients() || !haspriv(ci, PRIV_MASTER, "change the team of others")) break; 3749 clientinfo *cp = (clientinfo *)getinfo(who); 3750 if(!cp || !m_team(gamemode, mutators) || !m_fight(gamemode) || cp->state.aitype >= AI_START) break; 3751 if(cp->state.state == CS_SPECTATOR || cp->state.state == CS_EDITING || !isteam(gamemode, mutators, team, TEAM_FIRST)) break; 3752 setteam(cp, team, true, true); 3753 break; 3754 } 3755 3756 case SV_RECORDDEMO: 3757 { 3758 int val = getint(p); 3759 if(!haspriv(ci, PRIV_ADMIN, "record demos")) break; 3760 demonextmatch = val!=0; 3761 srvoutf(4, "demo recording is %s for next match", demonextmatch ? "enabled" : "disabled"); 3762 break; 3763 } 3764 3765 case SV_STOPDEMO: 3766 { 3767 if(!haspriv(ci, PRIV_ADMIN, "stop demos")) break; 3768 if(m_demo(gamemode)) enddemoplayback(); 3769 else enddemorecord(); 3770 break; 3771 } 3772 3773 case SV_CLEARDEMOS: 3774 { 3775 int demo = getint(p); 3776 if(!haspriv(ci, PRIV_ADMIN, "clear demos")) break; 3777 cleardemos(demo); 3778 break; 3779 } 3780 3781 case SV_LISTDEMOS: 3782 if(ci->state.state==CS_SPECTATOR) break; 3783 listdemos(sender); 3784 break; 3785 3786 case SV_GETDEMO: 3787 { 3788 int n = getint(p); 3789 if(ci->state.state==CS_SPECTATOR) break; 3790 senddemo(sender, n); 3791 break; 3792 } 3793 3794 case SV_EDITENT: 3795 { 3796 int n = getint(p), oldtype = NOTUSED; 3797 bool tweaked = false; 3798 loopk(3) getint(p); 3799 if(sents.inrange(n)) oldtype = sents[n].type; 3800 else while(sents.length() <= n) sents.add(); 3801 if((sents[n].type = getint(p)) != oldtype) tweaked = true; 3802 int numattrs = getint(p); 3803 while(sents[n].attrs.length() < max(5, numattrs)) sents[n].attrs.add(0); 3804 loopk(numattrs) sents[n].attrs[k] = getint(p); 3805 QUEUE_MSG; 3806 if(tweaked) 3807 { 3808 sents[n].spawned = false; 3809 sents[n].millis = gamemillis; 3810 if(enttype[sents[n].type].usetype == EU_ITEM) 3811 { 3812 loopvk(clients) 3813 { 3814 clientinfo *cq = clients[k]; 3815 cq->state.dropped.remove(n); 3816 loopj(WEAP_MAX) if(cq->state.entid[j] == n) cq->state.entid[j] = -1; 3817 } 3818 sents[n].millis += GVAR(itemspawndelay)*3; 3819 } 3820 else if(sents[n].type == TRIGGER) setuptriggers(true); 3821 } 3822 break; 3823 } 3824 3825 case SV_EDITVAR: 3826 { 3827 QUEUE_INT(SV_EDITVAR); 3828 int t = getint(p); 3829 QUEUE_INT(t); 3830 getstring(text, p); 3831 QUEUE_STR(text); 3832 switch(t) 3833 { 3834 case ID_VAR: 3835 { 3836 int val = getint(p); 3837 relayf(3, "\fg%s set worldvar %s to %d", colorname(ci), text, val); 3838 QUEUE_INT(val); 3839 break; 3840 } 3841 case ID_FVAR: 3842 { 3843 float val = getfloat(p); 3844 relayf(3, "\fg%s set worldvar %s to %s", colorname(ci), text, floatstr(val)); 3845 QUEUE_FLT(val); 3846 break; 3847 } 3848 case ID_SVAR: 3849 case ID_ALIAS: 3850 { 3851 string val; 3852 getstring(val, p); 3853 relayf(3, "\fg%s set world%s %s to %s", colorname(ci), t == ID_ALIAS ? "alias" : "var", text, val); 3854 QUEUE_STR(val); 3855 break; 3856 } 3857 default: break; 3858 } 3859 break; 3860 } 3861 3862 case SV_GETMAP: 3863 { 3864 ci->wantsmap = true; 3865 if(!mapsending && mapdata[0]) 3866 { 3867 loopk(3) if(mapdata[k]) 3868 sendfile(sender, 2, mapdata[k], "ri", SV_SENDMAPFILE+k); 3869 sendwelcome(ci); 3870 } 3871 else 3872 { 3873 if(!mapsending) 3874 { 3875 clientinfo *best = choosebestclient(); 3876 if(best) 3877 { 3878 loopk(3) if(mapdata[k]) DELETEP(mapdata[k]); 3879 mapsending = false; 3880 sendf(best->clientnum, 1, "ri", SV_GETMAP); 3881 } 3882 } 3883 srvmsgf(ci->clientnum, "map is being uploaded, please wait.."); 3884 } 3885 break; 3886 } 3887 3888 case SV_NEWMAP: 3889 { 3890 int size = getint(p); 3891 if(ci->state.state==CS_SPECTATOR) break; 3892 if(size>=0) 3893 { 3894 smapname[0] = '\0'; 3895 sents.setsize(0); 3896 hasgameinfo = true; 3897 if(smode) smode->reset(true); 3898 mutate(smuts, mut->reset(true)); 3899 } 3900 QUEUE_MSG; 3901 break; 3902 } 3903 3904 case SV_SETMASTER: 3905 { 3906 int val = getint(p); 3907 getstring(text, p); 3908 auth::setmaster(ci, val!=0, text); 3909 // don't broadcast the master password 3910 break; 3911 } 3912 3913 case SV_ADDBOT: 3914 { 3915 aiman::reqadd(ci, getint(p)); 3916 break; 3917 } 3918 3919 case SV_DELBOT: 3920 { 3921 aiman::reqdel(ci); 3922 break; 3923 } 3924 3925 case SV_AUTHTRY: 3926 { 3927 getstring(text, p); 3928 auth::tryauth(ci, text); 3929 break; 3930 } 3931 3932 case SV_AUTHANS: 3933 { 3934 uint id = (uint)getint(p); 3935 getstring(text, p); 3936 auth::answerchallenge(ci, id, text); 3937 break; 3938 } 3939 3940 default: 3941 { 3942 int size = msgsizelookup(type); 3943 if(size==-1) 3944 { 3945 conoutf("\fy[tag error] from: %d, cur: %d, msg: %d, prev: %d", sender, curtype, type, prevtype); 3946 disconnect_client(sender, DISC_TAGT); 3947 return; 3948 } 3949 if(size>0) loopi(size-1) getint(p); 3950 if(ci) QUEUE_MSG; 3951 break; 3952 } 3953 } 3954 if(verbose > 5) conoutf("\fy[server] from: %d, cur: %d, msg: %d, prev: %d", sender, curtype, type, prevtype); 3955 } 3956 } 3957 serveroption(char * arg)3958 bool serveroption(char *arg) 3959 { 3960 if(arg[0]=='-' && arg[1]=='s') switch(arg[2]) 3961 { 3962 case 'd': setsvar("serverdesc", &arg[3]); return true; 3963 case 'P': setsvar("adminpass", &arg[3]); return true; 3964 case 'k': setsvar("serverpass", &arg[3]); return true; 3965 case 'o': setvar("serveropen", atoi(&arg[2])); return true; 3966 case 'M': setsvar("servermotd", &arg[3]); return true; 3967 default: break; 3968 } 3969 return false; 3970 } 3971 }; 3972 #undef GAMESERVER 3973