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