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