Initial Comit: First commit.
[SauerEngine.git] / src / fpsgame / fpsserver.h
blob5e7e6ac1b352af39c957b98623101bab0a34ffe2
1 #ifdef WIN32
2 #include <io.h>
3 #else
4 #include <unistd.h>
5 #define _dup dup
6 #define _fileno fileno
7 #endif
9 struct fpsserver : igameserver
11 struct server_entity // server side version of "entity" type
13 int type;
14 int spawntime;
15 char spawned;
18 static const int DEATHMILLIS = 300;
20 enum { GE_NONE = 0, GE_SHOT, GE_EXPLODE, GE_HIT, GE_SUICIDE, GE_PICKUP };
22 struct shotevent
24 int type;
25 int millis, id;
26 int gun;
27 float from[3], to[3];
30 struct explodeevent
32 int type;
33 int millis, id;
34 int gun;
37 struct hitevent
39 int type;
40 int target;
41 int lifesequence;
42 union
44 int rays;
45 float dist;
47 float dir[3];
50 struct suicideevent
52 int type;
55 struct pickupevent
57 int type;
58 int ent;
61 union gameevent
63 int type;
64 shotevent shot;
65 explodeevent explode;
66 hitevent hit;
67 suicideevent suicide;
68 pickupevent pickup;
71 template <int N>
72 struct projectilestate
74 int projs[N];
75 int numprojs;
77 projectilestate() : numprojs(0) {}
79 void reset() { numprojs = 0; }
81 void add(int val)
83 if(numprojs>=N) numprojs = 0;
84 projs[numprojs++] = val;
87 bool remove(int val)
89 loopi(numprojs) if(projs[i]==val)
91 projs[i] = projs[--numprojs];
92 return true;
94 return false;
98 struct gamestate : fpsstate
100 vec o;
101 int state, editstate;
102 int lastdeath, lastspawn, lifesequence;
103 int lastshot;
104 projectilestate<8> rockets, grenades;
105 int frags, deaths, teamkills, shotdamage, damage;
106 int lasttimeplayed, timeplayed;
107 float effectiveness;
109 gamestate() : state(CS_DEAD), editstate(CS_DEAD) {}
111 bool isalive(int gamemillis)
113 return state==CS_ALIVE || (state==CS_DEAD && gamemillis - lastdeath <= DEATHMILLIS);
116 bool waitexpired(int gamemillis)
118 return gamemillis - lastshot >= gunwait;
121 void reset()
123 if(state!=CS_SPECTATOR) state = editstate = CS_DEAD;
124 lifesequence = 0;
125 maxhealth = 100;
126 rockets.reset();
127 grenades.reset();
129 timeplayed = 0;
130 effectiveness = 0;
131 frags = deaths = teamkills = shotdamage = damage = 0;
133 respawn();
136 void respawn()
138 fpsstate::respawn();
139 o = vec(-1e10f, -1e10f, -1e10f);
140 lastdeath = 0;
141 lastspawn = -1;
142 lastshot = 0;
146 struct savedscore
148 uint ip;
149 string name;
150 int maxhealth, frags, deaths, teamkills, shotdamage, damage;
151 int timeplayed;
152 float effectiveness;
154 void save(gamestate &gs)
156 maxhealth = gs.maxhealth;
157 frags = gs.frags;
158 deaths = gs.deaths;
159 teamkills = gs.teamkills;
160 shotdamage = gs.shotdamage;
161 damage = gs.damage;
162 timeplayed = gs.timeplayed;
163 effectiveness = gs.effectiveness;
166 void restore(gamestate &gs)
168 if(gs.health==gs.maxhealth) gs.health = maxhealth;
169 gs.maxhealth = maxhealth;
170 gs.frags = frags;
171 gs.deaths = deaths;
172 gs.teamkills = teamkills;
173 gs.shotdamage = shotdamage;
174 gs.damage = damage;
175 gs.timeplayed = timeplayed;
176 gs.effectiveness = effectiveness;
180 struct clientinfo
182 int clientnum;
183 string name, team, mapvote;
184 int modevote;
185 int privilege;
186 bool spectator, local, timesync, wantsmaster;
187 int gameoffset, lastevent;
188 gamestate state;
189 vector<gameevent> events;
190 vector<uchar> position, messages;
191 vector<clientinfo *> targets;
193 clientinfo() { reset(); }
195 gameevent &addevent()
197 static gameevent dummy;
198 if(state.state==CS_SPECTATOR || events.length()>100) return dummy;
199 return events.add();
202 void mapchange()
204 mapvote[0] = 0;
205 state.reset();
206 events.setsizenodelete(0);
207 targets.setsizenodelete(0);
208 timesync = false;
209 lastevent = 0;
212 void reset()
214 name[0] = team[0] = 0;
215 privilege = PRIV_NONE;
216 spectator = local = wantsmaster = false;
217 position.setsizenodelete(0);
218 messages.setsizenodelete(0);
219 mapchange();
223 struct worldstate
225 int uses;
226 vector<uchar> positions, messages;
229 struct ban
231 int time;
232 uint ip;
235 #define MM_MODE 0xF
236 #define MM_AUTOAPPROVE 0x1000
237 #define MM_DEFAULT (MM_MODE | MM_AUTOAPPROVE)
239 enum { MM_OPEN = 0, MM_VETO, MM_LOCKED, MM_PRIVATE };
241 bool notgotitems, notgotbases; // true when map has changed and waiting for clients to send item
242 int gamemode;
243 int gamemillis, gamelimit;
245 string serverdesc;
246 string smapname;
247 int lastmillis, totalmillis, curtime;
248 int interm, minremain;
249 bool mapreload;
250 enet_uint32 lastsend;
251 int mastermode, mastermask;
252 int currentmaster;
253 bool masterupdate;
254 string masterpass;
255 FILE *mapdata;
257 vector<uint> allowedips;
258 vector<ban> bannedips;
259 vector<clientinfo *> clients;
260 vector<worldstate *> worldstates;
261 bool reliablemessages;
263 struct demofile
265 string info;
266 uchar *data;
267 int len;
270 #define MAXDEMOS 5
271 vector<demofile> demos;
273 bool demonextmatch;
274 FILE *demotmp;
275 gzFile demorecord, demoplayback;
276 int nextplayback;
278 struct servmode
280 fpsserver &sv;
282 servmode(fpsserver &sv) : sv(sv) {}
283 virtual ~servmode() {}
285 virtual void entergame(clientinfo *ci) {}
286 virtual void leavegame(clientinfo *ci, bool disconnecting = false) {}
288 virtual void moved(clientinfo *ci, const vec &oldpos, const vec &newpos) {}
289 virtual bool canspawn(clientinfo *ci, bool connecting = false) { return true; }
290 virtual void spawned(clientinfo *ci) {}
291 virtual int fragvalue(clientinfo *victim, clientinfo *actor)
293 int gamemode = sv.gamemode;
294 if(victim==actor || isteam(victim->team, actor->team)) return -1;
295 return 1;
297 virtual void died(clientinfo *victim, clientinfo *actor) {}
298 virtual bool canchangeteam(clientinfo *ci, const char *oldteam, const char *newteam) { return true; }
299 virtual void changeteam(clientinfo *ci, const char *oldteam, const char *newteam) {}
300 virtual void initclient(clientinfo *ci, ucharbuf &p, bool connecting) {}
301 virtual void update() {}
302 virtual void reset(bool empty) {}
303 virtual void intermission() {}
306 struct arenaservmode : servmode
308 int arenaround;
310 arenaservmode(fpsserver &sv) : servmode(sv), arenaround(0) {}
312 bool canspawn(clientinfo *ci, bool connecting = false)
314 if(connecting && sv.nonspectators(ci->clientnum)<=1) return true;
315 return false;
318 void reset(bool empty)
320 arenaround = 0;
323 void update()
325 if(sv.interm || sv.gamemillis<arenaround || !sv.nonspectators()) return;
327 if(arenaround)
329 arenaround = 0;
330 loopv(sv.clients) if(sv.clients[i]->state.state==CS_DEAD || sv.clients[i]->state.state==CS_ALIVE)
332 sv.clients[i]->state.respawn();
333 sv.sendspawn(sv.clients[i]);
335 return;
338 int gamemode = sv.gamemode;
339 clientinfo *alive = NULL;
340 bool dead = false;
341 loopv(sv.clients)
343 clientinfo *ci = sv.clients[i];
344 if(ci->state.state==CS_ALIVE || (ci->state.state==CS_DEAD && ci->state.lastspawn>=0))
346 if(!alive) alive = ci;
347 else if(!m_teammode || strcmp(alive->team, ci->team)) return;
349 else if(ci->state.state==CS_DEAD) dead = true;
351 if(!dead) return;
352 sendf(-1, 1, "ri2", SV_ARENAWIN, !alive ? -1 : alive->clientnum);
353 arenaround = sv.gamemillis+5000;
357 #define CAPTURESERV 1
358 #include "capture.h"
359 #undef CAPTURESERV
361 #define ASSASSINSERV 1
362 #include "assassin.h"
363 #undef ASSASSINSERV
365 #define CTFSERV 1
366 #include "ctf.h"
367 #undef CTFSERV
369 arenaservmode arenamode;
370 captureservmode capturemode;
371 assassinservmode assassinmode;
372 ctfservmode ctfmode;
373 servmode *smode;
375 fpsserver() : notgotitems(true), notgotbases(false), gamemode(0), interm(0), minremain(0), mapreload(false), lastsend(0), mastermode(MM_OPEN), mastermask(MM_DEFAULT), currentmaster(-1), masterupdate(false), mapdata(NULL), reliablemessages(false), demonextmatch(false), demotmp(NULL), demorecord(NULL), demoplayback(NULL), nextplayback(0), arenamode(*this), capturemode(*this), assassinmode(*this), ctfmode(*this), smode(NULL)
377 serverdesc[0] = '\0';
378 masterpass[0] = '\0';
381 void *newinfo() { return new clientinfo; }
382 void deleteinfo(void *ci) { delete (clientinfo *)ci; }
384 vector<server_entity> sents;
385 vector<savedscore> scores;
387 static const char *modestr(int n, const char *unknown = "unknown")
389 static const char *modenames[] =
391 "slowmo SP", "slowmo DMSP", "demo", "SP", "DMSP", "ffa/default", "coopedit", "ffa/duel", "teamplay",
392 "instagib", "instagib team", "efficiency", "efficiency team",
393 "insta arena", "insta clan arena", "tactics arena", "tactics clan arena",
394 "capture", "insta capture", "regen capture", "assassin", "insta assassin",
395 "ctf", "insta ctf"
397 return (n>=-5 && size_t(n+5)<sizeof(modenames)/sizeof(modenames[0])) ? modenames[n+5] : unknown;
400 static const char *mastermodestr(int n, const char *unknown = "unknown")
402 static const char *mastermodenames[] =
404 "open", "veto", "locked", "private"
406 return (n>=0 && size_t(n)<sizeof(mastermodenames)/sizeof(mastermodenames[0])) ? mastermodenames[n] : unknown;
409 void sendservmsg(const char *s) { sendf(-1, 1, "ris", SV_SERVMSG, s); }
411 void resetitems()
413 sents.setsize(0);
414 //cps.reset();
417 int spawntime(int type)
419 if(m_classicsp) return INT_MAX;
420 int np = nonspectators();
421 np = np<3 ? 4 : (np>4 ? 2 : 3); // spawn times are dependent on number of players
422 int sec = 0;
423 switch(type)
425 case I_SHELLS:
426 case I_BULLETS:
427 case I_ROCKETS:
428 case I_ROUNDS:
429 case I_GRENADES:
430 case I_CARTRIDGES: sec = np*4; break;
431 case I_HEALTH: sec = np*5; break;
432 case I_GREENARMOUR:
433 case I_YELLOWARMOUR: sec = 20; break;
434 case I_BOOST:
435 case I_QUAD: sec = 40+rnd(40); break;
437 return sec*1000;
440 bool pickup(int i, int sender) // server side item pickup, acknowledge first client that gets it
442 if(minremain<=0 || !sents.inrange(i) || !sents[i].spawned) return false;
443 clientinfo *ci = (clientinfo *)getinfo(sender);
444 if(!ci || (!ci->local && !ci->state.canpickup(sents[i].type))) return false;
445 sents[i].spawned = false;
446 sents[i].spawntime = spawntime(sents[i].type);
447 sendf(-1, 1, "ri3", SV_ITEMACC, i, sender);
448 ci->state.pickup(sents[i].type);
449 return true;
452 void vote(char *map, int reqmode, int sender)
454 clientinfo *ci = (clientinfo *)getinfo(sender);
455 if(!ci || (ci->state.state==CS_SPECTATOR && !ci->privilege)) return;
456 s_strcpy(ci->mapvote, map);
457 ci->modevote = reqmode;
458 if(!ci->mapvote[0]) return;
459 if(ci->local || mapreload || (ci->privilege && mastermode>=MM_VETO))
461 if(demorecord) enddemorecord();
462 if(!ci->local && !mapreload)
464 s_sprintfd(msg)("%s forced %s on map %s", privname(ci->privilege), modestr(reqmode), map);
465 sendservmsg(msg);
467 sendf(-1, 1, "risii", SV_MAPCHANGE, ci->mapvote, ci->modevote, 1);
468 changemap(ci->mapvote, ci->modevote);
470 else
472 s_sprintfd(msg)("%s suggests %s on map %s (select map to vote)", colorname(ci), modestr(reqmode), map);
473 sendservmsg(msg);
474 checkvotes();
478 clientinfo *choosebestclient(float &bestrank)
480 clientinfo *best = NULL;
481 bestrank = -1;
482 loopv(clients)
484 clientinfo *ci = clients[i];
485 if(ci->state.timeplayed<0) continue;
486 float rank = ci->state.state!=CS_SPECTATOR ? ci->state.effectiveness/max(ci->state.timeplayed, 1) : -1;
487 if(!best || rank > bestrank) { best = ci; bestrank = rank; }
489 return best;
492 void autoteam()
494 static const char *teamnames[2] = {"good", "evil"};
495 vector<clientinfo *> team[2];
496 float teamrank[2] = {0, 0};
497 for(int round = 0, remaining = clients.length(); remaining>=0; round++)
499 int first = round&1, second = (round+1)&1, selected = 0;
500 while(teamrank[first] <= teamrank[second])
502 float rank;
503 clientinfo *ci = choosebestclient(rank);
504 if(!ci) break;
505 if(m_capture || m_ctf) rank = 1;
506 else if(selected && rank<=0) break;
507 ci->state.timeplayed = -1;
508 team[first].add(ci);
509 if(rank>0) teamrank[first] += rank;
510 selected++;
511 if(rank<=0) break;
513 if(!selected) break;
514 remaining -= selected;
516 loopi(sizeof(team)/sizeof(team[0]))
518 loopvj(team[i])
520 clientinfo *ci = team[i][j];
521 if(!strcmp(ci->team, teamnames[i])) continue;
522 s_strncpy(ci->team, teamnames[i], MAXTEAMLEN+1);
523 sendf(-1, 1, "riis", SV_SETTEAM, ci->clientnum, teamnames[i]);
528 struct teamrank
530 const char *name;
531 float rank;
532 int clients;
534 teamrank(const char *name) : name(name), rank(0), clients(0) {}
537 const char *chooseworstteam(const char *suggest = NULL, clientinfo *exclude = NULL)
539 teamrank teamranks[2] = { teamrank("good"), teamrank("evil") };
540 const int numteams = sizeof(teamranks)/sizeof(teamranks[0]);
541 loopv(clients)
543 clientinfo *ci = clients[i];
544 if(ci==exclude || ci->state.state==CS_SPECTATOR || !ci->team[0]) continue;
545 ci->state.timeplayed += lastmillis - ci->state.lasttimeplayed;
546 ci->state.lasttimeplayed = lastmillis;
548 loopj(numteams) if(!strcmp(ci->team, teamranks[j].name))
550 teamrank &ts = teamranks[j];
551 ts.rank += ci->state.effectiveness/max(ci->state.timeplayed, 1);
552 ts.clients++;
553 break;
556 teamrank *worst = &teamranks[numteams-1];
557 loopi(numteams-1)
559 teamrank &ts = teamranks[i];
560 if(m_capture || m_ctf)
562 if(ts.clients < worst->clients || (ts.clients == worst->clients && ts.rank < worst->rank)) worst = &ts;
564 else if(ts.rank < worst->rank || (ts.rank == worst->rank && ts.clients < worst->clients)) worst = &ts;
566 return worst->name;
569 void writedemo(int chan, void *data, int len)
571 if(!demorecord) return;
572 int stamp[3] = { gamemillis, chan, len };
573 endianswap(stamp, sizeof(int), 3);
574 gzwrite(demorecord, stamp, sizeof(stamp));
575 gzwrite(demorecord, data, len);
578 void recordpacket(int chan, void *data, int len)
580 writedemo(chan, data, len);
583 void enddemorecord()
585 if(!demorecord) return;
587 gzclose(demorecord);
588 demorecord = NULL;
590 #ifdef WIN32
591 demotmp = fopen("demorecord", "rb");
592 #endif
593 if(!demotmp) return;
595 fseek(demotmp, 0, SEEK_END);
596 int len = ftell(demotmp);
597 rewind(demotmp);
598 if(demos.length()>=MAXDEMOS)
600 delete[] demos[0].data;
601 demos.remove(0);
603 demofile &d = demos.add();
604 time_t t = time(NULL);
605 char *timestr = ctime(&t), *trim = timestr + strlen(timestr);
606 while(trim>timestr && isspace(*--trim)) *trim = '\0';
607 s_sprintf(d.info)("%s: %s, %s, %.2f%s", timestr, modestr(gamemode), smapname, len > 1024*1024 ? len/(1024*1024.f) : len/1024.0f, len > 1024*1024 ? "MB" : "kB");
608 s_sprintfd(msg)("demo \"%s\" recorded", d.info);
609 sendservmsg(msg);
610 d.data = new uchar[len];
611 d.len = len;
612 fread(d.data, 1, len, demotmp);
613 fclose(demotmp);
614 demotmp = NULL;
617 void setupdemorecord()
619 if(haslocalclients() || !m_mp(gamemode) || gamemode==1) return;
621 #ifdef WIN32
622 gzFile f = gzopen("demorecord", "wb9");
623 if(!f) return;
624 #else
625 demotmp = tmpfile();
626 if(!demotmp) return;
627 setvbuf(demotmp, NULL, _IONBF, 0);
629 gzFile f = gzdopen(_dup(_fileno(demotmp)), "wb9");
630 if(!f)
632 fclose(demotmp);
633 demotmp = NULL;
634 return;
636 #endif
638 sendservmsg("recording demo");
640 demorecord = f;
642 demoheader hdr;
643 memcpy(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic));
644 hdr.version = DEMO_VERSION;
645 hdr.protocol = PROTOCOL_VERSION;
646 endianswap(&hdr.version, sizeof(int), 1);
647 endianswap(&hdr.protocol, sizeof(int), 1);
648 gzwrite(demorecord, &hdr, sizeof(demoheader));
650 ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, 0);
651 ucharbuf p(packet->data, packet->dataLength);
652 welcomepacket(p, -1, packet);
653 writedemo(1, p.buf, p.len);
654 enet_packet_destroy(packet);
656 uchar buf[MAXTRANS];
657 loopv(clients)
659 clientinfo *ci = clients[i];
660 uchar header[16];
661 ucharbuf q(&buf[sizeof(header)], sizeof(buf)-sizeof(header));
662 putint(q, SV_INITC2S);
663 sendstring(ci->name, q);
664 sendstring(ci->team, q);
666 ucharbuf h(header, sizeof(header));
667 putint(h, SV_CLIENT);
668 putint(h, ci->clientnum);
669 putuint(h, q.len);
671 memcpy(&buf[sizeof(header)-h.len], header, h.len);
673 writedemo(1, &buf[sizeof(header)-h.len], h.len+q.len);
677 void listdemos(int cn)
679 ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
680 if(!packet) return;
681 ucharbuf p(packet->data, packet->dataLength);
682 putint(p, SV_SENDDEMOLIST);
683 putint(p, demos.length());
684 loopv(demos) sendstring(demos[i].info, p);
685 enet_packet_resize(packet, p.length());
686 sendpacket(cn, 1, packet);
687 if(!packet->referenceCount) enet_packet_destroy(packet);
690 void cleardemos(int n)
692 if(!n)
694 loopv(demos) delete[] demos[i].data;
695 demos.setsize(0);
696 sendservmsg("cleared all demos");
698 else if(demos.inrange(n-1))
700 delete[] demos[n-1].data;
701 demos.remove(n-1);
702 s_sprintfd(msg)("cleared demo %d", n);
703 sendservmsg(msg);
707 void senddemo(int cn, int num)
709 if(!num) num = demos.length();
710 if(!demos.inrange(num-1)) return;
711 demofile &d = demos[num-1];
712 sendf(cn, 2, "rim", SV_SENDDEMO, d.len, d.data);
715 void setupdemoplayback()
717 demoheader hdr;
718 string msg;
719 msg[0] = '\0';
720 s_sprintfd(file)("%s.dmo", smapname);
721 demoplayback = opengzfile(file, "rb9");
722 if(!demoplayback) s_sprintf(msg)("could not read demo \"%s\"", file);
723 else if(gzread(demoplayback, &hdr, sizeof(demoheader))!=sizeof(demoheader) || memcmp(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic)))
724 s_sprintf(msg)("\"%s\" is not a demo file", file);
725 else
727 endianswap(&hdr.version, sizeof(int), 1);
728 endianswap(&hdr.protocol, sizeof(int), 1);
729 if(hdr.version!=DEMO_VERSION) s_sprintf(msg)("demo \"%s\" requires an %s version of Sauerbraten", file, hdr.version<DEMO_VERSION ? "older" : "newer");
730 else if(hdr.protocol!=PROTOCOL_VERSION) s_sprintf(msg)("demo \"%s\" requires an %s version of Sauerbraten", file, hdr.protocol<PROTOCOL_VERSION ? "older" : "newer");
732 if(msg[0])
734 if(demoplayback) { gzclose(demoplayback); demoplayback = NULL; }
735 sendservmsg(msg);
736 return;
739 s_sprintf(msg)("playing demo \"%s\"", file);
740 sendservmsg(msg);
742 sendf(-1, 1, "rii", SV_DEMOPLAYBACK, 1);
744 if(gzread(demoplayback, &nextplayback, sizeof(nextplayback))!=sizeof(nextplayback))
746 enddemoplayback();
747 return;
749 endianswap(&nextplayback, sizeof(nextplayback), 1);
752 void enddemoplayback()
754 if(!demoplayback) return;
755 gzclose(demoplayback);
756 demoplayback = NULL;
758 sendf(-1, 1, "rii", SV_DEMOPLAYBACK, 0);
760 sendservmsg("demo playback finished");
762 loopv(clients)
764 ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
765 ucharbuf p(packet->data, packet->dataLength);
766 welcomepacket(p, clients[i]->clientnum, packet);
767 enet_packet_resize(packet, p.length());
768 sendpacket(clients[i]->clientnum, 1, packet);
769 if(!packet->referenceCount) enet_packet_destroy(packet);
773 void readdemo()
775 if(!demoplayback) return;
776 while(gamemillis>=nextplayback)
778 int chan, len;
779 if(gzread(demoplayback, &chan, sizeof(chan))!=sizeof(chan) ||
780 gzread(demoplayback, &len, sizeof(len))!=sizeof(len))
782 enddemoplayback();
783 return;
785 endianswap(&chan, sizeof(chan), 1);
786 endianswap(&len, sizeof(len), 1);
787 ENetPacket *packet = enet_packet_create(NULL, len, 0);
788 if(!packet || gzread(demoplayback, packet->data, len)!=len)
790 if(packet) enet_packet_destroy(packet);
791 enddemoplayback();
792 return;
794 sendpacket(-1, chan, packet);
795 if(!packet->referenceCount) enet_packet_destroy(packet);
796 if(gzread(demoplayback, &nextplayback, sizeof(nextplayback))!=sizeof(nextplayback))
798 enddemoplayback();
799 return;
801 endianswap(&nextplayback, sizeof(nextplayback), 1);
805 void changemap(const char *s, int mode)
807 if(m_demo) enddemoplayback();
808 else enddemorecord();
810 mapreload = false;
811 gamemode = mode;
812 gamemillis = 0;
813 minremain = m_teammode && !m_ctf ? 15 : 10;
814 gamelimit = minremain*60000;
815 interm = 0;
816 s_strcpy(smapname, s);
817 resetitems();
818 notgotitems = true;
819 scores.setsize(0);
820 loopv(clients)
822 clientinfo *ci = clients[i];
823 ci->state.timeplayed += lastmillis - ci->state.lasttimeplayed;
825 if(m_teammode) autoteam();
827 if(m_arena) smode = &arenamode;
828 else if(m_capture) smode = &capturemode;
829 else if(m_assassin) smode = &assassinmode;
830 else if(m_ctf) smode = &ctfmode;
831 else smode = NULL;
832 if(smode) smode->reset(false);
834 if(gamemode>1 || (gamemode==0 && hasnonlocalclients())) sendf(-1, 1, "ri2", SV_TIMEUP, minremain);
835 loopv(clients)
837 clientinfo *ci = clients[i];
838 ci->mapchange();
839 ci->state.lasttimeplayed = lastmillis;
840 if(m_mp(gamemode) && ci->state.state!=CS_SPECTATOR) sendspawn(ci);
843 if(m_demo) setupdemoplayback();
844 else if(demonextmatch)
846 demonextmatch = false;
847 setupdemorecord();
851 savedscore &findscore(clientinfo *ci, bool insert)
853 uint ip = getclientip(ci->clientnum);
854 if(!ip) return *(savedscore *)0;
855 if(!insert)
857 loopv(clients)
859 clientinfo *oi = clients[i];
860 if(oi->clientnum != ci->clientnum && getclientip(oi->clientnum) == ip && !strcmp(oi->name, ci->name))
862 oi->state.timeplayed += lastmillis - oi->state.lasttimeplayed;
863 oi->state.lasttimeplayed = lastmillis;
864 static savedscore curscore;
865 curscore.save(oi->state);
866 return curscore;
870 loopv(scores)
872 savedscore &sc = scores[i];
873 if(sc.ip == ip && !strcmp(sc.name, ci->name)) return sc;
875 if(!insert) return *(savedscore *)0;
876 savedscore &sc = scores.add();
877 sc.ip = ip;
878 s_strcpy(sc.name, ci->name);
879 return sc;
882 void savescore(clientinfo *ci)
884 savedscore &sc = findscore(ci, true);
885 if(&sc) sc.save(ci->state);
888 struct votecount
890 char *map;
891 int mode, count;
892 votecount() {}
893 votecount(char *s, int n) : map(s), mode(n), count(0) {}
896 void checkvotes(bool force = false)
898 vector<votecount> votes;
899 int maxvotes = 0;
900 loopv(clients)
902 clientinfo *oi = clients[i];
903 if(oi->state.state==CS_SPECTATOR && !oi->privilege) continue;
904 maxvotes++;
905 if(!oi->mapvote[0]) continue;
906 votecount *vc = NULL;
907 loopvj(votes) if(!strcmp(oi->mapvote, votes[j].map) && oi->modevote==votes[j].mode)
909 vc = &votes[j];
910 break;
912 if(!vc) vc = &votes.add(votecount(oi->mapvote, oi->modevote));
913 vc->count++;
915 votecount *best = NULL;
916 loopv(votes) if(!best || votes[i].count > best->count || (votes[i].count == best->count && rnd(2))) best = &votes[i];
917 if(force || (best && best->count > maxvotes/2))
919 if(demorecord) enddemorecord();
920 if(best && (best->count > (force ? 1 : maxvotes/2)))
922 sendservmsg(force ? "vote passed by default" : "vote passed by majority");
923 sendf(-1, 1, "risii", SV_MAPCHANGE, best->map, best->mode, 1);
924 changemap(best->map, best->mode);
926 else
928 mapreload = true;
929 if(clients.length()) sendf(-1, 1, "ri", SV_MAPRELOAD);
934 int nonspectators(int exclude = -1)
936 int n = 0;
937 loopv(clients) if(i!=exclude && clients[i]->state.state!=CS_SPECTATOR) n++;
938 return n;
941 int checktype(int type, clientinfo *ci)
943 if(ci && ci->local) return type;
944 #if 0
945 // other message types can get sent by accident if a master forces spectator on someone, so disabling this case for now and checking for spectator state in message handlers
946 // spectators can only connect and talk
947 static int spectypes[] = { SV_INITC2S, SV_POS, SV_TEXT, SV_PING, SV_CLIENTPING, SV_GETMAP, SV_SETMASTER };
948 if(ci && ci->state.state==CS_SPECTATOR && !ci->privilege)
950 loopi(sizeof(spectypes)/sizeof(int)) if(type == spectypes[i]) return type;
951 return -1;
953 #endif
954 // only allow edit messages in coop-edit mode
955 if(type>=SV_EDITENT && type<=SV_GETMAP && gamemode!=1) return -1;
956 // server only messages
957 static int servtypes[] = { SV_INITS2C, SV_MAPRELOAD, SV_SERVMSG, SV_DAMAGE, SV_HITPUSH, SV_SHOTFX, SV_DIED, SV_SPAWNSTATE, SV_FORCEDEATH, SV_ARENAWIN, SV_ITEMACC, SV_ITEMSPAWN, SV_TIMEUP, SV_CDIS, SV_CURRENTMASTER, SV_PONG, SV_RESUME, SV_TEAMSCORE, SV_BASEINFO, SV_BASEREGEN, SV_ANNOUNCE, SV_CLEARTARGETS, SV_CLEARHUNTERS, SV_ADDTARGET, SV_REMOVETARGET, SV_ADDHUNTER, SV_REMOVEHUNTER, SV_SENDDEMOLIST, SV_SENDDEMO, SV_DEMOPLAYBACK, SV_SENDMAP, SV_DROPFLAG, SV_SCOREFLAG, SV_RETURNFLAG, SV_CLIENT };
958 if(ci) loopi(sizeof(servtypes)/sizeof(int)) if(type == servtypes[i]) return -1;
959 return type;
962 static void freecallback(ENetPacket *packet)
964 extern igameserver *sv;
965 ((fpsserver *)sv)->cleanworldstate(packet);
968 void cleanworldstate(ENetPacket *packet)
970 loopv(worldstates)
972 worldstate *ws = worldstates[i];
973 if(packet->data >= ws->positions.getbuf() && packet->data <= &ws->positions.last()) ws->uses--;
974 else if(packet->data >= ws->messages.getbuf() && packet->data <= &ws->messages.last()) ws->uses--;
975 else continue;
976 if(!ws->uses)
978 delete ws;
979 worldstates.remove(i);
981 break;
985 bool buildworldstate()
987 static struct { int posoff, msgoff, msglen; } pkt[MAXCLIENTS];
988 worldstate &ws = *new worldstate;
989 loopv(clients)
991 clientinfo &ci = *clients[i];
992 if(ci.position.empty()) pkt[i].posoff = -1;
993 else
995 pkt[i].posoff = ws.positions.length();
996 loopvj(ci.position) ws.positions.add(ci.position[j]);
998 if(ci.messages.empty()) pkt[i].msgoff = -1;
999 else
1001 pkt[i].msgoff = ws.messages.length();
1002 ucharbuf p = ws.messages.reserve(16);
1003 putint(p, SV_CLIENT);
1004 putint(p, ci.clientnum);
1005 putuint(p, ci.messages.length());
1006 ws.messages.addbuf(p);
1007 loopvj(ci.messages) ws.messages.add(ci.messages[j]);
1008 pkt[i].msglen = ws.messages.length() - pkt[i].msgoff;
1011 int psize = ws.positions.length(), msize = ws.messages.length();
1012 if(psize) recordpacket(0, ws.positions.getbuf(), psize);
1013 if(msize) recordpacket(1, ws.messages.getbuf(), msize);
1014 loopi(psize) { uchar c = ws.positions[i]; ws.positions.add(c); }
1015 loopi(msize) { uchar c = ws.messages[i]; ws.messages.add(c); }
1016 ws.uses = 0;
1017 loopv(clients)
1019 clientinfo &ci = *clients[i];
1020 ENetPacket *packet;
1021 if(psize && (pkt[i].posoff<0 || psize-ci.position.length()>0))
1023 packet = enet_packet_create(&ws.positions[pkt[i].posoff<0 ? 0 : pkt[i].posoff+ci.position.length()],
1024 pkt[i].posoff<0 ? psize : psize-ci.position.length(),
1025 ENET_PACKET_FLAG_NO_ALLOCATE);
1026 sendpacket(ci.clientnum, 0, packet);
1027 if(!packet->referenceCount) enet_packet_destroy(packet);
1028 else { ++ws.uses; packet->freeCallback = freecallback; }
1030 ci.position.setsizenodelete(0);
1032 if(msize && (pkt[i].msgoff<0 || msize-pkt[i].msglen>0))
1034 packet = enet_packet_create(&ws.messages[pkt[i].msgoff<0 ? 0 : pkt[i].msgoff+pkt[i].msglen],
1035 pkt[i].msgoff<0 ? msize : msize-pkt[i].msglen,
1036 (reliablemessages ? ENET_PACKET_FLAG_RELIABLE : 0) | ENET_PACKET_FLAG_NO_ALLOCATE);
1037 sendpacket(ci.clientnum, 1, packet);
1038 if(!packet->referenceCount) enet_packet_destroy(packet);
1039 else { ++ws.uses; packet->freeCallback = freecallback; }
1041 ci.messages.setsizenodelete(0);
1043 reliablemessages = false;
1044 if(!ws.uses)
1046 delete &ws;
1047 return false;
1049 else
1051 worldstates.add(&ws);
1052 return true;
1056 bool sendpackets()
1058 if(clients.empty()) return false;
1059 enet_uint32 curtime = enet_time_get()-lastsend;
1060 if(curtime<33) return false;
1061 bool flush = buildworldstate();
1062 lastsend += curtime - (curtime%33);
1063 return flush;
1066 void parsepacket(int sender, int chan, bool reliable, ucharbuf &p) // has to parse exactly each byte of the packet
1068 if(sender<0) return;
1069 if(chan==2)
1071 receivefile(sender, p.buf, p.maxlen);
1072 return;
1074 if(reliable) reliablemessages = true;
1075 char text[MAXTRANS];
1076 int cn = -1, type;
1077 clientinfo *ci = sender>=0 ? (clientinfo *)getinfo(sender) : NULL;
1078 #define QUEUE_MSG { if(!ci->local) while(curmsg<p.length()) ci->messages.add(p.buf[curmsg++]); }
1079 #define QUEUE_BUF(size, body) { \
1080 if(!ci->local) \
1082 curmsg = p.length(); \
1083 ucharbuf buf = ci->messages.reserve(size); \
1084 { body; } \
1085 ci->messages.addbuf(buf); \
1088 #define QUEUE_INT(n) QUEUE_BUF(5, putint(buf, n))
1089 #define QUEUE_UINT(n) QUEUE_BUF(4, putuint(buf, n))
1090 #define QUEUE_STR(text) QUEUE_BUF(2*strlen(text)+1, sendstring(text, buf))
1091 int curmsg;
1092 while((curmsg = p.length()) < p.maxlen) switch(type = checktype(getint(p), ci))
1094 case SV_POS:
1096 cn = getint(p);
1097 if(cn<0 || cn>=getnumclients() || cn!=sender)
1099 disconnect_client(sender, DISC_CN);
1100 return;
1102 vec oldpos(ci->state.o);
1103 loopi(3) ci->state.o[i] = getuint(p)/DMF;
1104 getuint(p);
1105 loopi(5) getint(p);
1106 int physstate = getuint(p);
1107 if(physstate&0x20) loopi(2) getint(p);
1108 if(physstate&0x10) getint(p);
1109 getuint(p);
1110 if(!ci->local && (ci->state.state==CS_ALIVE || ci->state.state==CS_EDITING))
1112 ci->position.setsizenodelete(0);
1113 while(curmsg<p.length()) ci->position.add(p.buf[curmsg++]);
1115 if(smode && ci->state.state==CS_ALIVE) smode->moved(ci, oldpos, ci->state.o);
1116 break;
1119 case SV_EDITMODE:
1121 int val = getint(p);
1122 if(!ci->local && gamemode!=1) break;
1123 if(val ? ci->state.state!=CS_ALIVE && ci->state.state!=CS_DEAD : ci->state.state!=CS_EDITING) break;
1124 if(smode)
1126 if(val) smode->leavegame(ci);
1127 else smode->entergame(ci);
1129 if(val)
1131 ci->state.editstate = ci->state.state;
1132 ci->state.state = CS_EDITING;
1134 else ci->state.state = ci->state.editstate;
1135 if(val)
1137 ci->events.setsizenodelete(0);
1138 ci->state.rockets.reset();
1139 ci->state.grenades.reset();
1141 QUEUE_MSG;
1142 break;
1145 case SV_TRYSPAWN:
1146 if(ci->state.state!=CS_DEAD || ci->state.lastspawn>=0 || (smode && !smode->canspawn(ci))) break;
1147 if(ci->state.lastdeath) ci->state.respawn();
1148 sendspawn(ci);
1149 break;
1151 case SV_GUNSELECT:
1153 int gunselect = getint(p);
1154 if(ci->state.state!=CS_ALIVE) break;
1155 ci->state.gunselect = gunselect;
1156 QUEUE_MSG;
1157 break;
1160 case SV_SPAWN:
1162 int ls = getint(p), gunselect = getint(p);
1163 if((ci->state.state!=CS_ALIVE && ci->state.state!=CS_DEAD) || ls!=ci->state.lifesequence || ci->state.lastspawn<0) break;
1164 ci->state.lastspawn = -1;
1165 ci->state.state = CS_ALIVE;
1166 ci->state.gunselect = gunselect;
1167 if(smode) smode->spawned(ci);
1168 QUEUE_BUF(100,
1170 putint(buf, SV_SPAWN);
1171 sendstate(ci->state, buf);
1173 break;
1176 case SV_SUICIDE:
1178 gameevent &suicide = ci->addevent();
1179 suicide.type = GE_SUICIDE;
1180 break;
1183 case SV_SHOOT:
1185 gameevent &shot = ci->addevent();
1186 shot.type = GE_SHOT;
1187 #define seteventmillis(event) \
1189 event.id = getint(p); \
1190 if(!ci->timesync || (ci->events.length()==1 && ci->state.waitexpired(gamemillis))) \
1192 ci->timesync = true; \
1193 ci->gameoffset = gamemillis - event.id; \
1194 event.millis = gamemillis; \
1196 else event.millis = ci->gameoffset + event.id; \
1198 seteventmillis(shot.shot);
1199 shot.shot.gun = getint(p);
1200 loopk(3) shot.shot.from[k] = getint(p)/DMF;
1201 loopk(3) shot.shot.to[k] = getint(p)/DMF;
1202 int hits = getint(p);
1203 loopk(hits)
1205 gameevent &hit = ci->addevent();
1206 hit.type = GE_HIT;
1207 hit.hit.target = getint(p);
1208 hit.hit.lifesequence = getint(p);
1209 hit.hit.rays = getint(p);
1210 loopk(3) hit.hit.dir[k] = getint(p)/DNF;
1212 break;
1215 case SV_EXPLODE:
1217 gameevent &exp = ci->addevent();
1218 exp.type = GE_EXPLODE;
1219 seteventmillis(exp.explode);
1220 exp.explode.gun = getint(p);
1221 exp.explode.id = getint(p);
1222 int hits = getint(p);
1223 loopk(hits)
1225 gameevent &hit = ci->addevent();
1226 hit.type = GE_HIT;
1227 hit.hit.target = getint(p);
1228 hit.hit.lifesequence = getint(p);
1229 hit.hit.dist = getint(p)/DMF;
1230 loopk(3) hit.hit.dir[k] = getint(p)/DNF;
1232 break;
1235 case SV_ITEMPICKUP:
1237 int n = getint(p);
1238 gameevent &pickup = ci->addevent();
1239 pickup.type = GE_PICKUP;
1240 pickup.pickup.ent = n;
1241 break;
1244 case SV_TEXT:
1245 QUEUE_MSG;
1246 getstring(text, p);
1247 filtertext(text, text);
1248 QUEUE_STR(text);
1249 break;
1251 case SV_SAYTEAM:
1253 getstring(text, p);
1254 if(ci->state.state==CS_SPECTATOR || !m_teammode || !ci->team[0]) break;
1255 loopv(clients)
1257 clientinfo *t = clients[i];
1258 if(t==ci || t->state.state==CS_SPECTATOR || strcmp(ci->team, t->team)) continue;
1259 sendf(t->clientnum, 1, "riis", SV_SAYTEAM, ci->clientnum, text);
1261 break;
1264 case SV_INITC2S:
1266 QUEUE_MSG;
1267 bool connected = !ci->name[0];
1268 getstring(text, p);
1269 filtertext(text, text, false, MAXNAMELEN);
1270 if(!text[0]) s_strcpy(text, "unnamed");
1271 QUEUE_STR(text);
1272 s_strncpy(ci->name, text, MAXNAMELEN+1);
1273 if(!ci->local && connected)
1275 savedscore &sc = findscore(ci, false);
1276 if(&sc)
1278 sc.restore(ci->state);
1279 gamestate &gs = ci->state;
1280 sendf(-1, 1, "ri2i9vi", SV_RESUME, sender,
1281 gs.state, gs.frags, gs.quadmillis,
1282 gs.lifesequence,
1283 gs.health, gs.maxhealth,
1284 gs.armour, gs.armourtype,
1285 gs.gunselect, GUN_PISTOL-GUN_SG+1, &gs.ammo[GUN_SG], -1);
1288 getstring(text, p);
1289 filtertext(text, text, false, MAXTEAMLEN);
1290 if(!ci->local && (smode && !smode->canchangeteam(ci, ci->team, text)) && m_teammode)
1292 const char *worst = chooseworstteam(text, ci);
1293 if(worst)
1295 s_strcpy(text, worst);
1296 sendf(sender, 1, "riis", SV_SETTEAM, sender, worst);
1297 QUEUE_STR(worst);
1299 else QUEUE_STR(text);
1301 else QUEUE_STR(text);
1302 if(smode && ci->state.state==CS_ALIVE && strcmp(ci->team, text)) smode->changeteam(ci, ci->team, text);
1303 s_strncpy(ci->team, text, MAXTEAMLEN+1);
1304 QUEUE_MSG;
1305 break;
1308 case SV_MAPVOTE:
1309 case SV_MAPCHANGE:
1311 getstring(text, p);
1312 filtertext(text, text);
1313 int reqmode = getint(p);
1314 if(type!=SV_MAPVOTE && !mapreload) break;
1315 if(!ci->local && !m_mp(reqmode)) reqmode = 0;
1316 vote(text, reqmode, sender);
1317 break;
1320 case SV_ITEMLIST:
1322 if((ci->state.state==CS_SPECTATOR && !ci->privilege) || !notgotitems) { while(getint(p)>=0 && !p.overread()) getint(p); break; }
1323 int n;
1324 while((n = getint(p))>=0 && !p.overread())
1326 server_entity se = { getint(p), false, 0 };
1327 while(sents.length()<=n) sents.add(se);
1328 if(gamemode>=0 && (sents[n].type==I_QUAD || sents[n].type==I_BOOST)) sents[n].spawntime = spawntime(sents[n].type);
1329 else sents[n].spawned = true;
1331 notgotitems = false;
1332 break;
1335 case SV_TEAMSCORE:
1336 getstring(text, p);
1337 getint(p);
1338 QUEUE_MSG;
1339 break;
1341 case SV_BASEINFO:
1342 getint(p);
1343 getstring(text, p);
1344 getstring(text, p);
1345 getint(p);
1346 QUEUE_MSG;
1347 break;
1349 case SV_BASES:
1350 if((ci->state.state!=CS_SPECTATOR || ci->privilege) && smode==&capturemode) capturemode.parsebases(p);
1351 break;
1353 case SV_REPAMMO:
1354 if(ci->state.state!=CS_SPECTATOR && smode==&capturemode) capturemode.replenishammo(ci);
1355 break;
1357 case SV_TAKEFLAG:
1359 int flag = getint(p);
1360 if(ci->state.state!=CS_SPECTATOR && smode==&ctfmode) ctfmode.takeflag(ci, flag);
1361 break;
1364 case SV_INITFLAGS:
1365 if((ci->state.state!=CS_SPECTATOR || ci->privilege) && smode==&ctfmode) ctfmode.parseflags(p);
1366 break;
1368 case SV_PING:
1369 sendf(sender, 1, "i2", SV_PONG, getint(p));
1370 break;
1372 case SV_CLIENTPING:
1373 getint(p);
1374 QUEUE_MSG;
1375 break;
1377 case SV_MASTERMODE:
1379 int mm = getint(p);
1380 if(ci->privilege && mm>=MM_OPEN && mm<=MM_PRIVATE)
1382 if(ci->privilege>=PRIV_ADMIN || (mastermask&(1<<mm)))
1384 mastermode = mm;
1385 allowedips.setsize(0);
1386 if(mm>=MM_PRIVATE)
1388 loopv(clients) allowedips.add(getclientip(clients[i]->clientnum));
1390 s_sprintfd(s)("mastermode is now %s (%d)", mastermodestr(mastermode), mastermode);
1391 sendservmsg(s);
1393 else
1395 s_sprintfd(s)("mastermode %d is disabled on this server", mm);
1396 sendf(sender, 1, "ris", SV_SERVMSG, s);
1399 break;
1402 case SV_CLEARBANS:
1404 if(ci->privilege)
1406 bannedips.setsize(0);
1407 sendservmsg("cleared all bans");
1409 break;
1412 case SV_KICK:
1414 int victim = getint(p);
1415 if(ci->privilege && victim>=0 && victim<getnumclients() && ci->clientnum!=victim && getinfo(victim))
1417 ban &b = bannedips.add();
1418 b.time = totalmillis;
1419 b.ip = getclientip(victim);
1420 allowedips.removeobj(b.ip);
1421 disconnect_client(victim, DISC_KICK);
1423 break;
1426 case SV_SPECTATOR:
1428 int spectator = getint(p), val = getint(p);
1429 if(!ci->privilege && (ci->state.state==CS_SPECTATOR || spectator!=sender)) break;
1430 clientinfo *spinfo = (clientinfo *)getinfo(spectator);
1431 if(!spinfo) break;
1433 sendf(-1, 1, "ri3", SV_SPECTATOR, spectator, val);
1435 if(spinfo->state.state!=CS_SPECTATOR && val)
1437 if(smode) smode->leavegame(spinfo);
1438 spinfo->state.state = CS_SPECTATOR;
1439 spinfo->state.timeplayed += lastmillis - spinfo->state.lasttimeplayed;
1441 else if(spinfo->state.state==CS_SPECTATOR && !val)
1443 spinfo->state.state = CS_DEAD;
1444 spinfo->state.respawn();
1445 if(!smode || smode->canspawn(spinfo)) sendspawn(spinfo);
1446 spinfo->state.lasttimeplayed = lastmillis;
1448 break;
1451 case SV_SETTEAM:
1453 int who = getint(p);
1454 getstring(text, p);
1455 filtertext(text, text, false, MAXTEAMLEN);
1456 if(!ci->privilege || who<0 || who>=getnumclients()) break;
1457 clientinfo *wi = (clientinfo *)getinfo(who);
1458 if(!wi) break;
1459 if(!smode || smode->canchangeteam(wi, wi->team, text))
1461 if(smode && wi->state.state==CS_ALIVE && strcmp(wi->team, text))
1462 smode->changeteam(wi, wi->team, text);
1463 s_strncpy(wi->team, text, MAXTEAMLEN+1);
1465 sendf(sender, 1, "riis", SV_SETTEAM, who, wi->team);
1466 QUEUE_INT(SV_SETTEAM);
1467 QUEUE_INT(who);
1468 QUEUE_STR(wi->team);
1469 break;
1472 case SV_FORCEINTERMISSION:
1473 if(m_sp) startintermission();
1474 break;
1476 case SV_RECORDDEMO:
1478 int val = getint(p);
1479 if(ci->privilege<PRIV_ADMIN) break;
1480 demonextmatch = val!=0;
1481 s_sprintfd(msg)("demo recording is %s for next match", demonextmatch ? "enabled" : "disabled");
1482 sendservmsg(msg);
1483 break;
1486 case SV_STOPDEMO:
1488 if(!ci->local && ci->privilege<PRIV_ADMIN) break;
1489 if(m_demo) enddemoplayback();
1490 else enddemorecord();
1491 break;
1494 case SV_CLEARDEMOS:
1496 int demo = getint(p);
1497 if(ci->privilege<PRIV_ADMIN) break;
1498 cleardemos(demo);
1499 break;
1502 case SV_LISTDEMOS:
1503 if(!ci->privilege && ci->state.state==CS_SPECTATOR) break;
1504 listdemos(sender);
1505 break;
1507 case SV_GETDEMO:
1509 int n = getint(p);
1510 if(!ci->privilege && ci->state.state==CS_SPECTATOR) break;
1511 senddemo(sender, n);
1512 break;
1515 case SV_GETMAP:
1516 if(mapdata)
1518 sendf(sender, 1, "ris", SV_SERVMSG, "server sending map...");
1519 sendfile(sender, 2, mapdata, "ri", SV_SENDMAP);
1521 else sendf(sender, 1, "ris", SV_SERVMSG, "no map to send");
1522 break;
1524 case SV_NEWMAP:
1526 int size = getint(p);
1527 if(!ci->privilege && ci->state.state==CS_SPECTATOR) break;
1528 if(size>=0)
1530 smapname[0] = '\0';
1531 resetitems();
1532 notgotitems = false;
1533 if(smode) smode->reset(true);
1535 QUEUE_MSG;
1536 break;
1539 case SV_SETMASTER:
1541 int val = getint(p);
1542 getstring(text, p);
1543 setmaster(ci, val!=0, text);
1544 // don't broadcast the master password
1545 break;
1548 case SV_APPROVEMASTER:
1550 int mn = getint(p);
1551 if(mastermask&MM_AUTOAPPROVE || ci->state.state==CS_SPECTATOR) break;
1552 clientinfo *candidate = (clientinfo *)getinfo(mn);
1553 if(!candidate || !candidate->wantsmaster || mn==sender || getclientip(mn)==getclientip(sender)) break;
1554 setmaster(candidate, true, "", true);
1555 break;
1558 default:
1560 int size = msgsizelookup(type);
1561 if(size==-1) { disconnect_client(sender, DISC_TAGT); return; }
1562 if(size>0) loopi(size-1) getint(p);
1563 if(ci && ci->state.state!=CS_SPECTATOR) QUEUE_MSG;
1564 break;
1569 void sendstate(gamestate &gs, ucharbuf &p)
1571 putint(p, gs.lifesequence);
1572 putint(p, gs.health);
1573 putint(p, gs.maxhealth);
1574 putint(p, gs.armour);
1575 putint(p, gs.armourtype);
1576 putint(p, gs.gunselect);
1577 loopi(GUN_PISTOL-GUN_SG+1) putint(p, gs.ammo[GUN_SG+i]);
1580 int welcomepacket(ucharbuf &p, int n, ENetPacket *packet)
1582 clientinfo *ci = (clientinfo *)getinfo(n);
1583 int hasmap = (gamemode==1 && clients.length()>1) || (smapname[0] && (minremain>0 || (ci && ci->state.state==CS_SPECTATOR) || nonspectators(n)));
1584 putint(p, SV_INITS2C);
1585 putint(p, n);
1586 putint(p, PROTOCOL_VERSION);
1587 putint(p, hasmap);
1588 if(hasmap)
1590 putint(p, SV_MAPCHANGE);
1591 sendstring(smapname, p);
1592 putint(p, gamemode);
1593 putint(p, notgotitems ? 1 : 0);
1594 if(!ci || gamemode>1 || (gamemode==0 && hasnonlocalclients()))
1596 putint(p, SV_TIMEUP);
1597 putint(p, minremain);
1599 if(!notgotitems)
1601 putint(p, SV_ITEMLIST);
1602 loopv(sents) if(sents[i].spawned)
1604 putint(p, i);
1605 putint(p, sents[i].type);
1606 if(p.remaining() < 256)
1608 enet_packet_resize(packet, packet->dataLength + MAXTRANS);
1609 p.buf = packet->data;
1612 putint(p, -1);
1615 if(ci && !ci->local && m_teammode)
1617 const char *worst = chooseworstteam();
1618 if(worst)
1620 putint(p, SV_SETTEAM);
1621 putint(p, ci->clientnum);
1622 sendstring(worst, p);
1625 if(ci && (m_demo || m_mp(gamemode)) && ci->state.state!=CS_SPECTATOR)
1627 if(smode && !smode->canspawn(ci, true))
1629 ci->state.state = CS_DEAD;
1630 putint(p, SV_FORCEDEATH);
1631 putint(p, n);
1632 sendf(-1, 1, "ri2x", SV_FORCEDEATH, n, n);
1634 else
1636 gamestate &gs = ci->state;
1637 spawnstate(ci);
1638 putint(p, SV_SPAWNSTATE);
1639 sendstate(gs, p);
1640 gs.lastspawn = gamemillis;
1643 if(ci && ci->state.state==CS_SPECTATOR)
1645 putint(p, SV_SPECTATOR);
1646 putint(p, n);
1647 putint(p, 1);
1648 sendf(-1, 1, "ri3x", SV_SPECTATOR, n, 1, n);
1650 if(clients.length()>1)
1652 putint(p, SV_RESUME);
1653 loopv(clients)
1655 clientinfo *oi = clients[i];
1656 if(oi->clientnum==n) continue;
1657 if(p.remaining() < 256)
1659 enet_packet_resize(packet, packet->dataLength + MAXTRANS);
1660 p.buf = packet->data;
1662 putint(p, oi->clientnum);
1663 putint(p, oi->state.state);
1664 putint(p, oi->state.frags);
1665 putint(p, oi->state.quadmillis);
1666 sendstate(oi->state, p);
1668 putint(p, -1);
1670 if(smode)
1672 enet_packet_resize(packet, packet->dataLength + MAXTRANS);
1673 p.buf = packet->data;
1674 smode->initclient(ci, p, true);
1676 return 1;
1679 void checkintermission()
1681 if(minremain>0)
1683 minremain = gamemillis>=gamelimit ? 0 : (gamelimit - gamemillis + 60000 - 1)/60000;
1684 sendf(-1, 1, "ri2", SV_TIMEUP, minremain);
1685 if(!minremain && smode) smode->intermission();
1687 if(!interm && minremain<=0) interm = gamemillis+10000;
1690 void startintermission() { gamelimit = min(gamelimit, gamemillis); checkintermission(); }
1692 void clearevent(clientinfo *ci)
1694 int n = 1;
1695 while(n<ci->events.length() && ci->events[n].type==GE_HIT) n++;
1696 ci->events.remove(0, n);
1699 void spawnstate(clientinfo *ci)
1701 gamestate &gs = ci->state;
1702 gs.spawnstate(gamemode);
1703 gs.lifesequence++;
1706 void sendspawn(clientinfo *ci)
1708 gamestate &gs = ci->state;
1709 spawnstate(ci);
1710 sendf(ci->clientnum, 1, "ri7v", SV_SPAWNSTATE, gs.lifesequence,
1711 gs.health, gs.maxhealth,
1712 gs.armour, gs.armourtype,
1713 gs.gunselect, GUN_PISTOL-GUN_SG+1, &gs.ammo[GUN_SG]);
1714 gs.lastspawn = gamemillis;
1717 void dodamage(clientinfo *target, clientinfo *actor, int damage, int gun, const vec &hitpush = vec(0, 0, 0))
1719 gamestate &ts = target->state;
1720 ts.dodamage(damage);
1721 actor->state.damage += damage;
1722 sendf(-1, 1, "ri6", SV_DAMAGE, target->clientnum, actor->clientnum, damage, ts.armour, ts.health);
1723 if(target!=actor && !hitpush.iszero())
1725 vec v(hitpush);
1726 if(!v.iszero()) v.normalize();
1727 sendf(target->clientnum, 1, "ri6", SV_HITPUSH, gun, damage,
1728 int(v.x*DNF), int(v.y*DNF), int(v.z*DNF));
1730 if(ts.health<=0)
1732 target->state.deaths++;
1733 if(actor!=target && isteam(actor->team, target->team)) actor->state.teamkills++;
1734 int fragvalue = smode ? smode->fragvalue(target, actor) : (target==actor || isteam(target->team, actor->team) ? -1 : 1);
1735 actor->state.frags += fragvalue;
1736 if(fragvalue>0)
1738 int friends = 0, enemies = 0; // note: friends also includes the fragger
1739 if(m_teammode) loopv(clients) if(strcmp(clients[i]->team, actor->team)) enemies++; else friends++;
1740 else { friends = 1; enemies = clients.length()-1; }
1741 actor->state.effectiveness += fragvalue*friends/float(max(enemies, 1));
1743 sendf(-1, 1, "ri4", SV_DIED, target->clientnum, actor->clientnum, actor->state.frags);
1744 target->position.setsizenodelete(0);
1745 if(smode) smode->died(target, actor);
1746 ts.state = CS_DEAD;
1747 ts.lastdeath = gamemillis;
1748 // don't issue respawn yet until DEATHMILLIS has elapsed
1749 // ts.respawn();
1753 void processevent(clientinfo *ci, suicideevent &e)
1755 gamestate &gs = ci->state;
1756 if(gs.state!=CS_ALIVE) return;
1757 ci->state.frags += smode ? smode->fragvalue(ci, ci) : -1;
1758 ci->state.deaths++;
1759 sendf(-1, 1, "ri4", SV_DIED, ci->clientnum, ci->clientnum, gs.frags);
1760 ci->position.setsizenodelete(0);
1761 if(smode) smode->died(ci, NULL);
1762 gs.state = CS_DEAD;
1763 gs.respawn();
1766 void processevent(clientinfo *ci, explodeevent &e)
1768 gamestate &gs = ci->state;
1769 switch(e.gun)
1771 case GUN_RL:
1772 if(!gs.rockets.remove(e.id)) return;
1773 break;
1775 case GUN_GL:
1776 if(!gs.grenades.remove(e.id)) return;
1777 break;
1779 default:
1780 return;
1782 for(int i = 1; i<ci->events.length() && ci->events[i].type==GE_HIT; i++)
1784 hitevent &h = ci->events[i].hit;
1785 clientinfo *target = (clientinfo *)getinfo(h.target);
1786 if(!target || target->state.state!=CS_ALIVE || h.lifesequence!=target->state.lifesequence || h.dist<0 || h.dist>RL_DAMRAD) continue;
1788 int j = 1;
1789 for(j = 1; j<i; j++) if(ci->events[j].hit.target==h.target) break;
1790 if(j<i) continue;
1792 int damage = guns[e.gun].damage;
1793 if(gs.quadmillis) damage *= 4;
1794 damage = int(damage*(1-h.dist/RL_DISTSCALE/RL_DAMRAD));
1795 if(e.gun==GUN_RL && target==ci) damage /= RL_SELFDAMDIV;
1796 dodamage(target, ci, damage, e.gun, h.dir);
1800 void processevent(clientinfo *ci, shotevent &e)
1802 gamestate &gs = ci->state;
1803 int wait = e.millis - gs.lastshot;
1804 if(!gs.isalive(gamemillis) ||
1805 wait<gs.gunwait ||
1806 e.gun<GUN_FIST || e.gun>GUN_PISTOL ||
1807 gs.ammo[e.gun]<=0)
1808 return;
1809 if(e.gun!=GUN_FIST) gs.ammo[e.gun]--;
1810 gs.lastshot = e.millis;
1811 gs.gunwait = guns[e.gun].attackdelay;
1812 sendf(-1, 1, "ri9x", SV_SHOTFX, ci->clientnum, e.gun,
1813 int(e.from[0]*DMF), int(e.from[1]*DMF), int(e.from[2]*DMF),
1814 int(e.to[0]*DMF), int(e.to[1]*DMF), int(e.to[2]*DMF),
1815 ci->clientnum);
1816 gs.shotdamage += guns[e.gun].damage*(gs.quadmillis ? 4 : 1)*(e.gun==GUN_SG ? SGRAYS : 1);
1817 switch(e.gun)
1819 case GUN_RL: gs.rockets.add(e.id); break;
1820 case GUN_GL: gs.grenades.add(e.id); break;
1821 default:
1823 int totalrays = 0, maxrays = e.gun==GUN_SG ? SGRAYS : 1;
1824 for(int i = 1; i<ci->events.length() && ci->events[i].type==GE_HIT; i++)
1826 hitevent &h = ci->events[i].hit;
1827 clientinfo *target = (clientinfo *)getinfo(h.target);
1828 if(!target || target->state.state!=CS_ALIVE || h.lifesequence!=target->state.lifesequence || h.rays<1) continue;
1830 totalrays += h.rays;
1831 if(totalrays>maxrays) continue;
1832 int damage = h.rays*guns[e.gun].damage;
1833 if(gs.quadmillis) damage *= 4;
1834 dodamage(target, ci, damage, e.gun, h.dir);
1836 break;
1841 void processevent(clientinfo *ci, pickupevent &e)
1843 gamestate &gs = ci->state;
1844 if(m_mp(gamemode) && !gs.isalive(gamemillis)) return;
1845 pickup(e.ent, ci->clientnum);
1848 void processevents()
1850 loopv(clients)
1852 clientinfo *ci = clients[i];
1853 if(curtime>0 && ci->state.quadmillis) ci->state.quadmillis = max(ci->state.quadmillis-curtime, 0);
1854 while(ci->events.length())
1856 gameevent &e = ci->events[0];
1857 if(e.type<GE_SUICIDE)
1859 if(e.shot.millis>gamemillis) break;
1860 if(e.shot.millis<ci->lastevent) { clearevent(ci); continue; }
1861 ci->lastevent = e.shot.millis;
1863 switch(e.type)
1865 case GE_SHOT: processevent(ci, e.shot); break;
1866 case GE_EXPLODE: processevent(ci, e.explode); break;
1867 // untimed events
1868 case GE_SUICIDE: processevent(ci, e.suicide); break;
1869 case GE_PICKUP: processevent(ci, e.pickup); break;
1871 clearevent(ci);
1876 void serverupdate(int _lastmillis, int _totalmillis)
1878 curtime = _lastmillis - lastmillis;
1879 gamemillis += curtime;
1880 lastmillis = _lastmillis;
1881 totalmillis = _totalmillis;
1883 if(m_demo) readdemo();
1884 else if(minremain>0)
1886 processevents();
1887 if(curtime)
1889 loopv(sents) if(sents[i].spawntime) // spawn entities when timer reached
1891 int oldtime = sents[i].spawntime;
1892 sents[i].spawntime -= curtime;
1893 if(sents[i].spawntime<=0)
1895 sents[i].spawntime = 0;
1896 sents[i].spawned = true;
1897 sendf(-1, 1, "ri2", SV_ITEMSPAWN, i);
1899 else if(sents[i].spawntime<=10000 && oldtime>10000 && (sents[i].type==I_QUAD || sents[i].type==I_BOOST))
1901 sendf(-1, 1, "ri2", SV_ANNOUNCE, sents[i].type);
1905 if(smode) smode->update();
1908 while(bannedips.length() && bannedips[0].time-totalmillis>4*60*60000) bannedips.remove(0);
1910 if(masterupdate)
1912 clientinfo *m = currentmaster>=0 ? (clientinfo *)getinfo(currentmaster) : NULL;
1913 sendf(-1, 1, "ri3", SV_CURRENTMASTER, currentmaster, m ? m->privilege : 0);
1914 masterupdate = false;
1917 if((gamemode>1 || (gamemode==0 && hasnonlocalclients())) && gamemillis-curtime>0 && gamemillis/60000!=(gamemillis-curtime)/60000) checkintermission();
1918 if(interm && gamemillis>interm)
1920 if(demorecord) enddemorecord();
1921 interm = 0;
1922 checkvotes(true);
1926 bool serveroption(char *arg)
1928 if(arg[0]=='-') switch(arg[1])
1930 case 'n': s_strcpy(serverdesc, &arg[2]); return true;
1931 case 'p': s_strcpy(masterpass, &arg[2]); return true;
1932 case 'o': if(atoi(&arg[2])) mastermask = (1<<MM_OPEN) | (1<<MM_VETO); return true;
1934 return false;
1937 void serverinit()
1939 smapname[0] = '\0';
1940 resetitems();
1943 const char *privname(int type)
1945 switch(type)
1947 case PRIV_ADMIN: return "admin";
1948 case PRIV_MASTER: return "master";
1949 default: return "unknown";
1953 void setmaster(clientinfo *ci, bool val, const char *pass = "", bool approved = false)
1955 if(approved && (!val || !ci->wantsmaster)) return;
1956 const char *name = "";
1957 if(val)
1959 if(ci->privilege)
1961 if(!masterpass[0] || !pass[0]==(ci->privilege!=PRIV_ADMIN)) return;
1963 else if(ci->state.state==CS_SPECTATOR && (!masterpass[0] || strcmp(masterpass, pass))) return;
1964 loopv(clients) if(ci!=clients[i] && clients[i]->privilege)
1966 if(masterpass[0] && !strcmp(masterpass, pass)) clients[i]->privilege = PRIV_NONE;
1967 else return;
1969 if(masterpass[0] && !strcmp(masterpass, pass)) ci->privilege = PRIV_ADMIN;
1970 else if(!approved && !(mastermask&MM_AUTOAPPROVE) && !ci->privilege)
1972 ci->wantsmaster = true;
1973 s_sprintfd(msg)("%s wants master. Type \"/approve %d\" to approve.", colorname(ci), ci->clientnum);
1974 sendservmsg(msg);
1975 return;
1977 else ci->privilege = PRIV_MASTER;
1978 name = privname(ci->privilege);
1980 else
1982 if(!ci->privilege) return;
1983 name = privname(ci->privilege);
1984 ci->privilege = 0;
1986 mastermode = MM_OPEN;
1987 allowedips.setsize(0);
1988 s_sprintfd(msg)("%s %s %s", colorname(ci), val ? (approved ? "approved for" : "claimed") : "relinquished", name);
1989 sendservmsg(msg);
1990 currentmaster = val ? ci->clientnum : -1;
1991 masterupdate = true;
1992 loopv(clients) clients[i]->wantsmaster = false;
1995 void localconnect(int n)
1997 clientinfo *ci = (clientinfo *)getinfo(n);
1998 ci->clientnum = n;
1999 ci->local = true;
2000 clients.add(ci);
2003 void localdisconnect(int n)
2005 clientinfo *ci = (clientinfo *)getinfo(n);
2006 if(smode) smode->leavegame(ci, true);
2007 clients.removeobj(ci);
2010 int clientconnect(int n, uint ip)
2012 clientinfo *ci = (clientinfo *)getinfo(n);
2013 ci->clientnum = n;
2014 clients.add(ci);
2015 loopv(bannedips) if(bannedips[i].ip==ip) return DISC_IPBAN;
2016 if(mastermode>=MM_PRIVATE)
2018 if(allowedips.find(ip)<0) return DISC_PRIVATE;
2020 if(mastermode>=MM_LOCKED) ci->state.state = CS_SPECTATOR;
2021 if(currentmaster>=0) masterupdate = true;
2022 ci->state.lasttimeplayed = lastmillis;
2023 return DISC_NONE;
2026 void clientdisconnect(int n)
2028 clientinfo *ci = (clientinfo *)getinfo(n);
2029 if(ci->privilege) setmaster(ci, false);
2030 if(smode) smode->leavegame(ci, true);
2031 ci->state.timeplayed += lastmillis - ci->state.lasttimeplayed;
2032 savescore(ci);
2033 sendf(-1, 1, "ri2", SV_CDIS, n);
2034 clients.removeobj(ci);
2035 if(clients.empty()) bannedips.setsize(0); // bans clear when server empties
2038 const char *servername() { return "sauerbratenserver"; }
2039 int serverinfoport() { return SAUERBRATEN_SERVINFO_PORT; }
2040 int serverport() { return SAUERBRATEN_SERVER_PORT; }
2041 const char *getdefaultmaster() { return "sauerbraten.org/masterserver/"; }
2043 #include "extinfo.h"
2045 void serverinforeply(ucharbuf &req, ucharbuf &p)
2047 if(!getint(req))
2049 extserverinforeply(req, p);
2050 return;
2053 putint(p, clients.length());
2054 putint(p, 5); // number of attrs following
2055 putint(p, PROTOCOL_VERSION); // a // generic attributes, passed back below
2056 putint(p, gamemode); // b
2057 putint(p, minremain); // c
2058 putint(p, maxclients);
2059 putint(p, mastermode);
2060 sendstring(smapname, p);
2061 sendstring(serverdesc, p);
2062 sendserverinforeply(p);
2065 bool servercompatible(char *name, char *sdec, char *map, int ping, const vector<int> &attr, int np)
2067 return attr.length() && attr[0]==PROTOCOL_VERSION;
2070 void receivefile(int sender, uchar *data, int len)
2072 if(gamemode != 1 || len > 1024*1024) return;
2073 clientinfo *ci = (clientinfo *)getinfo(sender);
2074 if(ci->state.state==CS_SPECTATOR && !ci->privilege) return;
2075 if(mapdata) { fclose(mapdata); mapdata = NULL; }
2076 if(!len) return;
2077 mapdata = tmpfile();
2078 if(!mapdata) { sendf(sender, 1, "ris", SV_SERVMSG, "failed to open temporary file for map"); return; }
2079 fwrite(data, 1, len, mapdata);
2080 s_sprintfd(msg)("[%s uploaded map to server, \"/getmap\" to receive it]", colorname(ci));
2081 sendservmsg(msg);
2084 bool duplicatename(clientinfo *ci, char *name)
2086 if(!name) name = ci->name;
2087 loopv(clients) if(clients[i]!=ci && !strcmp(name, clients[i]->name)) return true;
2088 return false;
2091 char *colorname(clientinfo *ci, char *name = NULL)
2093 if(!name) name = ci->name;
2094 if(name[0] && !duplicatename(ci, name)) return name;
2095 static string cname;
2096 s_sprintf(cname)("%s \fs\f5(%d)\fr", name, ci->clientnum);
2097 return cname;