Recent Posts
Erlang | 1 sec ago
None | 43 sec ago
None | 50 sec ago
None | 1 min ago
VeriLog | 1 min ago
None | 1 min ago
None | 3 min ago
None | 3 min ago
None | 3 min ago
None | 3 min ago
Cool Sites
Free Subdomains
Want a pastebin.com sub-domain for your community?
learn more...
What is pastebin?
Pastebin is a website that hosts all your text & code on dedicated servers for easy sharing.
learn more...
By root on the 1st of May 2010 03:41:47 PM Download | Raw | Embed | Report
  1. // server.cpp: little more than enhanced multicaster
  2. // runs dedicated or as client coroutine
  3.  
  4. #include "pch.h"
  5.  
  6. #ifdef WIN32
  7. #include <io.h>
  8. #else
  9. #include <unistd.h>
  10. #include <time.h>
  11. #define _dup    dup
  12. #define _fileno fileno
  13. #endif
  14.  
  15. #include "cube.h"
  16. #include "servercontroller.h"
  17.  
  18. #define DEBUGCOND (true)
  19.  
  20. #define SERVER_PROTOCOL_VERSION    (PROTOCOL_VERSION)    // server without any gameplay modification
  21. //#define SERVER_PROTOCOL_VERSION   (-PROTOCOL_VERSION)  // server with gameplay modification but compatible to vanilla client (using /modconnect)
  22. //#define SERVER_PROTOCOL_VERSION  (PROTOCOL_VERSION)    // server with incompatible protocol (change PROTOCOL_VERSION in file protocol.h to a negative number!)
  23.  
  24. void resetmap(const char *newname, int newmode, int newtime = -1, bool notify = true);
  25. void disconnect_client(int n, int reason = -1);
  26. int clienthasflag(int cn);
  27. bool refillteams(bool now = false, bool notify = true);
  28. void changeclientrole(int client, int role, char *pwd = NULL, bool force=false);
  29. int mapavailable(const char *mapname);
  30. void getservermap(void);
  31. mapstats *getservermapstats(const char *mapname, bool getlayout = false);
  32.  
  33. servercontroller *svcctrl = NULL;
  34. struct servercommandline scl;
  35.  
  36. #define valid_flag(f) (f >= 0 && f < 2)
  37.  
  38. #define SERVERMAP_PATH          "packages/maps/servermaps/"
  39. #define SERVERMAP_PATH_BUILTIN  "packages/maps/official/"
  40. #define SERVERMAP_PATH_INCOMING "packages/maps/servermaps/incoming/"
  41.  
  42. static const int DEATHMILLIS = 300;
  43.  
  44. enum { GE_NONE = 0, GE_SHOT, GE_EXPLODE, GE_HIT, GE_AKIMBO, GE_RELOAD, GE_SUICIDE, GE_PICKUP };
  45. enum { ST_EMPTY, ST_LOCAL, ST_TCPIP };
  46.  
  47. int mastermode = MM_OPEN;
  48.  
  49. // allows the gamemode macros to work with the server mode
  50. #define gamemode smode
  51. string smapname, nextmapname;
  52. int smode = 0, nextgamemode;
  53.  
  54. struct shotevent
  55. {
  56.     int type;
  57.     int millis, id;
  58.     int gun;
  59.     float from[3], to[3];
  60. };
  61.  
  62. struct explodeevent
  63. {
  64.     int type;
  65.     int millis, id;
  66.     int gun;
  67. };
  68.  
  69. struct hitevent
  70. {
  71.     int type;
  72.     int target;
  73.     int lifesequence;
  74.     union
  75.     {
  76.         int info;
  77.         float dist;
  78.     };
  79.     float dir[3];
  80. };
  81.  
  82. struct suicideevent
  83. {
  84.     int type;
  85. };
  86.  
  87. struct pickupevent
  88. {
  89.     int type;
  90.     int ent;
  91. };
  92.  
  93. struct akimboevent
  94. {
  95.     int type;
  96.     int millis, id;
  97. };
  98.  
  99. struct reloadevent
  100. {
  101.     int type;
  102.     int millis, id;
  103.     int gun;
  104. };
  105.  
  106. union gameevent
  107. {
  108.     int type;
  109.     shotevent shot;
  110.     explodeevent explode;
  111.     hitevent hit;
  112.     suicideevent suicide;
  113.     pickupevent pickup;
  114.     akimboevent akimbo;
  115.     reloadevent reload;
  116. };
  117.  
  118. template <int N>
  119. struct projectilestate
  120. {
  121.     int projs[N];
  122.     int numprojs;
  123.  
  124.     projectilestate() : numprojs(0) {}
  125.  
  126.     void reset() { numprojs = 0; }
  127.  
  128.     void add(int val)
  129.     {
  130.         if(numprojs>=N) numprojs = 0;
  131.         projs[numprojs++] = val;
  132.     }
  133.  
  134.     bool remove(int val)
  135.     {
  136.         loopi(numprojs) if(projs[i]==val)
  137.         {
  138.             projs[i] = projs[--numprojs];
  139.             return true;
  140.         }
  141.         return false;
  142.     }
  143. };
  144.  
  145. struct clientstate : playerstate
  146. {
  147.     vec o;
  148.     int state;
  149.     int lastdeath, lastspawn, lifesequence;
  150.     int lastshot;
  151.     projectilestate<8> grenades;
  152.     int akimbos, akimbomillis;
  153.     int flagscore, frags, teamkills, deaths, shotdamage, damage;
  154.  
  155.     clientstate() : state(CS_DEAD) {}
  156.  
  157.     bool isalive(int gamemillis)
  158.     {
  159.         return state==CS_ALIVE || (state==CS_DEAD && gamemillis - lastdeath <= DEATHMILLIS);
  160.     }
  161.  
  162.     bool waitexpired(int gamemillis)
  163.     {
  164.         int wait = gamemillis - lastshot;
  165.         loopi(NUMGUNS) if(wait < gunwait[i]) return false;
  166.         return true;
  167.     }
  168.  
  169.     void reset()
  170.     {
  171.         state = CS_DEAD;
  172.         lifesequence = -1;
  173.         grenades.reset();
  174.         akimbos = 0;
  175.         akimbomillis = 0;
  176.         flagscore = frags = teamkills = deaths = shotdamage = damage = 0;
  177.         respawn();
  178.     }
  179.  
  180.     void respawn()
  181.     {
  182.         playerstate::respawn();
  183.         o = vec(-1e10f, -1e10f, -1e10f);
  184.         lastdeath = 0;
  185.         lastspawn = -1;
  186.         lastshot = 0;
  187.         akimbos = 0;
  188.         akimbomillis = 0;
  189.     }
  190. };
  191.  
  192. struct savedscore
  193. {
  194.     string name;
  195.     uint ip;
  196.     int frags, flagscore, deaths, teamkills, shotdamage, damage;
  197.  
  198.     void save(clientstate &cs)
  199.     {
  200.         frags = cs.frags;
  201.         flagscore = cs.flagscore;
  202.         deaths = cs.deaths;
  203.         teamkills = cs.teamkills;
  204.         shotdamage = cs.shotdamage;
  205.         damage = cs.damage;
  206.     }
  207.  
  208.     void restore(clientstate &cs)
  209.     {
  210.         cs.frags = frags;
  211.         cs.flagscore = flagscore;
  212.         cs.deaths = deaths;
  213.         cs.teamkills = teamkills;
  214.         cs.shotdamage = shotdamage;
  215.         cs.damage = damage;
  216.     }
  217. };
  218.  
  219. static vector<savedscore> scores;
  220.  
  221. struct client                   // server side version of "dynent" type
  222. {
  223.     int type;
  224.     int clientnum;
  225.     ENetPeer *peer;
  226.     string hostname;
  227.     string name, team;
  228.     int ping;
  229.     int skin;
  230.     int vote;
  231.     int role;
  232.     int connectmillis;
  233.     bool isauthed; // for passworded servers
  234.     bool haswelcome;
  235.     bool timesync;
  236.     int gameoffset, lastevent, lastvotecall;
  237.     int demoflags;
  238.     clientstate state;
  239.     vector<gameevent> events;
  240.     vector<uchar> position, messages;
  241.     string lastsaytext;
  242.     int saychars, lastsay, spamcount;
  243.     int at3_score, at3_lastforce, lastforce;
  244.     bool at3_dontmove;
  245.     int spawnindex;
  246.     int salt;
  247.     string pwd;
  248.     int mapcollisions, farpickups;
  249.  
  250.     gameevent &addevent()
  251.     {
  252.         static gameevent dummy;
  253.         if(events.length()>100) return dummy;
  254.         return events.add();
  255.     }
  256.  
  257.     void mapchange()
  258.     {
  259.         state.reset();
  260.         events.setsizenodelete(0);
  261.         timesync = false;
  262.         lastevent = 0;
  263.         at3_lastforce = 0;
  264.         mapcollisions = farpickups = 0;
  265.     }
  266.  
  267.     void reset()
  268.     {
  269.         name[0] = team[0] = demoflags = 0;
  270.         ping = 9999;
  271.         skin = 0;
  272.         position.setsizenodelete(0);
  273.         messages.setsizenodelete(0);
  274.         isauthed = haswelcome = false;
  275.         role = CR_DEFAULT;
  276.         lastvotecall = 0;
  277.         vote = VOTE_NEUTRAL;
  278.         lastsaytext[0] = '\0';
  279.         saychars = 0;
  280.         lastforce = 0;
  281.         spawnindex = -1;
  282.         mapchange();
  283.     }
  284.  
  285.     void zap()
  286.     {
  287.         type = ST_EMPTY;
  288.         role = CR_DEFAULT;
  289.         isauthed = haswelcome = false;
  290.     }
  291. };
  292.  
  293. vector<client *> clients;
  294.  
  295. bool valid_client(int cn)
  296. {
  297.     return clients.inrange(cn) && clients[cn]->type != ST_EMPTY;
  298. }
  299.  
  300. struct ban
  301. {
  302.         ENetAddress address;
  303.         int millis;
  304. };
  305.  
  306. vector<ban> bans;
  307.  
  308. char *maplayout = NULL;
  309. int maplayout_factor;
  310.  
  311. struct worldstate
  312. {
  313.     enet_uint32 uses;
  314.     vector<uchar> positions, messages;
  315. };
  316.  
  317. vector<worldstate *> worldstates;
  318.  
  319. void cleanworldstate(ENetPacket *packet)
  320. {
  321.    loopv(worldstates)
  322.    {
  323.        worldstate *ws = worldstates[i];
  324.        if(ws->positions.inbuf(packet->data) || ws->messages.inbuf(packet->data)) ws->uses--;
  325.        else continue;
  326.        if(!ws->uses)
  327.        {
  328.            delete ws;
  329.            worldstates.remove(i);
  330.        }
  331.        break;
  332.    }
  333. }
  334.  
  335. int bsend = 0, brec = 0, laststatus = 0, servmillis = 0, lastfillup = 0;
  336.  
  337. void recordpacket(int chan, void *data, int len);
  338.  
  339. void sendpacket(int n, int chan, ENetPacket *packet, int exclude = -1)
  340. {
  341.     if(n<0)
  342.     {
  343.         recordpacket(chan, packet->data, (int)packet->dataLength);
  344.         loopv(clients) if(i!=exclude && (clients[i]->type!=ST_TCPIP || clients[i]->isauthed)) sendpacket(i, chan, packet);
  345.         return;
  346.     }
  347.     switch(clients[n]->type)
  348.     {
  349.         case ST_TCPIP:
  350.         {
  351.             enet_peer_send(clients[n]->peer, chan, packet);
  352.             bsend += (int)packet->dataLength;
  353.             break;
  354.         }
  355.  
  356.         case ST_LOCAL:
  357.             localservertoclient(chan, packet->data, (int)packet->dataLength);
  358.             break;
  359.     }
  360. }
  361.  
  362. static bool reliablemessages = false;
  363.  
  364. bool buildworldstate()
  365. {
  366.     static struct { int posoff, msgoff, msglen; } pkt[MAXCLIENTS];
  367.     worldstate &ws = *new worldstate;
  368.     loopv(clients)
  369.     {
  370.         client &c = *clients[i];
  371.         if(c.type!=ST_TCPIP || !c.isauthed) continue;
  372.         if(c.position.empty()) pkt[i].posoff = -1;
  373.         else
  374.         {
  375.             pkt[i].posoff = ws.positions.length();
  376.             loopvj(c.position) ws.positions.add(c.position[j]);
  377.         }
  378.         if(c.messages.empty()) pkt[i].msgoff = -1;
  379.         else
  380.         {
  381.             pkt[i].msgoff = ws.messages.length();
  382.             ucharbuf p = ws.messages.reserve(16);
  383.             putint(p, SV_CLIENT);
  384.             putint(p, c.clientnum);
  385.             putuint(p, c.messages.length());
  386.             ws.messages.addbuf(p);
  387.             loopvj(c.messages) ws.messages.add(c.messages[j]);
  388.             pkt[i].msglen = ws.messages.length()-pkt[i].msgoff;
  389.         }
  390.     }
  391.     int psize = ws.positions.length(), msize = ws.messages.length();
  392.     if(psize)
  393.     {
  394.         recordpacket(0, ws.positions.getbuf(), psize);
  395.         ucharbuf p = ws.positions.reserve(psize);
  396.         p.put(ws.positions.getbuf(), psize);
  397.         ws.positions.addbuf(p);
  398.     }
  399.     if(msize)
  400.     {
  401.         recordpacket(1, ws.messages.getbuf(), msize);
  402.         ucharbuf p = ws.messages.reserve(msize);
  403.         p.put(ws.messages.getbuf(), msize);
  404.         ws.messages.addbuf(p);
  405.     }
  406.     ws.uses = 0;
  407.     loopv(clients)
  408.     {
  409.         client &c = *clients[i];
  410.         if(c.type!=ST_TCPIP || !c.isauthed) continue;
  411.         ENetPacket *packet;
  412.         if(psize && (pkt[i].posoff<0 || psize-c.position.length()>0))
  413.         {
  414.             packet = enet_packet_create(&ws.positions[pkt[i].posoff<0 ? 0 : pkt[i].posoff+c.position.length()],
  415.                                         pkt[i].posoff<0 ? psize : psize-c.position.length(),
  416.                                         ENET_PACKET_FLAG_NO_ALLOCATE);
  417.             sendpacket(c.clientnum, 0, packet);
  418.             if(!packet->referenceCount) enet_packet_destroy(packet);
  419.             else { ++ws.uses; packet->freeCallback = cleanworldstate; }
  420.         }
  421.         c.position.setsizenodelete(0);
  422.  
  423.         if(msize && (pkt[i].msgoff<0 || msize-pkt[i].msglen>0))
  424.         {
  425.             packet = enet_packet_create(&ws.messages[pkt[i].msgoff<0 ? 0 : pkt[i].msgoff+pkt[i].msglen],
  426.                                         pkt[i].msgoff<0 ? msize : msize-pkt[i].msglen,
  427.                                         (reliablemessages ? ENET_PACKET_FLAG_RELIABLE : 0) | ENET_PACKET_FLAG_NO_ALLOCATE);
  428.             sendpacket(c.clientnum, 1, packet);
  429.             if(!packet->referenceCount) enet_packet_destroy(packet);
  430.             else { ++ws.uses; packet->freeCallback = cleanworldstate; }
  431.         }
  432.         c.messages.setsizenodelete(0);
  433.     }
  434.     reliablemessages = false;
  435.     if(!ws.uses)
  436.     {
  437.         delete &ws;
  438.         return false;
  439.     }
  440.     else
  441.     {
  442.         worldstates.add(&ws);
  443.         return true;
  444.     }
  445. }
  446.  
  447. int countclients(int type, bool exclude = false)
  448. {
  449.     int num = 0;
  450.     loopv(clients) if((clients[i]->type!=type)==exclude) num++;
  451.     return num;
  452. }
  453.  
  454. int numclients() { return countclients(ST_EMPTY, true); }
  455. int numlocalclients() { return countclients(ST_LOCAL); }
  456. int numnonlocalclients() { return countclients(ST_TCPIP); }
  457.  
  458. int numauthedclients()
  459. {
  460.     int num = 0;
  461.     loopv(clients) if(clients[i]->type!=ST_EMPTY && clients[i]->isauthed) num++;
  462.     return num;
  463. }
  464.  
  465. int calcscores();
  466.  
  467. int freeteam(int pl = -1)
  468. {
  469.         int teamsize[2] = {0, 0};
  470.         int teamscore[2] = {0, 0};
  471.         int t;
  472.     int sum = calcscores();
  473.         loopv(clients) if(clients[i]->type!=ST_EMPTY && i != pl && clients[i]->isauthed)
  474.         {
  475.             t = team_int(clients[i]->team);
  476.             teamsize[t]++;
  477.         teamscore[t] += clients[i]->at3_score;
  478.         }
  479.         if(teamsize[0] == teamsize[1])
  480.         {
  481.         return sum > 200 ? (teamscore[0] < teamscore[1] ? 0 : 1) : rnd(2);
  482.         }
  483.         return teamsize[0] < teamsize[1] ? 0 : 1;
  484. }
  485.  
  486. int findcnbyaddress(ENetAddress *address)
  487. {
  488.     loopv(clients)
  489.     {
  490.         if(clients[i]->type == ST_TCPIP && clients[i]->peer->address.host == address->host && clients[i]->peer->address.port == address->port)
  491.             return i;
  492.     }
  493.     return -1;
  494. }
  495.  
  496. savedscore *findscore(client &c, bool insert)
  497. {
  498.     if(c.type!=ST_TCPIP) return NULL;
  499.     if(!insert)
  500.     {
  501.         loopv(clients)
  502.         {
  503.             client &o = *clients[i];
  504.             if(o.type!=ST_TCPIP) continue;
  505.             if(o.clientnum!=c.clientnum && o.peer->address.host==c.peer->address.host && !strcmp(o.name, c.name))
  506.             {
  507.                 static savedscore curscore;
  508.                 curscore.save(o.state);
  509.                 return &curscore;
  510.             }
  511.         }
  512.     }
  513.     loopv(scores)
  514.     {
  515.         savedscore &sc = scores[i];
  516.         if(!strcmp(sc.name, c.name) && sc.ip==c.peer->address.host) return &sc;
  517.     }
  518.     if(!insert) return NULL;
  519.     savedscore &sc = scores.add();
  520.     s_strcpy(sc.name, c.name);
  521.     sc.ip = c.peer->address.host;
  522.     return &sc;
  523. }
  524.  
  525. struct server_entity            // server side version of "entity" type
  526. {
  527.     int type;
  528.     bool spawned, hascoord;
  529.     int spawntime;
  530.     short x, y;
  531. };
  532.  
  533. vector<server_entity> sents;
  534.  
  535. bool notgotitems = true;        // true when map has changed and waiting for clients to send item
  536. int clnumspawn[3], clnumflagspawn[2];
  537.  
  538. void restoreserverstate(vector<entity> &ents)   // hack: called from savegame code, only works in SP
  539. {
  540.     loopv(sents)
  541.     {
  542.         sents[i].spawned = ents[i].spawned;
  543.         sents[i].spawntime = 0;
  544.     }
  545. }
  546.  
  547. static int interm = 0, minremain = 0, gamemillis = 0, gamelimit = 0;
  548. static bool mapreload = false, autoteam = true, forceintermission = false;
  549.  
  550. string servdesc_current;
  551. ENetAddress servdesc_caller;
  552. bool custom_servdesc = false;
  553.  
  554. bool isdedicated = false;
  555. ENetHost *serverhost = NULL;
  556.  
  557. void process(ENetPacket *packet, int sender, int chan);
  558. void welcomepacket(ucharbuf &p, int n, ENetPacket *packet, bool forcedeath = false);
  559. void sendwelcome(client *cl, int chan = 1, bool forcedeath = false);
  560.  
  561. void sendf(int cn, int chan, const char *format, ...)
  562. {
  563.     int exclude = -1;
  564.     bool reliable = false;
  565.     if(*format=='r') { reliable = true; ++format; }
  566.     ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, reliable ? ENET_PACKET_FLAG_RELIABLE : 0);
  567.     ucharbuf p(packet->data, packet->dataLength);
  568.     va_list args;
  569.     va_start(args, format);
  570.     while(*format) switch(*format++)
  571.     {
  572.         case 'x':
  573.             exclude = va_arg(args, int);
  574.             break;
  575.  
  576.         case 'v':
  577.         {
  578.             int n = va_arg(args, int);
  579.             int *v = va_arg(args, int *);
  580.             loopi(n) putint(p, v[i]);
  581.             break;
  582.         }
  583.  
  584.         case 'i':
  585.         {
  586.             int n = isdigit(*format) ? *format++-'0' : 1;
  587.             loopi(n) putint(p, va_arg(args, int));
  588.             break;
  589.         }
  590.         case 's': sendstring(va_arg(args, const char *), p); break;
  591.         case 'm':
  592.         {
  593.             int n = va_arg(args, int);
  594.             enet_packet_resize(packet, packet->dataLength+n);
  595.             p.buf = packet->data;
  596.             p.maxlen += n;
  597.             p.put(va_arg(args, uchar *), n);
  598.             break;
  599.         }
  600.     }
  601.     va_end(args);
  602.     enet_packet_resize(packet, p.length());
  603.     sendpacket(cn, chan, packet, exclude);
  604.     if(packet->referenceCount==0) enet_packet_destroy(packet);
  605. }
  606.  
  607. void sendservmsg(const char *msg, int client=-1)
  608. {
  609.     sendf(client, 1, "ris", SV_SERVMSG, msg);
  610. }
  611.  
  612. void spawnstate(client *c)
  613. {
  614.     clientstate &gs = c->state;
  615.     gs.spawnstate(smode);
  616.     gs.lifesequence++;
  617. }
  618.  
  619. void sendspawn(client *c)
  620. {
  621.     clientstate &gs = c->state;
  622.     spawnstate(c);
  623.     sendf(c->clientnum, 1, "ri7vv", SV_SPAWNSTATE, gs.lifesequence,
  624.         gs.health, gs.armour,
  625.         gs.primary, gs.gunselect, m_arena ? c->spawnindex : -1,
  626.         NUMGUNS, gs.ammo, NUMGUNS, gs.mag);
  627.     gs.lastspawn = gamemillis;
  628. }
  629.  
  630. // demo
  631.  
  632. struct demofile
  633. {
  634.     string info;
  635.     uchar *data;
  636.     int len;
  637. };
  638.  
  639. vector<demofile> demos;
  640.  
  641. bool demonextmatch = false;
  642. FILE *demotmp = NULL;
  643. gzFile demorecord = NULL, demoplayback = NULL;
  644. bool recordpackets = false;
  645. int nextplayback = 0;
  646.  
  647. void writedemo(int chan, void *data, int len)
  648. {
  649.     if(!demorecord) return;
  650.     int stamp[3] = { gamemillis, chan, len };
  651.     endianswap(stamp, sizeof(int), 3);
  652.     gzwrite(demorecord, stamp, sizeof(stamp));
  653.     gzwrite(demorecord, data, len);
  654. }
  655.  
  656. void recordpacket(int chan, void *data, int len)
  657. {
  658.     if(recordpackets) writedemo(chan, data, len);
  659. }
  660.  
  661. void enddemorecord()
  662. {
  663.     if(!demorecord) return;
  664.  
  665.     gzclose(demorecord);
  666.     recordpackets = false;
  667.     demorecord = NULL;
  668.  
  669. #ifdef WIN32
  670.     demotmp = fopen(path("demos/demorecord", true), "rb");
  671. #endif
  672.     if(!demotmp) return;
  673.  
  674.     fseek(demotmp, 0, SEEK_END);
  675.     int len = ftell(demotmp);
  676.     rewind(demotmp);
  677.     if(demos.length() >= scl.maxdemos)
  678.     {
  679.         delete[] demos[0].data;
  680.         demos.remove(0);
  681.     }
  682.     demofile &d = demos.add();
  683.     s_sprintf(d.info)("%s: %s, %s, %.2f%s", asctime(), modestr(gamemode), smapname, len > 1024*1024 ? len/(1024*1024.f) : len/1024.0f, len > 1024*1024 ? "MB" : "kB");
  684.     s_sprintfd(msg)("Demo \"%s\" recorded\nPress F10 to download it from the server..", d.info);
  685.     sendservmsg(msg);
  686.     logline(ACLOG_INFO, "Demo \"%s\" recorded.", d.info);
  687.     d.data = new uchar[len];
  688.     d.len = len;
  689.     fread(d.data, 1, len, demotmp);
  690.     fclose(demotmp);
  691.     demotmp = NULL;
  692.     if(scl.demopath[0])
  693.     {
  694.         s_sprintf(msg)("%s%s_%s_%s.dmo", scl.demopath, timestring(), behindpath(smapname), modestr(gamemode, true));
  695.         path(msg);
  696.         FILE *demo = openfile(msg, "wb");
  697.         if(demo)
  698.         {
  699.             int wlen = (int) fwrite(d.data, 1, d.len, demo);
  700.             fclose(demo);
  701.             logline(ACLOG_INFO, "demo written to file \"%s\" (%d bytes)", msg, wlen);
  702.         }
  703.         else
  704.         {
  705.             logline(ACLOG_INFO, "failed to write demo to file \"%s\"", msg);
  706.         }
  707.     }
  708. }
  709.  
  710. void setupdemorecord()
  711. {
  712.     if(numlocalclients() || !m_mp(gamemode) || gamemode==1) return;
  713.  
  714. #ifdef WIN32
  715.     gzFile f = gzopen(path("demos/demorecord", true), "wb9");
  716.     if(!f) return;
  717. #else
  718.     demotmp = tmpfile();
  719.     if(!demotmp) return;
  720.     setvbuf(demotmp, NULL, _IONBF, 0);
  721.  
  722.     gzFile f = gzdopen(_dup(_fileno(demotmp)), "wb9");
  723.     if(!f)
  724.     {
  725.         fclose(demotmp);
  726.         demotmp = NULL;
  727.         return;
  728.     }
  729. #endif
  730.  
  731.     sendservmsg("recording demo");
  732.     logline(ACLOG_INFO, "Demo recording started.");
  733.  
  734.     demorecord = f;
  735.     recordpackets = false;
  736.  
  737.     demoheader hdr;
  738.     memcpy(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic));
  739.     hdr.version = DEMO_VERSION;
  740.     hdr.protocol = SERVER_PROTOCOL_VERSION;
  741.     endianswap(&hdr.version, sizeof(int), 1);
  742.     endianswap(&hdr.protocol, sizeof(int), 1);
  743.     memset(hdr.desc, 0, DHDR_DESCCHARS);
  744.     s_sprintfd(desc)("%s, %s, %s %s", modestr(gamemode, false), behindpath(smapname), asctime(), servdesc_current);
  745.     if(strlen(desc) > DHDR_DESCCHARS)
  746.         s_sprintf(desc)("%s, %s, %s %s", modestr(gamemode, true), behindpath(smapname), asctime(), servdesc_current);
  747.     desc[DHDR_DESCCHARS - 1] = '\0';
  748.     strcpy(hdr.desc, desc);
  749.     gzwrite(demorecord, &hdr, sizeof(demoheader));
  750.  
  751.     ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
  752.     ucharbuf p(packet->data, packet->dataLength);
  753.     welcomepacket(p, -1, packet);
  754.     writedemo(1, p.buf, p.len);
  755.     enet_packet_destroy(packet);
  756.  
  757.     uchar buf[MAXTRANS];
  758.     loopv(clients)
  759.     {
  760.         client *ci = clients[i];
  761.         if(ci->type==ST_EMPTY) continue;
  762.  
  763.         uchar header[16];
  764.         ucharbuf q(&buf[sizeof(header)], sizeof(buf)-sizeof(header));
  765.         putint(q, SV_INITC2S);
  766.         sendstring(ci->name, q);
  767.         sendstring(ci->team, q);
  768.         putint(q, ci->skin);
  769.  
  770.         ucharbuf h(header, sizeof(header));
  771.         putint(h, SV_CLIENT);
  772.         putint(h, ci->clientnum);
  773.         putuint(h, q.len);
  774.  
  775.         memcpy(&buf[sizeof(header)-h.len], header, h.len);
  776.  
  777.         writedemo(1, &buf[sizeof(header)-h.len], h.len+q.len);
  778.     }
  779. }
  780.  
  781. void listdemos(int cn)
  782. {
  783.     ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
  784.     if(!packet) return;
  785.     ucharbuf p(packet->data, packet->dataLength);
  786.     putint(p, SV_SENDDEMOLIST);
  787.     putint(p, demos.length());
  788.     loopv(demos) sendstring(demos[i].info, p);
  789.     enet_packet_resize(packet, p.length());
  790.     sendpacket(cn, 1, packet);
  791.     if(!packet->referenceCount) enet_packet_destroy(packet);
  792. }
  793.  
  794. static void cleardemos(int n)
  795. {
  796.     if(!n)
  797.     {
  798.         loopv(demos) delete[] demos[i].data;
  799.         demos.setsize(0);
  800.         sendservmsg("cleared all demos");
  801.     }
  802.     else if(demos.inrange(n-1))
  803.     {
  804.         delete[] demos[n-1].data;
  805.         demos.remove(n-1);
  806.         s_sprintfd(msg)("cleared demo %d", n);
  807.         sendservmsg(msg);
  808.     }
  809. }
  810.  
  811. void senddemo(int cn, int num)
  812. {
  813.     if(!num) num = demos.length();
  814.     if(!demos.inrange(num-1))
  815.     {
  816.         if(demos.empty()) sendservmsg("no demos available", cn);
  817.         else
  818.         {
  819.             s_sprintfd(msg)("no demo %d available", num);
  820.             sendservmsg(msg, cn);
  821.         }
  822.         return;
  823.     }
  824.     demofile &d = demos[num-1];
  825.     sendf(cn, 2, "rim", SV_SENDDEMO, d.len, d.data);
  826. }
  827.  
  828. void enddemoplayback()
  829. {
  830.     if(!demoplayback) return;
  831.     gzclose(demoplayback);
  832.     demoplayback = NULL;
  833.  
  834.     loopv(clients) sendf(i, 1, "ri3", SV_DEMOPLAYBACK, 0, i);
  835.  
  836.     sendservmsg("demo playback finished");
  837.  
  838.     loopv(clients) sendwelcome(clients[i]);
  839. }
  840.  
  841. void setupdemoplayback()
  842. {
  843.     demoheader hdr;
  844.     string msg;
  845.     msg[0] = '\0';
  846.     s_sprintfd(file)("demos/%s.dmo", smapname);
  847.     path(file);
  848.     demoplayback = opengzfile(file, "rb9");
  849.     if(!demoplayback) s_sprintf(msg)("could not read demo \"%s\"", file);
  850.     else if(gzread(demoplayback, &hdr, sizeof(demoheader))!=sizeof(demoheader) || memcmp(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic)))
  851.         s_sprintf(msg)("\"%s\" is not a demo file", file);
  852.     else
  853.     {
  854.         endianswap(&hdr.version, sizeof(int), 1);
  855.         endianswap(&hdr.protocol, sizeof(int), 1);
  856.         if(hdr.version!=DEMO_VERSION) s_sprintf(msg)("demo \"%s\" requires an %s version of AssaultCube", file, hdr.version<DEMO_VERSION ? "older" : "newer");
  857.         else if(hdr.protocol != PROTOCOL_VERSION && !(hdr.protocol < 0 && hdr.protocol == -PROTOCOL_VERSION)) s_sprintf(msg)("demo \"%s\" requires an %s version of AssaultCube", file, hdr.protocol<PROTOCOL_VERSION ? "older" : "newer");
  858.     }
  859.     if(msg[0])
  860.     {
  861.         if(demoplayback) { gzclose(demoplayback); demoplayback = NULL; }
  862.         sendservmsg(msg);
  863.         return;
  864.     }
  865.  
  866.     s_sprintf(msg)("playing demo \"%s\"", file);
  867.     sendservmsg(msg);
  868.  
  869.     sendf(-1, 1, "ri3", SV_DEMOPLAYBACK, 1, -1);
  870.  
  871.     if(gzread(demoplayback, &nextplayback, sizeof(nextplayback))!=sizeof(nextplayback))
  872.     {
  873.         enddemoplayback();
  874.         return;
  875.     }
  876.     endianswap(&nextplayback, sizeof(nextplayback), 1);
  877. }
  878.  
  879. void readdemo()
  880. {
  881.     if(!demoplayback) return;
  882.     while(gamemillis>=nextplayback)
  883.     {
  884.         int chan, len;
  885.         if(gzread(demoplayback, &chan, sizeof(chan))!=sizeof(chan) ||
  886.            gzread(demoplayback, &len, sizeof(len))!=sizeof(len))
  887.         {
  888.             enddemoplayback();
  889.             return;
  890.         }
  891.         endianswap(&chan, sizeof(chan), 1);
  892.         endianswap(&len, sizeof(len), 1);
  893.         ENetPacket *packet = enet_packet_create(NULL, len, 0);
  894.         if(!packet || gzread(demoplayback, packet->data, len)!=len)
  895.         {
  896.             if(packet) enet_packet_destroy(packet);
  897.             enddemoplayback();
  898.             return;
  899.         }
  900.         sendpacket(-1, chan, packet);
  901.         if(!packet->referenceCount) enet_packet_destroy(packet);
  902.         if(gzread(demoplayback, &nextplayback, sizeof(nextplayback))!=sizeof(nextplayback))
  903.         {
  904.             enddemoplayback();
  905.             return;
  906.         }
  907.         endianswap(&nextplayback, sizeof(nextplayback), 1);
  908.     }
  909. }
  910.  
  911. //
  912.  
  913. struct sflaginfo
  914. {
  915.     int state;
  916.     int actor_cn;
  917.     float pos[3];
  918.     int lastupdate;
  919.     int stolentime;
  920.     short x, y;          // flag entity location
  921. } sflaginfos[2];
  922.  
  923. void putflaginfo(ucharbuf &p, int flag)
  924. {
  925.     sflaginfo &f = sflaginfos[flag];
  926.     putint(p, SV_FLAGINFO);
  927.     putint(p, flag);
  928.     putint(p, f.state);
  929.     switch(f.state)
  930.     {
  931.         case CTFF_STOLEN:
  932.             putint(p, f.actor_cn);
  933.             break;
  934.         case CTFF_DROPPED:
  935.             loopi(3) putuint(p, (int)(f.pos[i]*DMF));
  936.             break;
  937.     }
  938. }
  939.  
  940. bool flagdistance(sflaginfo &f, int cn)
  941. {
  942.         if(!valid_client(cn)) return false;
  943.         client &c = *clients[cn];
  944.     vec v(-1, -1, c.state.o.z);
  945.     switch(f.state)
  946.     {
  947.         case CTFF_INBASE:
  948.             v.x = f.x; v.y = f.y;
  949.             break;
  950.         case CTFF_DROPPED:
  951.             v.x = f.pos[0]; v.y = f.pos[1];
  952.             break;
  953.     }
  954.     if(v.x < 0) return true;
  955.     float dist = c.state.o.dist(v);
  956.     if(dist > 10)                // <2.5 would be normal, LAG may increase the value
  957.     {
  958.         c.farpickups++;
  959.         logline(ACLOG_INFO, "[%s] %s touched the %s flag at distance %.2f (%d)", c.hostname, c.name, team_string(&f == sflaginfos + 1), dist, c.farpickups);
  960.         return false;
  961.     }
  962.     return true;
  963. }
  964.  
  965. void sendflaginfo(int flag = -1, int cn = -1)
  966. {
  967.     ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
  968.     ucharbuf p(packet->data, packet->dataLength);
  969.     if(flag >= 0) putflaginfo(p, flag);
  970.     else loopi(2) putflaginfo(p, i);
  971.     enet_packet_resize(packet, p.length());
  972.     sendpacket(cn, 1, packet);
  973.     if(packet->referenceCount==0) enet_packet_destroy(packet);
  974. }
  975.  
  976. void flagmessage(int flag, int message, int actor, int cn = -1)
  977. {
  978.     if(message == FM_KTFSCORE)
  979.         sendf(cn, 1, "riiiii", SV_FLAGMSG, flag, message, actor, (gamemillis - sflaginfos[flag].stolentime) / 1000);
  980.     else
  981.         sendf(cn, 1, "riiii", SV_FLAGMSG, flag, message, actor);
  982. }
  983.  
  984. void flagaction(int flag, int action, int actor)
  985. {
  986.     if(!valid_flag(flag)) return;
  987.         sflaginfo &f = sflaginfos[flag];
  988.         sflaginfo &of = sflaginfos[team_opposite(flag)];
  989.         bool deadactor = valid_client(actor) ? clients[actor]->state.state != CS_ALIVE : true;
  990.     int score = 0;
  991.     int message = -1;
  992.  
  993.     if(m_ctf || m_htf)
  994.     {
  995.         switch(action)
  996.         {
  997.             case FA_PICKUP:  // ctf: f = enemy team    htf: f = own team
  998.             {
  999.                 if(deadactor || f.state == CTFF_STOLEN) return;
  1000.                 flagdistance(f, actor);
  1001.                 int team = team_int(clients[actor]->team);
  1002.                 if(m_ctf) team = team_opposite(team);
  1003.                 if(team != flag) return;
  1004.                 f.state = CTFF_STOLEN;
  1005.                 f.actor_cn = actor;
  1006.                 message = FM_PICKUP;
  1007.                 break;
  1008.             }
  1009.             case FA_LOST:
  1010.                 if(actor == -1) actor = f.actor_cn;
  1011.             case FA_DROP:
  1012.                 if(f.state!=CTFF_STOLEN || f.actor_cn != actor) return;
  1013.                 f.state = CTFF_DROPPED;
  1014.                 loopi(3) f.pos[i] = clients[actor]->state.o[i];
  1015.                 message = action == FA_LOST ? FM_LOST : FM_DROP;
  1016.                 break;
  1017.             case FA_RETURN:
  1018.                 if(f.state!=CTFF_DROPPED || m_htf) return;
  1019.                 f.state = CTFF_INBASE;
  1020.                 message = FM_RETURN;
  1021.                 break;
  1022.             case FA_SCORE:  // ctf: f = carried by actor flag,  htf: f = hunted flag (run over by actor)
  1023.                 if(m_ctf)
  1024.                 {
  1025.                     if(f.state != CTFF_STOLEN || f.actor_cn != actor || of.state != CTFF_INBASE) return;
  1026.                     flagdistance(of, actor);
  1027.                     score = 1;
  1028.                     message = FM_SCORE;
  1029.                 }
  1030.                 else // m_htf
  1031.                 {
  1032.                     if(f.state != CTFF_DROPPED) return;
  1033.                     flagdistance(f, actor);
  1034.                     score = (of.state == CTFF_STOLEN) ? 1 : 0;
  1035.                     message = score ? FM_SCORE : FM_SCOREFAIL;
  1036.                     if(of.actor_cn == actor) score *= 2;
  1037.                 }
  1038.                 f.state = CTFF_INBASE;
  1039.                 break;
  1040.  
  1041.             case FA_RESET:
  1042.                 f.state = CTFF_INBASE;
  1043.                 message = FM_RESET;
  1044.                 break;
  1045.         }
  1046.     }
  1047.     else if(m_ktf)  // f: active flag, of: idle flag
  1048.     {
  1049.         switch(action)
  1050.         {
  1051.             case FA_PICKUP:
  1052.                 if(deadactor || f.state != CTFF_INBASE) return;
  1053.                 flagdistance(f, actor);
  1054.                 f.state = CTFF_STOLEN;
  1055.                 f.actor_cn = actor;
  1056.                 f.stolentime = gamemillis;
  1057.                 message = FM_PICKUP;
  1058.                 break;
  1059.             case FA_SCORE:  // f = carried by actor flag
  1060.                 if(actor != -1 || f.state != CTFF_STOLEN) return; // no client msg allowed here
  1061.                 if(valid_client(f.actor_cn) && clients[f.actor_cn]->state.state == CS_ALIVE)
  1062.                 {
  1063.                     actor = f.actor_cn;
  1064.                     score = 1;
  1065.                     message = FM_KTFSCORE;
  1066.                     break;
  1067.                 }
  1068.             case FA_LOST:
  1069.                 if(actor == -1) actor = f.actor_cn;
  1070.             case FA_DROP:
  1071.                 if(f.actor_cn != actor || f.state != CTFF_STOLEN) return;
  1072.             case FA_RESET:
  1073.                 if(f.state == CTFF_STOLEN)
  1074.                 {
  1075.                     actor = f.actor_cn;
  1076.                     message = FM_LOST;
  1077.                 }
  1078.                 f.state = CTFF_IDLE;
  1079.                 of.state = CTFF_INBASE;
  1080.                 sendflaginfo(team_opposite(flag));
  1081.                 break;
  1082.         }
  1083.     }
  1084.     if(score)
  1085.     {
  1086.         clients[actor]->state.flagscore += score;
  1087.         sendf(-1, 1, "riii", SV_FLAGCNT, actor, clients[actor]->state.flagscore);
  1088.     }
  1089.     if(valid_client(actor))
  1090.     {
  1091.         client &c = *clients[actor];
  1092.         switch(message)
  1093.         {
  1094.             case FM_PICKUP:
  1095.                 logline(ACLOG_INFO,"[%s] %s stole the flag", c.hostname, c.name);
  1096.                 break;
  1097.             case FM_DROP:
  1098.             case FM_LOST:
  1099.                 logline(ACLOG_INFO,"[%s] %s %s the flag", c.hostname, c.name, message == FM_LOST ? "lost" : "dropped");
  1100.                 break;
  1101.             case FM_RETURN:
  1102.                 logline(ACLOG_INFO,"[%s] %s returned the flag", c.hostname, c.name);
  1103.                 break;
  1104.             case FM_SCORE:
  1105.                 logline(ACLOG_INFO, "[%s] %s scored with the flag for %s, new score %d", c.hostname, c.name, c.team, c.state.flagscore);
  1106.                 break;
  1107.             case FM_KTFSCORE:
  1108.                 logline(ACLOG_INFO, "[%s] %s scored, carrying for %d seconds, new score %d", c.hostname, c.name, (gamemillis - f.stolentime) / 1000, c.state.flagscore);
  1109.                 break;
  1110.             case FM_SCOREFAIL:
  1111.                 logline(ACLOG_INFO, "[%s] %s failed to score", c.hostname, c.name);
  1112.                 break;
  1113.             default:
  1114.                 logline(ACLOG_INFO, "flagaction %d, actor %d, flag %d, message %d", action, actor, flag, message);
  1115.                 break;
  1116.         }
  1117.     }
  1118.     else
  1119.     {
  1120.         switch(message)
  1121.         {
  1122.             case FM_RESET:
  1123.                 logline(ACLOG_INFO,"the server reset the flag for team %s", team_string(flag));
  1124.                 break;
  1125.             default:
  1126.                 logline(ACLOG_INFO, "flagaction %d with invalid actor cn %d, flag %d, message %d", action, actor, flag, message);
  1127.                 break;
  1128.         }
  1129.     }
  1130.  
  1131.         f.lastupdate = gamemillis;
  1132.         sendflaginfo(flag);
  1133.         if(message >= 0)
  1134.         flagmessage(flag, message, valid_client(actor) ? actor : -1);
  1135. }
  1136.  
  1137. int clienthasflag(int cn)
  1138. {
  1139.     if(m_flags && valid_client(cn))
  1140.     {
  1141.         loopi(2) { if(sflaginfos[i].state==CTFF_STOLEN && sflaginfos[i].actor_cn==cn) return i; }
  1142.     }
  1143.     return -1;
  1144. }
  1145.  
  1146. void ctfreset()
  1147. {
  1148.     int idleflag = m_ktf ? rnd(2) : -1;
  1149.     loopi(2)
  1150.     {
  1151.         sflaginfos[i].actor_cn = -1;
  1152.         sflaginfos[i].state = i == idleflag ? CTFF_IDLE : CTFF_INBASE;
  1153.         sflaginfos[i].lastupdate = -1;
  1154.     }
  1155. }
  1156.  
  1157. void sdropflag(int cn)
  1158. {
  1159.     int fl = clienthasflag(cn);
  1160.     if(fl >= 0) flagaction(fl, FA_LOST, cn);
  1161. }
  1162.  
  1163. void resetflag(int cn)
  1164. {
  1165.     int fl = clienthasflag(cn);
  1166.     if(fl >= 0) flagaction(fl, FA_RESET, -1);
  1167. }
  1168.  
  1169. void htf_forceflag(int flag)
  1170. {
  1171.     sflaginfo &f = sflaginfos[flag];
  1172.     int besthealth = 0, numbesthealth = 0;
  1173.     loopv(clients) if(clients[i]->type!=ST_EMPTY)
  1174.     {
  1175.         if(clients[i]->state.state == CS_ALIVE && team_int(clients[i]->team) == flag)
  1176.         {
  1177.             if(clients[i]->state.health == besthealth)
  1178.                 numbesthealth++;
  1179.             else
  1180.             {
  1181.                 if(clients[i]->state.health > besthealth)
  1182.                 {
  1183.                     besthealth = clients[i]->state.health;
  1184.                     numbesthealth = 1;
  1185.                 }
  1186.             }
  1187.         }
  1188.     }
  1189.     if(numbesthealth)
  1190.     {
  1191.         int pick = rnd(numbesthealth);
  1192.         loopv(clients) if(clients[i]->type!=ST_EMPTY)
  1193.         {
  1194.             if(clients[i]->state.state == CS_ALIVE && team_int(clients[i]->team) == flag && --pick < 0)
  1195.             {
  1196.                 f.state = CTFF_STOLEN;
  1197.                 f.actor_cn = i;
  1198.                 sendflaginfo(flag);
  1199.                 flagmessage(flag, FM_PICKUP, i);
  1200.                 logline(ACLOG_INFO,"[%s] %s got forced to pickup the flag", clients[i]->hostname, clients[i]->name);
  1201.                 break;
  1202.             }
  1203.         }
  1204.     }
  1205.     f.lastupdate = gamemillis;
  1206. }
  1207.  
  1208.  
  1209. bool canspawn(client *c, bool connecting = false)
  1210. {
  1211.     if(m_arena)
  1212.     {
  1213.         if(connecting && numauthedclients()<=2) return true;
  1214.         return false;
  1215.     }
  1216.     return true;
  1217. }
  1218.  
  1219. int arenaround = 0;
  1220.  
  1221. struct twoint { int index, value; };
  1222. int cmpscore(const int *a, const int *b) { return clients[*a]->at3_score - clients[*b]->at3_score; }
  1223. int cmptwoint(const struct twoint *a, const struct twoint *b) { return a->value - b->value; }
  1224. ivector tdistrib;
  1225. vector<twoint> sdistrib;
  1226.  
  1227. void distributeteam(int team)
  1228. {
  1229.     int numsp = team == 100 ? clnumspawn[2] : clnumspawn[team];
  1230.     if(!numsp) numsp = 30; // no map data yet: make a guess
  1231.     twoint ti;
  1232.     tdistrib.setsize(0);
  1233.     loopv(clients) if(clients[i]->type!=ST_EMPTY)
  1234.     {
  1235.         if(team == 100 || team == team_int(clients[i]->team))
  1236.         {
  1237.             tdistrib.add(i);
  1238.             clients[i]->at3_score = rand();
  1239.         }
  1240.     }
  1241.     tdistrib.sort(cmpscore); // random player order
  1242.     sdistrib.setsize(0);
  1243.     loopi(numsp)
  1244.     {
  1245.         ti.index = i;
  1246.         ti.value = rand();
  1247.         sdistrib.add(ti);
  1248.     }
  1249.     sdistrib.sort(cmptwoint); // random spawn order
  1250.     int x = 0;
  1251.     loopv(tdistrib)
  1252.     {
  1253.         clients[tdistrib[i]]->spawnindex = sdistrib[x++].index;
  1254.         x %= sdistrib.length();
  1255.     }
  1256. }
  1257.  
  1258. void distributespawns()
  1259. {
  1260.     loopv(clients) if(clients[i]->type!=ST_EMPTY)
  1261.     {
  1262.         clients[i]->spawnindex = -1;
  1263.     }
  1264.     if(m_teammode)
  1265.     {
  1266.         distributeteam(0);
  1267.         distributeteam(1);
  1268.     }
  1269.     else
  1270.     {
  1271.         distributeteam(100);
  1272.     }
  1273. }
  1274.  
  1275. void arenacheck()
  1276. {
  1277.     if(!m_arena || interm || gamemillis<arenaround || clients.empty()) return;
  1278.  
  1279.     if(arenaround)
  1280.     {   // start new arena round
  1281.         arenaround = 0;
  1282.         distributespawns();
  1283.         loopv(clients) if(clients[i]->type!=ST_EMPTY && clients[i]->isauthed)
  1284.         {
  1285.             clients[i]->state.respawn();
  1286.             sendspawn(clients[i]);
  1287.         }
  1288.         return;
  1289.     }
  1290.  
  1291. #ifndef STANDALONE
  1292.     if(m_botmode && clients[0]->type==ST_LOCAL)
  1293.     {
  1294.         bool alive = false, dead = false;
  1295.         loopv(players) if(players[i])
  1296.         {
  1297.             if(players[i]->state==CS_DEAD) dead = true;
  1298.             else alive = true;
  1299.         }
  1300.         if((dead && !alive) || player1->state==CS_DEAD)
  1301.         {
  1302.             sendf(-1, 1, "ri2", SV_ARENAWIN, player1->state==CS_ALIVE ? getclientnum() : (alive ? -2 : -1));
  1303.             arenaround = gamemillis+5000;
  1304.         }
  1305.         return;
  1306.     }
  1307. #endif
  1308.     client *alive = NULL;
  1309.     bool dead = false;
  1310.     int lastdeath = 0;
  1311.     loopv(clients)
  1312.     {
  1313.         client &c = *clients[i];
  1314.         if(c.type==ST_EMPTY || !c.isauthed) continue;
  1315.         if(c.state.state==CS_ALIVE || (c.state.state==CS_DEAD && c.state.lastspawn>=0))
  1316.         {
  1317.             if(!alive) alive = &c;
  1318.             else if(!m_teammode || strcmp(alive->team, c.team)) return;
  1319.         }
  1320.         else if(c.state.state==CS_DEAD)
  1321.         {
  1322.             dead = true;
  1323.             lastdeath = max(lastdeath, c.state.lastdeath);
  1324.         }
  1325.     }
  1326.  
  1327.     if(!dead || gamemillis < lastdeath + 500) return;
  1328.     sendf(-1, 1, "ri2", SV_ARENAWIN, alive ? alive->clientnum : -1);
  1329.     arenaround = gamemillis+5000;
  1330.     if(autoteam && m_teammode) refillteams(true);
  1331. }
  1332.  
  1333. #define SPAMREPEATINTERVAL  20   // detect doubled lines only if interval < 20 seconds
  1334. #define SPAMMAXREPEAT       3    // 4th time is SPAM
  1335. #define SPAMCHARPERMINUTE   220  // good typist
  1336. #define SPAMCHARINTERVAL    30   // allow 20 seconds typing at maxspeed
  1337.  
  1338. bool spamdetect(client *cl, char *text) // checks doubled lines and average typing speed
  1339. {
  1340.     if(cl->type != ST_TCPIP || cl->role == CR_ADMIN) return false;
  1341.     bool spam = false;
  1342.     int pause = servmillis - cl->lastsay;
  1343.     if(pause < 0 || pause > 90*1000) pause = 90*1000;
  1344.     cl->saychars -= (SPAMCHARPERMINUTE * pause) / (60*1000);
  1345.     cl->saychars += (int)strlen(text);
  1346.     if(cl->saychars < 0) cl->saychars = 0;
  1347.     if(text[0] && !strcmp(text, cl->lastsaytext) && servmillis - cl->lastsay < SPAMREPEATINTERVAL*1000)
  1348.     {
  1349.         spam = ++cl->spamcount > SPAMMAXREPEAT;
  1350.     }
  1351.     else
  1352.     {
  1353.          s_strcpy(cl->lastsaytext, text);
  1354.          cl->spamcount = 0;
  1355.     }
  1356.     cl->lastsay = servmillis;
  1357.     if(cl->saychars > (SPAMCHARPERMINUTE * SPAMCHARINTERVAL) / 60)
  1358.         spam = true;
  1359.     return spam;
  1360. }
  1361.  
  1362. void sendteamtext(char *text, int sender)
  1363. {
  1364.     if(!valid_client(sender) || !clients[sender]->team[0]) return;
  1365.     ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
  1366.     ucharbuf p(packet->data, packet->dataLength);
  1367.     putint(p, SV_TEAMTEXT);
  1368.     putint(p, sender);
  1369.     sendstring(text, p);
  1370.     enet_packet_resize(packet, p.length());
  1371.     loopv(clients) if(i!=sender)
  1372.     {
  1373.         if(!strcmp(clients[i]->team, clients[sender]->team) || !m_teammode) // send to everyone in non-team mode
  1374.             sendpacket(i, 1, packet);
  1375.     }
  1376.     if(packet->referenceCount==0) enet_packet_destroy(packet);
  1377. }
  1378.  
  1379. void sendvoicecomteam(int sound, int sender)
  1380. {
  1381.     if(!valid_client(sender) || !clients[sender]->team[0]) return;
  1382.     ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
  1383.     ucharbuf p(packet->data, packet->dataLength);
  1384.     putint(p, SV_VOICECOMTEAM);
  1385.     putint(p, sender);
  1386.     putint(p, sound);
  1387.     enet_packet_resize(packet, p.length());
  1388.     loopv(clients) if(i!=sender)
  1389.     {
  1390.         if(!strcmp(clients[i]->team, clients[sender]->team) || !m_teammode)
  1391.             sendpacket(i, 1, packet);
  1392.     }
  1393.     if(packet->referenceCount==0) enet_packet_destroy(packet);
  1394. }
  1395.  
  1396. void resetitems() { sents.setsize(0); notgotitems = true; }
  1397.  
  1398. int spawntime(int type)
  1399. {
  1400.     int np = numclients();
  1401.     np = np<3 ? 4 : (np>4 ? 2 : 3);         // spawn times are dependent on number of players
  1402.     int sec = 0;
  1403.     switch(type)
  1404.     {
  1405.         case I_CLIPS:
  1406.         case I_AMMO:
  1407.         case I_GRENADE: sec = np*2; break;
  1408.         case I_HEALTH: sec = np*5; break;
  1409.         case I_ARMOUR: sec = 20; break;
  1410.         case I_AKIMBO: sec = 60; break;
  1411.     }
  1412.     return sec*1000;
  1413. }
  1414.  
  1415. bool serverpickup(int i, int sender)         // server side item pickup, acknowledge first client that gets it
  1416. {
  1417.     const char *hn = sender >= 0 && clients[sender]->type == ST_TCPIP ? clients[sender]->hostname : NULL;
  1418.     if(!sents.inrange(i))
  1419.     {
  1420.         if(hn) logline(ACLOG_INFO, "[%s] tried to pick up entity #%d - doesn't exist on this map", hn, i);
  1421.         return false;
  1422.     }
  1423.     server_entity &e = sents[i];
  1424.     if(!e.spawned)
  1425.     {
  1426.         if(!e.spawntime && hn) logline(ACLOG_INFO, "[%s] tried to pick up entity #%d - can't be picked up in this gamemode or at all", hn, i);
  1427.         return false;
  1428.     }
  1429.     if(sender>=0)
  1430.     {
  1431.         client *cl = clients[sender];
  1432.         if(cl->type==ST_TCPIP)
  1433.         {
  1434.             if(cl->state.state!=CS_ALIVE || !cl->state.canpickup(e.type)) return false;
  1435.             if(e.hascoord)
  1436.             {
  1437.                 vec v(e.x, e.y, cl->state.o.z);
  1438.                 float dist = cl->state.o.dist(v);
  1439.                 if(dist > 10)                // <2.5 would be normal, LAG may increase the value
  1440.                 {
  1441.                     cl->farpickups++;
  1442.                     logline(ACLOG_INFO, "[%s] %s picked up entity type %d #%d, distance %.2f (%d)", cl->hostname, cl->name, e.type, i, dist, cl->farpickups);
  1443.                 }
  1444.             }
  1445.         }
  1446.         sendf(-1, 1, "ri3", SV_ITEMACC, i, sender);
  1447.         cl->state.pickup(sents[i].type);
  1448.     }
  1449.     e.spawned = false;
  1450.     e.spawntime = spawntime(e.type);
  1451.     return true;
  1452. }
  1453.  
  1454. void checkitemspawns(int diff)
  1455. {
  1456.     if(!diff) return;
  1457.     loopv(sents) if(sents[i].spawntime)
  1458.     {
  1459.         sents[i].spawntime -= diff;
  1460.         if(sents[i].spawntime<=0)
  1461.         {
  1462.             sents[i].spawntime = 0;
  1463.             sents[i].spawned = true;
  1464.             sendf(-1, 1, "ri2", SV_ITEMSPAWN, i);
  1465.         }
  1466.     }
  1467. }
  1468.  
  1469. void serverdamage(client *target, client *actor, int damage, int gun, bool gib, const vec &hitpush = vec(0, 0, 0))
  1470. {
  1471.     clientstate &ts = target->state;
  1472.     ts.dodamage(damage);
  1473.     actor->state.damage += damage != 1000 ? damage : 0;
  1474.     sendf(-1, 1, "ri6", gib ? SV_GIBDAMAGE : SV_DAMAGE, target->clientnum, actor->clientnum, damage, ts.armour, ts.health);
  1475.     if(target!=actor && !hitpush.iszero())
  1476.     {
  1477.         vec v(hitpush);
  1478.         if(!v.iszero()) v.normalize();
  1479.         sendf(target->clientnum, 1, "ri6", SV_HITPUSH, gun, damage,
  1480.             int(v.x*DNF), int(v.y*DNF), int(v.z*DNF));
  1481.     }
  1482.     if(ts.health<=0)
  1483.     {
  1484.         int targethasflag = clienthasflag(target->clientnum);
  1485.         bool tk = false, suic = false;
  1486.         target->state.deaths++;
  1487.         if(target!=actor)
  1488.         {
  1489.             if(!isteam(target->team, actor->team)) actor->state.frags += gib ? 2 : 1;
  1490.             else
  1491.             {
  1492.                 actor->state.frags--;
  1493.                 actor->state.teamkills++;
  1494.                 tk = true;
  1495.             }
  1496.         }
  1497.         else
  1498.         { // suicide
  1499.             actor->state.frags--;
  1500.             suic = true;
  1501.             logline(ACLOG_INFO, "[%s] %s suicided", actor->hostname, actor->name);
  1502.         }
  1503.         sendf(-1, 1, "ri4", gib ? SV_GIBDIED : SV_DIED, target->clientnum, actor->clientnum, actor->state.frags);
  1504.         if((suic || tk) && (m_htf || m_ktf) && targethasflag >= 0)
  1505.         {
  1506.             actor->state.flagscore--;
  1507.             sendf(-1, 1, "riii", SV_FLAGCNT, actor->clientnum, actor->state.flagscore);
  1508.         }
  1509.         target->position.setsizenodelete(0);
  1510.         ts.state = CS_DEAD;
  1511.         ts.lastdeath = gamemillis;
  1512.         if(!suic) logline(ACLOG_INFO, "[%s] %s %s%s %s", actor->hostname, actor->name, gib ? "gibbed" : "fragged", tk ? " his teammate" : "", target->name);
  1513.         if(m_flags && targethasflag >= 0)
  1514.         {
  1515.             if(m_ctf)
  1516.                 flagaction(targethasflag, tk ? FA_RESET : FA_LOST, -1);
  1517.             else if(m_htf)
  1518.                 flagaction(targethasflag, FA_LOST, -1);
  1519.             else // ktf || tktf
  1520.                 flagaction(targethasflag, FA_RESET, -1);
  1521.         }
  1522.         // don't issue respawn yet until DEATHMILLIS has elapsed
  1523.         // ts.respawn();
  1524.  
  1525.         if(isdedicated && actor->type == ST_TCPIP)
  1526.         {
  1527.             if(actor->state.frags < scl.banthreshold)
  1528.             {
  1529.                 ban b = { actor->peer->address, servmillis+20*60*1000 };
  1530.                 bans.add(b);
  1531.                 disconnect_client(actor->clientnum, DISC_AUTOBAN);
  1532.             }
  1533.             else if(actor->state.frags < scl.kickthreshold) disconnect_client(actor->clientnum, DISC_AUTOKICK);
  1534.         }
  1535.     }
  1536. }
  1537.  
  1538. #include "serverevents.h"
  1539.  
  1540. #define CONFIG_MAXPAR 6
  1541.  
  1542. struct configset
  1543. {
  1544.     string mapname;
  1545.     union
  1546.     {
  1547.         struct { int mode, time, vote, minplayer, maxplayer, skiplines; };
  1548.         int par[CONFIG_MAXPAR];
  1549.     };
  1550. };
  1551.  
  1552. vector<configset> configsets;
  1553. int curcfgset = -1;
  1554.  
  1555. char *loadcfgfile(char *cfg, const char *name, int *len)
  1556. {
  1557.     if(name && name[0])
  1558.     {
  1559.         s_strcpy(cfg, name);
  1560.         path(cfg);
  1561.     }
  1562.     char *p, *buf = loadfile(cfg, len);
  1563.     if(!buf)
  1564.     {
  1565.         if(name) logline(ACLOG_INFO,"could not read config file '%s'", name);
  1566.         return NULL;
  1567.     }
  1568.     if('\r' != '\n') // this is not a joke!
  1569.     {
  1570.         char c = strchr(buf, '\n') ? ' ' : '\n'; // in files without /n substitute /r with /n, otherwise remove /r
  1571.         for(p = buf; (p = strchr(p, '\r')); p++) *p = c;
  1572.     }
  1573.     for(p = buf; (p = strstr(p, "//")); ) // remove comments
  1574.     {
  1575.         while(*p != '\n' && *p != '\0') p++[0] = ' ';
  1576.     }
  1577.     for(p = buf; (p = strchr(p, '\t')); p++) *p = ' ';
  1578.     for(p = buf; (p = strchr(p, '\n')); p++) *p = '\0'; // one string per line
  1579.     return buf;
  1580. }
  1581.  
  1582. void readscfg(const char *name)
  1583. {
  1584.     static string cfgfilename;
  1585.     static int cfgfilesize;
  1586.     const char *sep = ": ";
  1587.     configset c;
  1588.     char *p, *l;
  1589.     int i, len, line = 0;
  1590.  
  1591.     if(!name && getfilesize(cfgfilename) == cfgfilesize) return;
  1592.     configsets.setsize(0);
  1593.     char *buf = loadcfgfile(cfgfilename, name, &len);
  1594.     cfgfilesize = len;
  1595.     if(!buf) return;
  1596.     p = buf;
  1597.     logline(ACLOG_VERBOSE,"reading map rotation '%s'", cfgfilename);
  1598.     while(p < buf + len)
  1599.     {
  1600.         l = p; p += strlen(p) + 1; line++;
  1601.         l = strtok(l, sep);
  1602.         if(l)
  1603.         {
  1604.             s_strcpy(c.mapname, behindpath(l));
  1605.             for(i = 3; i < CONFIG_MAXPAR; i++) c.par[i] = 0;  // default values
  1606.             for(i = 0; i < CONFIG_MAXPAR; i++)
  1607.             {
  1608.                 if((l = strtok(NULL, sep)) != NULL)
  1609.                     c.par[i] = atoi(l);
  1610.                 else
  1611.                     break;
  1612.             }
  1613.             if(i > 2)
  1614.             {
  1615.                 configsets.add(c);
  1616.                 logline(ACLOG_VERBOSE," %s, %s, %d minutes, vote:%d, minplayer:%d, maxplayer:%d, skiplines:%d", c.mapname, modestr(c.mode, false), c.time, c.vote, c.minplayer, c.maxplayer, c.skiplines);
  1617.             }
  1618.             else
  1619.             {
  1620.                 logline(ACLOG_INFO," error in line %d, file %s", line, cfgfilename);
  1621.             }
  1622.         }
  1623.     }
  1624.     delete[] buf;
  1625.     logline(ACLOG_INFO,"read %d map rotation entries from '%s'", configsets.length(), cfgfilename);
  1626. }
  1627.  
  1628. int cmpiprange(const struct iprange *a, const struct iprange *b)
  1629. {
  1630.     if(a->lr < b->lr) return -1;
  1631.     if(a->lr > b->lr) return 1;
  1632.     return 0;
  1633. }
  1634.  
  1635. int cmpipmatch(const struct iprange *a, const struct iprange *b) { return - (a->lr < b->lr) + (a->lr > b->ur); }
  1636.  
  1637. vector<iprange> ipblacklist;
  1638.  
  1639. void readipblacklist(const char *name)
  1640. {
  1641.     const enet_uint32 permaban[] = { 0x43b92a06, 0xcec10504 };
  1642.     static string blfilename;
  1643.     static int blfilesize;
  1644.     char *p, *l, *r;
  1645.     iprange ir;
  1646.     int len, line = 0, errors = 0;
  1647.  
  1648.     if(!name && getfilesize(blfilename) == blfilesize) return;
  1649.     ipblacklist.setsize(0);
  1650.     loopi(sizeof(permaban)/sizeof(permaban[0])) { ir.lr = ir.ur = permaban[i]; ipblacklist.add(ir); }
  1651.     char *buf = loadcfgfile(blfilename, name, &len);
  1652.     blfilesize = len;
  1653.     if(!buf) return;
  1654.     p = buf;
  1655.     logline(ACLOG_VERBOSE,"reading ip blacklist '%s'", blfilename);
  1656.     while(p < buf + len)
  1657.     {
  1658.         l = p; p += strlen(p) + 1; line++;
  1659.         if((r = (char *) atoipr(l, &ir)))
  1660.         {
  1661.             ipblacklist.add(ir);
  1662.             l = r;
  1663.         }
  1664.         if(l[strspn(l, " ")])
  1665.         {
  1666.             for(int i = strlen(l) - 1; i > 0 && l[i] == ' '; i--) l[i] = '\0';
  1667.             logline(ACLOG_INFO," error in line %d, file %s: ignored '%s'", line, blfilename, l);
  1668.             errors++;
  1669.         }
  1670.     }
  1671.     delete[] buf;
  1672.     ipblacklist.sort(cmpiprange);
  1673.     int orglength = ipblacklist.length();
  1674.     loopv(ipblacklist)
  1675.     {
  1676.         if(!i) continue;
  1677.         if(ipblacklist[i].ur <= ipblacklist[i - 1].ur)
  1678.         {
  1679.             if(ipblacklist[i].lr == ipblacklist[i - 1].lr && ipblacklist[i].ur == ipblacklist[i - 1].ur)
  1680.                 logline(ACLOG_VERBOSE," blacklist entry %s got dropped (double entry)", iprtoa(ipblacklist[i]));
  1681.             else
  1682.                 logline(ACLOG_VERBOSE," blacklist entry %s got dropped (already covered by %s)", iprtoa(ipblacklist[i]), iprtoa(ipblacklist[i - 1]));
  1683.             ipblacklist.remove(i--); continue;
  1684.         }
  1685.         if(ipblacklist[i].lr <= ipblacklist[i - 1].ur)
  1686.         {
  1687.             logline(ACLOG_VERBOSE," blacklist entries %s and %s are joined due to overlap", iprtoa(ipblacklist[i - 1]), iprtoa(ipblacklist[i]));
  1688.             ipblacklist[i - 1].ur = ipblacklist[i].ur;
  1689.             ipblacklist.remove(i--); continue;
  1690.         }
  1691.     }
  1692.     loopv(ipblacklist) logline(ACLOG_VERBOSE," %s", iprtoa(ipblacklist[i]));
  1693.     logline(ACLOG_INFO,"read %d (%d) blacklist entries from '%s', %d errors", ipblacklist.length(), orglength, blfilename, errors);
  1694. }
  1695.  
  1696. bool checkipblacklist(enet_uint32 ip) // ip: network byte order
  1697. {
  1698.     iprange t;
  1699.     t.lr = ntohl(ip); // blacklist uses host byte order
  1700.     t.ur = 0;
  1701.     return ipblacklist.search(&t, cmpipmatch) != NULL;
  1702. }
  1703.  
  1704. #define MAXNICKFRAGMENTS 5
  1705. enum { NWL_UNLISTED = 0, NWL_PASS, NWL_PWDFAIL, NWL_IPFAIL };
  1706.  
  1707. struct nickblacklist {
  1708.     struct iprchain     { struct iprange ipr; const char *pwd; int next; };
  1709.     struct blackline    { int frag[MAXNICKFRAGMENTS]; bool ignorecase; int line; void clear() { loopi(MAXNICKFRAGMENTS) frag[i] = -1; } };
  1710.     hashtable<const char *, int> whitelist;
  1711.     vector<iprchain> whitelistranges;
  1712.     vector<blackline> blacklines;
  1713.     vector<const char *> blfraglist;
  1714.  
  1715.     void destroylists()
  1716.     {
  1717.         whitelistranges.setsizenodelete(0);
  1718.         enumeratek(whitelist, const char *, key, delete key);
  1719.         whitelist.clear(false);
  1720.         blfraglist.deletecontentsp();
  1721.         blacklines.setsizenodelete(0);
  1722.     }
  1723.  
  1724.     void readnickblacklist(const char *name)
  1725.     {
  1726.         static string nbfilename;
  1727.         static int nbfilesize;
  1728.         const char *sep = " ";
  1729.         int len, line = 1, errors = 0;
  1730.         iprchain iprc;
  1731.         blackline bl;
  1732.  
  1733.         if(!name && getfilesize(nbfilename) == nbfilesize) return;
  1734.         destroylists();
  1735.         char *buf = loadcfgfile(nbfilename, name, &len);
  1736.         nbfilesize = len;
  1737.         if(!buf) return;
  1738.         char *l, *s, *r, *p = buf;
  1739.         logline(ACLOG_VERBOSE,"reading nickname blacklist '%s'", nbfilename);
  1740.         while(p < buf + len)
  1741.         {
  1742.             l = p; p += strlen(p) + 1;
  1743.             l = strtok(l, sep);
  1744.             if(l)
  1745.             {
  1746.                 s = strtok(NULL, sep);
  1747.                 int ic = 0;
  1748.                 if(s && (!strcmp(l, "accept") || !strcmp(l, "a")))
  1749.                 { // accept nickname IP-range
  1750.                     int *i = whitelist.access(s);
  1751.                     if(!i) i = &whitelist.access(newstring(s), -1);
  1752.                     s += strlen(s) + 1;
  1753.                     while(s < p)
  1754.                     {
  1755.                         r = (char *) atoipr(s, &iprc.ipr);
  1756.                         s += strspn(s, sep);
  1757.                         iprc.pwd = r && *s ? NULL : newstring(s, strcspn(s, sep));
  1758.                         if(r || *s)
  1759.                         {
  1760.                             iprc.next = *i;
  1761.                             *i = whitelistranges.length();
  1762.                             whitelistranges.add(iprc);
  1763.                             s = r ? r : s + strlen(iprc.pwd);
  1764.                         }
  1765.                         else break;
  1766.                     }
  1767.                     s = NULL;
  1768.                 }
  1769.                 else if(s && (!strcmp(l, "block") || !strcmp(l, "b") || ic++ || !strcmp(l, "blocki") || !strcmp(l, "bi")))
  1770.                 { // block nickname fragments (ic == ignore case)
  1771.                     bl.clear();
  1772.                     loopi(MAXNICKFRAGMENTS)
  1773.                     {
  1774.                         if(ic) strtoupper(s);
  1775.                         loopvj(blfraglist)
  1776.                         {
  1777.                             if(!strcmp(s, blfraglist[j])) { bl.frag[i] = j; break; }
  1778.                         }
  1779.                         if(bl.frag[i] < 0)
  1780.                         {
  1781.                             bl.frag[i] = blfraglist.length();
  1782.                             blfraglist.add(newstring(s));
  1783.                         }
  1784.                         s = strtok(NULL, sep);
  1785.                         if(!s) break;
  1786.                     }
  1787.                     bl.ignorecase = ic > 0;
  1788.                     bl.line = line;
  1789.                     blacklines.add(bl);
  1790.                 }
  1791.                 else { logline(ACLOG_INFO," error in line %d, file %s: unknown keyword '%s'", line, nbfilename, l); errors++; }
  1792.                 if(s && s[strspn(s, " ")]) { logline(ACLOG_INFO," error in line %d, file %s: ignored '%s'", line, nbfilename, s); errors++; }
  1793.             }
  1794.             line++;
  1795.         }
  1796.         delete[] buf;
  1797.         logline(ACLOG_VERBOSE," nickname whitelist (%d entries):", whitelist.numelems);
  1798.         string text;
  1799.         enumeratekt(whitelist, const char *, key, int, idx,
  1800.         {
  1801.             text[0] = '\0';
  1802.             for(int i = idx; i >= 0; i = whitelistranges[i].next)
  1803.             {
  1804.                 iprchain &ic = whitelistranges[i];
  1805.                 if(ic.pwd) s_strcatf(text, "  pwd:\"%s\"", hiddenpwd(ic.pwd));
  1806.                 else s_strcatf(text, "  %s", iprtoa(ic.ipr));
  1807.             }
  1808.             logline(ACLOG_VERBOSE, "  accept %s%s", key, text);
  1809.         });
  1810.         logline(ACLOG_VERBOSE," nickname blacklist (%d entries):", blacklines.length());
  1811.         loopv(blacklines)
  1812.         {
  1813.             text[0] = '\0';
  1814.             loopj(MAXNICKFRAGMENTS)
  1815.             {
  1816.                 int k = blacklines[i].frag[j];
  1817.                 if(k >= 0) { s_strcat(text, " "); s_strcat(text, blfraglist[k]); }
  1818.             }
  1819.             logline(ACLOG_VERBOSE, "  %2d block%s%s", blacklines[i].line, blacklines[i].ignorecase ? "i" : "", text);
  1820.         }
  1821.         logline(ACLOG_INFO,"read %d + %d entries from nickname blacklist file '%s', %d errors", whitelist.numelems, blacklines.length(), nbfilename, errors);
  1822.     }
  1823.  
  1824.     int checknickwhitelist(const client &c)
  1825.     {
  1826.         if(c.type != ST_TCPIP) return NWL_PASS;
  1827.         iprange ipr;
  1828.         ipr.lr = ntohl(c.peer->address.host); // blacklist uses host byte order
  1829.         int *idx = whitelist.access(c.name);
  1830.         if(!idx) return NWL_UNLISTED; // no matching entry
  1831.         int i = *idx;
  1832.         bool needipr = false, iprok = false, needpwd = false, pwdok = false;
  1833.         while(i >= 0)
  1834.         {
  1835.             iprchain &ic = whitelistranges[i];
  1836.             if(ic.pwd)
  1837.             { // check pwd
  1838.                 needpwd = true;
  1839.                 if(pwdok || !strcmp(genpwdhash(c.name, ic.pwd, c.salt), c.pwd)) pwdok = true;
  1840.             }
  1841.             else
  1842.             { // check IP
  1843.                 needipr = true;
  1844.                 if(!cmpipmatch(&ipr, &ic.ipr)) iprok = true; // range match found
  1845.             }
  1846.             i = whitelistranges[i].next;
  1847.         }
  1848.         if(needpwd && !pwdok) return NWL_PWDFAIL; // wrong PWD
  1849.         if(needipr && !iprok) return NWL_IPFAIL; // wrong IP
  1850.         return NWL_PASS;
  1851.     }
  1852.  
  1853.     int checknickblacklist(const char *name)
  1854.     {
  1855.         if(blacklines.empty()) return -2;  // no nickname blacklist loaded
  1856.         string nameuc;
  1857.         s_strcpy(nameuc, name);
  1858.         strtoupper(nameuc);
  1859.         loopv(blacklines)
  1860.         {
  1861.             loopj(MAXNICKFRAGMENTS)
  1862.             {
  1863.                 int k = blacklines[i].frag[j];
  1864.                 if(k < 0) return blacklines[i].line; // no more fragments to check
  1865.                 if(strstr(blacklines[i].ignorecase ? nameuc : name, blfraglist[k]))
  1866.                 {
  1867.                     if(j == MAXNICKFRAGMENTS - 1) return blacklines[i].line; // all fragments match
  1868.                 }
  1869.                 else break; // this line no match
  1870.             }
  1871.         }
  1872.         return -1; // no match
  1873.     }
  1874. } nbl;
  1875.  
  1876. struct pwddetail
  1877. {
  1878.     string pwd;
  1879.     int line;
  1880.     bool denyadmin;    // true: connect only
  1881. };
  1882.  
  1883. vector<pwddetail> adminpwds;
  1884. #define ADMINPWD_MAXPAR 1
  1885.  
  1886. void readpwdfile(const char *name)
  1887. {
  1888.     static string pwdfilename;
  1889.     static int pwdfilesize;
  1890.     const char *sep = " ";
  1891.     pwddetail c;
  1892.     char *p, *l;
  1893.     int i, len, line, par[ADMINPWD_MAXPAR];
  1894.  
  1895.     if(!name && getfilesize(pwdfilename) == pwdfilesize) return;
  1896.     adminpwds.setsize(0);
  1897.     if(scl.adminpasswd[0])
  1898.     {
  1899.         s_strcpy(c.pwd, scl.adminpasswd);
  1900.         c.line = 0;   // commandline is 'line 0'
  1901.         c.denyadmin = false;
  1902.         adminpwds.add(c);
  1903.     }
  1904.     char *buf = loadcfgfile(pwdfilename, name, &len);
  1905.     pwdfilesize = len;
  1906.     if(!buf) return;
  1907.     p = buf; line = 1;
  1908.     logline(ACLOG_VERBOSE,"reading admin passwords '%s'", pwdfilename);
  1909.     while(p < buf + len)
  1910.     {
  1911.         l = p; p += strlen(p) + 1;
  1912.         l = strtok(l, sep);
  1913.         if(l)
  1914.         {
  1915.             s_strcpy(c.pwd, l);
  1916.             par[0] = 0;  // default values
  1917.             for(i = 0; i < ADMINPWD_MAXPAR; i++)
  1918.             {
  1919.                 if((l = strtok(NULL, sep)) != NULL)
  1920.                     par[i] = atoi(l);
  1921.                 else
  1922.                     break;
  1923.             }
  1924.             //if(i > 0)
  1925.             {
  1926.                 c.line = line;
  1927.                 c.denyadmin = par[0] > 0;
  1928.                 adminpwds.add(c);
  1929.                 logline(ACLOG_VERBOSE,"line%4d: %s %d", c.line, hiddenpwd(c.pwd), c.denyadmin ? 1 : 0);
  1930.             }
  1931.         }
  1932.         line++;
  1933.     }
  1934.     delete[] buf;
  1935.     logline(ACLOG_INFO,"read %d admin passwords from '%s'", adminpwds.length() - (scl.adminpasswd[0] > 0), pwdfilename);
  1936. }
  1937.  
  1938. bool checkadmin(const char *name, const char *pwd, int salt, pwddetail *detail = NULL)
  1939. {
  1940.     bool found = false;
  1941.     loopv(adminpwds)
  1942.     {
  1943.         if(!strcmp(genpwdhash(name, adminpwds[i].pwd, salt), pwd))
  1944.         {
  1945.             if(detail) *detail = adminpwds[i];
  1946.             found = true;
  1947.             break;
  1948.         }
  1949.     }
  1950.     return found;
  1951. }
  1952.  
  1953. bool updatedescallowed(void) { return scl.servdesc_pre[0] || scl.servdesc_suf[0]; }
  1954.  
  1955. void updatesdesc(const char *newdesc, ENetAddress *caller = NULL)
  1956. {
  1957.     if(!newdesc || !newdesc[0] || !updatedescallowed())
  1958.     {
  1959.         s_strcpy(servdesc_current, scl.servdesc_full);
  1960.         custom_servdesc = false;
  1961.     }
  1962.     else
  1963.     {
  1964.         s_sprintf(servdesc_current)("%s%s%s", scl.servdesc_pre, newdesc, scl.servdesc_suf);
  1965.         custom_servdesc = true;
  1966.         if(caller) servdesc_caller = *caller;
  1967.     }
  1968. }
  1969.  
  1970. void forceteam(int client, int team, bool respawn, bool notify = false)
  1971. {
  1972.     if(!valid_client(client) || team < 0 || team > 1) return;
  1973.     if(clients[client]->lastforce && (servmillis - clients[client]->lastforce) < 2000) return;
  1974.     sendf(client, 1, "riii", SV_FORCETEAM, team, (respawn ? 1 : 0) | (respawn && !notify ? 2 : 0));
  1975.     clients[client]->lastforce = servmillis;
  1976.     if(notify) sendf(-1, 1, "riii", SV_FORCENOTIFY, client, team);
  1977. }
  1978.  
  1979. int calcscores() // skill eval
  1980. {
  1981.     int fp12 = (m_ctf || m_htf) ? 55 : 33;
  1982.     int fp3 = (m_ctf || m_htf) ? 25 : 15;
  1983.     int sum = 0;
  1984.     loopv(clients) if(clients[i]->type!=ST_EMPTY)
  1985.     {
  1986.         clientstate &cs = clients[i]->state;
  1987.         sum += clients[i]->at3_score = (cs.frags * 100) / (cs.deaths ? cs.deaths : 1)
  1988.                                      + (cs.flagscore < 3 ? fp12 * cs.flagscore : 2 * fp12 + fp3 * (cs.flagscore - 2));
  1989.     }
  1990.     return sum;
  1991. }
  1992.  
  1993. ivector shuffle;
  1994.  
  1995. void shuffleteams(bool respawn = true)
  1996. {
  1997.     int numplayers = numclients();
  1998.     int team, sums = calcscores();
  1999.     if(gamemillis < 2 * 60 *1000)
  2000.     { // random
  2001.         int teamsize[2] = {0, 0};
  2002.         loopv(clients) if(clients[i]->type!=ST_EMPTY)
  2003.         {
  2004.             sums += rnd(1000);
  2005.             team = sums & 1;
  2006.             if(teamsize[team] >= numplayers/2) team = team_opposite(team);
  2007.             forceteam(i, team, respawn);
  2008.             teamsize[team]++;
  2009.             sums >>= 1;
  2010.         }
  2011.     }
  2012.     else
  2013.     { // skill sorted
  2014.         shuffle.setsize(0);
  2015.         sums /= 4 * numplayers + 2;
  2016.         team = rnd(2);
  2017.         loopv(clients) if(clients[i]->type!=ST_EMPTY) { clients[i]->at3_score += rnd(sums | 1); shuffle.add(i); }
  2018.         shuffle.sort(cmpscore);
  2019.         loopi(shuffle.length())
  2020.         {
  2021.             forceteam(shuffle[i], team, respawn);
  2022.             team = !team;
  2023.         }
  2024.     }
  2025. }
  2026.  
  2027. bool refillteams(bool now, bool notify)  // force only minimal amounts of players
  2028. {
  2029.     static int lasttime_eventeams = 0;
  2030.     int teamsize[2] = {0, 0}, teamscore[2] = {0, 0}, moveable[2] = {0, 0};
  2031.     bool switched = false;
  2032.  
  2033.     calcscores();
  2034.     loopv(clients) if(clients[i]->type!=ST_EMPTY)     // playerlist stocktaking
  2035.     {
  2036.         client *c = clients[i];
  2037.         c->at3_dontmove = true;
  2038.         if(c->isauthed)
  2039.         {
  2040.             int t = 0;
  2041.             if(!strcmp(c->team, "CLA") || t++ || !strcmp(c->team, "RVSF")) // need exact teams here
  2042.             {
  2043.                 teamsize[t]++;
  2044.                 teamscore[t] += c->at3_score;
  2045.                 if(clienthasflag(i) < 0)
  2046.                 {
  2047.                     c->at3_dontmove = false;
  2048.                     moveable[t]++;
  2049.                     if(c->lastforce && (servmillis - c->lastforce) < 3000) return false; // possible unanswered forceteam commands
  2050.                 }
  2051.             }
  2052.         }
  2053.     }
  2054.     int bigteam = teamsize[1] > teamsize[0];
  2055.     int allplayers = teamsize[0] + teamsize[1];
  2056.     int diffnum = teamsize[bigteam] - teamsize[!bigteam];
  2057.     int diffscore = teamscore[bigteam] - teamscore[!bigteam];
  2058.     if(lasttime_eventeams > gamemillis) lasttime_eventeams = 0;
  2059.     if(diffnum > 1)
  2060.     {
  2061.         if(now || gamemillis - lasttime_eventeams > 8000 + allplayers * 1000 || diffnum > 2 + allplayers / 10)
  2062.         {
  2063.             // time to even out teams
  2064.             loopv(clients) if(clients[i]->type!=ST_EMPTY && team_int(clients[i]->team) != bigteam) clients[i]->at3_dontmove = true;  // dont move small team players
  2065.             while(diffnum > 1 && moveable[bigteam] > 0)
  2066.             {
  2067.                 // pick best fitting cn
  2068.                 string atlog, buf;    // debug logging - will be removed
  2069.                 int pick = -1;
  2070.                 int bestfit = 1000000000;
  2071.                 int targetscore = diffscore / (diffnum & ~1);
  2072.                 s_sprintf(atlog)("at-target: %d, ", targetscore);
  2073.                 loopv(clients) if(clients[i]->type!=ST_EMPTY && !clients[i]->at3_dontmove) // try all still movable players
  2074.                 {
  2075.                     int fit = targetscore - clients[i]->at3_score;
  2076.                     if(fit < 0 ) fit = -(fit * 15) / 10;       // avoid too good players
  2077.                     int forcedelay = clients[i]->at3_lastforce ? (1000 - (gamemillis - clients[i]->at3_lastforce) / (5 * 60)) : 0;
  2078.                     if(forcedelay > 0) fit += (fit * forcedelay) / 600;   // avoid lately forced players
  2079.                     if(fit < bestfit + fit * rnd(100) / 400)   // search 'almost' best fit
  2080.                     {
  2081.                         bestfit = fit;
  2082.                         pick = i;
  2083.                     }
  2084.                     s_sprintf(buf)("%d:%d ", i, fit); s_strcat(atlog, buf);
  2085.                 }
  2086.                 if(pick < 0) break; // should really never happen
  2087.                 // move picked player
  2088.                 forceteam(pick, !bigteam, true, notify);
  2089.  
  2090.                 diffnum -= 2;
  2091.                 diffscore -= 2 * clients[pick]->at3_score;
  2092.                 moveable[bigteam]--;
  2093.                 clients[pick]->at3_dontmove = true;
  2094.                 clients[pick]->at3_lastforce = gamemillis;  // try not to force this player again for the next 5 minutes
  2095.                 switched = true;
  2096.                 s_sprintf(buf)(" pick:%d", pick); s_strcat(atlog, buf);
  2097.                 logline(ACLOG_INFO,"%s", atlog);
  2098.             }
  2099.         }
  2100.     }
  2101.     if(diffnum < 2)
  2102.         lasttime_eventeams = gamemillis;
  2103.     return switched;
  2104. }
  2105.  
  2106. void resetmap(const char *newname, int newmode, int newtime, bool notify)
  2107. {
  2108.     if(m_demo) enddemoplayback();
  2109.     else enddemorecord();
  2110.  
  2111.     if(custom_servdesc && findcnbyaddress(&servdesc_caller) < 0)
  2112.     {
  2113.         updatesdesc(NULL);
  2114.         if(notify)
  2115.         {
  2116.             sendservmsg("server description reset to default");
  2117.             logline(ACLOG_INFO, "server description reset to '%s'", servdesc_current);
  2118.         }
  2119.     }
  2120.  
  2121.         bool lastteammode = m_teammode;
  2122.     smode = newmode;
  2123.     s_strcpy(smapname, newname);
  2124.     if(isdedicated && smapname[0]) getservermap();
  2125.  
  2126.     minremain = newtime >= 0 ? newtime : (m_teammode ? 15 : 10);
  2127.     gamemillis = 0;
  2128.     gamelimit = minremain*60000;
  2129.  
  2130.     mapreload = false;
  2131.     interm = 0;
  2132.     if(!laststatus) laststatus = servmillis-61*1000;
  2133.     lastfillup = servmillis;
  2134.     resetitems();
  2135.     mapstats *ms = getservermapstats(smapname, isdedicated);
  2136.     loopi(3) clnumspawn[i] = ms ? ms->spawns[i] : 0;
  2137.     loopi(2)
  2138.     {
  2139.         clnumflagspawn[i] = ms ? ms->flags[i] : 0;
  2140.         sflaginfo &f = sflaginfos[i];
  2141.         if(clnumflagspawn[i] == 1)    // don't check flag positions, if there is more than one flag per team
  2142.         {
  2143.             short *fe = ms->entposs + ms->flagents[i] * 3;
  2144.             f.x = *fe;
  2145.             fe++;
  2146.             f.y = *fe;
  2147.         }
  2148.         else f.x = f.y = -1;
  2149.     }
  2150.     if(ms)
  2151.     {
  2152.         entity e;
  2153.         loopi(ms->hdr.numents)
  2154.         {
  2155.             e.type = ms->enttypes[i];
  2156.             e.transformtype(smode);
  2157.             server_entity se = { e.type, false, true, 0, ms->entposs[i * 3], ms->entposs[i * 3 + 1]};
  2158.             sents.add(se);
  2159.             if(e.fitsmode(smode)) sents[i].spawned = true;
  2160.         }
  2161.         notgotitems = false;
  2162.     }
  2163.     scores.setsize(0);
  2164.     ctfreset();
  2165.     if(notify)
  2166.     {
  2167.         // change map
  2168.         sendf(-1, 1, "risii", SV_MAPCHANGE, smapname, smode, mapavailable(smapname));
  2169.         if(smode>1 || (smode==0 && numnonlocalclients()>0)) sendf(-1, 1, "ri2", SV_TIMEUP, minremain);
  2170.     }
  2171.     if(newname[0])
  2172.     {
  2173.         logline(ACLOG_INFO, "");
  2174.         logline(ACLOG_INFO, "Game start: %s on %s, %d players, %d minutes remaining, mastermode %d, (itemlist %spreloaded, 'getmap' %sprepared)",
  2175.             modestr(smode), smapname, numclients(), minremain, mastermode, ms ? "" : "not ", mapavailable(smapname) ? "" : "not ");
  2176.     }
  2177.     arenaround = 0;
  2178.     if(m_arena)
  2179.     {
  2180.         distributespawns();
  2181.     }
  2182.     if(notify)
  2183.     {
  2184.         // shuffle if previous mode wasn't a team-mode
  2185.         if(m_teammode)
  2186.         {
  2187.             if(!lastteammode)
  2188.                 shuffleteams(false);
  2189.             else if(autoteam)
  2190.                 refillteams(true, false);
  2191.         }
  2192.         // send spawns
  2193.         loopv(clients) if(clients[i]->type!=ST_EMPTY)
  2194.         {
  2195.             client *c = clients[i];
  2196.             c->mapchange();
  2197.             if(m_mp(smode)) sendspawn(c);
  2198.         }
  2199.     }
  2200.     if(m_demo) setupdemoplayback();
  2201.     else if((demonextmatch || scl.demoeverymatch) && *newname && numnonlocalclients() > 0)
  2202.     {
  2203.         demonextmatch = false;
  2204.         setupdemorecord();
  2205.     }
  2206.     if(notify && m_ktf) sendflaginfo();
  2207.  
  2208.     nextmapname[0] = '\0';
  2209.     forceintermission = false;
  2210. }
  2211.  
  2212. int nextcfgset(bool notify = true, bool nochange = false) // load next maprotation set
  2213. {
  2214.     int n = numclients();
  2215.     int csl = configsets.length();
  2216.     int ccs = curcfgset;
  2217.     if(ccs >= 0 && ccs < csl) ccs += configsets[ccs].skiplines;
  2218.     configset *c = NULL;
  2219.     loopi(csl)
  2220.     {
  2221.         ccs++;
  2222.         if(ccs >= csl || ccs < 0) ccs = 0;
  2223.         c = &configsets[ccs];
  2224.         if(n >= c->minplayer && (!c->maxplayer || n <= c->maxplayer))
  2225.         {
  2226.             if(getservermapstats(c->mapname)) break;
  2227.             else logline(ACLOG_INFO, "maprot error: map '%s' not found", c->mapname);
  2228.         }
  2229.     }
  2230.     if(!nochange)
  2231.     {
  2232.         curcfgset = ccs;
  2233.         resetmap(c->mapname, c->mode, c->time, notify);
  2234.     }
  2235.     return ccs;
  2236. }
  2237.  
  2238. bool isbanned(int cn)
  2239. {
  2240.         if(!valid_client(cn)) return false;
  2241.         client &c = *clients[cn];
  2242.     if(c.type==ST_LOCAL) return false;
  2243.         loopv(bans)
  2244.         {
  2245.                 ban &b = bans[i];
  2246.                 if(b.millis < servmillis) { bans.remove(i--); }
  2247.                 if(b.address.host == c.peer->address.host) { return true; }
  2248.         }
  2249.         return checkipblacklist(c.peer->address.host);
  2250. }
  2251.  
  2252. int serveroperator()
  2253. {
  2254.         loopv(clients) if(clients[i]->type!=ST_EMPTY && clients[i]->role > CR_DEFAULT) return i;
  2255.         return -1;
  2256. }
  2257.  
  2258. void sendserveropinfo(int receiver)
  2259. {
  2260.     int op = serveroperator();
  2261.     sendf(receiver, 1, "riii", SV_SERVOPINFO, op, op >= 0 ? clients[op]->role : -1);
  2262. }
  2263.  
  2264. #include "serveractions.h"
  2265.  
  2266. struct voteinfo
  2267. {
  2268.     int owner, callmillis, result;
  2269.     serveraction *action;
  2270.  
  2271.     voteinfo() : owner(0), callmillis(0), result(VOTE_NEUTRAL), action(NULL) {}
  2272.  
  2273.     void end(int result)
  2274.     {
  2275.         if(action && !action->isvalid()) result = VOTE_NO; // don't perform() invalid votes
  2276.         sendf(-1, 1, "ri2", SV_VOTERESULT, result);
  2277.         this->result = result;
  2278.         if(result == VOTE_YES)
  2279.         {
  2280.             if(valid_client(owner)) clients[owner]->lastvotecall = 0;
  2281.             if(action) action->perform();
  2282.         }
  2283.         loopv(clients) clients[i]->vote = VOTE_NEUTRAL;
  2284.     }
  2285.  
  2286.     bool isvalid() { return valid_client(owner) && action != NULL && action->isvalid(); }
  2287.     bool isalive() { return servmillis - callmillis < 40*1000; }
  2288.  
  2289.     void evaluate(bool forceend = false)
  2290.     {
  2291.         if(result!=VOTE_NEUTRAL) return; // block double action
  2292.         if(action && !action->isvalid()) end(VOTE_NO);
  2293.         int stats[VOTE_NUM] = {0};
  2294.         int adminvote = VOTE_NEUTRAL;
  2295.         loopv(clients)
  2296.             if(clients[i]->type!=ST_EMPTY && clients[i]->connectmillis < callmillis)
  2297.             {
  2298.                 stats[clients[i]->vote]++;
  2299.                 if(clients[i]->role==CR_ADMIN) adminvote = clients[i]->vote;
  2300.             };
  2301.  
  2302.         bool admin = clients[owner]->role==CR_ADMIN || (!isdedicated && clients[owner]->type==ST_LOCAL);
  2303.         int total = stats[VOTE_NO]+stats[VOTE_YES]+stats[VOTE_NEUTRAL];
  2304.         const float requiredcount = 0.51f;
  2305.         if(stats[VOTE_YES]/(float)total > requiredcount || admin || adminvote == VOTE_YES)
  2306.             end(VOTE_YES);
  2307.         else if(forceend || stats[VOTE_NO]/(float)total > requiredcount || stats[VOTE_NO] >= stats[VOTE_YES]+stats[VOTE_NEUTRAL] || adminvote == VOTE_NO)
  2308.             end(VOTE_NO);
  2309.         else return;
  2310.     }
  2311. };
  2312.  
  2313. static voteinfo *curvote = NULL;
  2314.  
  2315. bool svote(int sender, int vote, ENetPacket *msg) // true if the vote was placed successfully
  2316. {
  2317.     if(!curvote || !valid_client(sender) || vote < VOTE_YES || vote > VOTE_NO) return false;
  2318.     if(clients[sender]->vote != VOTE_NEUTRAL)
  2319.     {
  2320.         sendf(sender, 1, "ri2", SV_CALLVOTEERR, VOTEE_MUL);
  2321.         return false;
  2322.     }
  2323.     else
  2324.     {
  2325.         sendpacket(-1, 1, msg, sender);
  2326.  
  2327.         clients[sender]->vote = vote;
  2328.         logline(ACLOG_DEBUG,"[%s] client %s voted %s", clients[sender]->hostname, clients[sender]->name, vote == VOTE_NO ? "no" : "yes");
  2329.         curvote->evaluate();
  2330.         return true;
  2331.     }
  2332. }
  2333.  
  2334. void scallvotesuc(voteinfo *v)
  2335. {
  2336.     if(!v->isvalid()) return;
  2337.     DELETEP(curvote);
  2338.     curvote = v;
  2339.     clients[v->owner]->lastvotecall = servmillis;
  2340.  
  2341.     sendf(v->owner, 1, "ri", SV_CALLVOTESUC);
  2342.     logline(ACLOG_INFO, "[%s] client %s called a vote: %s", clients[v->owner]->hostname, clients[v->owner]->name, v->action->desc ? v->action->desc : "[unknown]");
  2343. }
  2344.  
  2345. void scallvoteerr(voteinfo *v, int error)
  2346. {
  2347.     if(!valid_client(v->owner)) return;
  2348.     sendf(v->owner, 1, "ri2", SV_CALLVOTEERR, error);
  2349.     logline(ACLOG_INFO, "[%s] client %s failed to call a vote: %s (%s)", clients[v->owner]->hostname, clients[v->owner]->name, v->action->desc ? v->action->desc : "[unknown]", voteerrorstr(error));
  2350. }
  2351.  
  2352. bool scallvote(voteinfo *v, ENetPacket *msg) // true if a regular vote was called
  2353. {
  2354.     int area = isdedicated ? EE_DED_SERV : EE_LOCAL_SERV;
  2355.     int error = -1;
  2356.  
  2357.     if(!v || !v->isvalid()) error = VOTEE_INVALID;
  2358.     else if(v->action->role > clients[v->owner]->role) error = VOTEE_PERMISSION;
  2359.     else if(!(area & v->action->area)) error = VOTEE_AREA;
  2360.     else if(curvote && curvote->result==VOTE_NEUTRAL) error = VOTEE_CUR;
  2361.     else if(clients[v->owner]->role == CR_DEFAULT && v->action->isdisabled()) error = VOTEE_DISABLED;
  2362.     else if(clients[v->owner]->lastvotecall && servmillis - clients[v->owner]->lastvotecall < 60*1000 && clients[v->owner]->role != CR_ADMIN && numclients()>1)
  2363.         error = VOTEE_MAX;
  2364.  
  2365.     if(error>=0)
  2366.     {
  2367.         scallvoteerr(v, error);
  2368.         return false;
  2369.     }
  2370.     else
  2371.     {
  2372.         sendpacket(-1, 1, msg, v->owner);
  2373.  
  2374.         scallvotesuc(v);
  2375.         return true;
  2376.     }
  2377. }
  2378.  
  2379. void changeclientrole(int client, int role, char *pwd, bool force)
  2380. {
  2381.     pwddetail pd;
  2382.     if(!isdedicated || !valid_client(client)) return;
  2383.     pd.line = -1;
  2384.     if(force || role == CR_DEFAULT || (role == CR_ADMIN && pwd && pwd[0] && checkadmin(clients[client]->name, pwd, clients[client]->salt, &pd) && !pd.denyadmin))
  2385.     {
  2386.         if(role == clients[client]->role) return;
  2387.         if(role > CR_DEFAULT)
  2388.         {
  2389.             loopv(clients) clients[i]->role = CR_DEFAULT;
  2390.         }
  2391.         clients[client]->role = role;
  2392.         sendserveropinfo(-1);
  2393.         if(pd.line > -1)
  2394.             logline(ACLOG_INFO,"[%s] player %s used admin password in line %d", clients[client]->hostname, clients[client]->name[0] ? clients[client]->name : "[unnamed]", pd.line);
  2395.         logline(ACLOG_INFO,"[%s] set role of player %s to %s", clients[client]->hostname, clients[client]->name[0] ? clients[client]->name : "[unnamed]", role == CR_ADMIN ? "admin" : "normal player"); // flowtron : connecting players haven't got a name yet (connectadmin)
  2396.     }
  2397.     else if(pwd && pwd[0]) disconnect_client(client, DISC_SOPLOGINFAIL); // avoid brute-force
  2398.     if(curvote) curvote->evaluate();
  2399. }
  2400.  
  2401. const char *disc_reason(int reason)
  2402. {
  2403.     static const char *disc_reasons[] = { "normal", "end of packet", "client num", "kicked by server operator", "banned by server operator", "tag type", "connection refused due to ban", "wrong password", "failed admin login", "server FULL - maxclients", "server mastermode is \"private\"", "auto kick - did your score drop below the threshold?", "auto ban - did your score drop below the threshold?", "duplicate connection" };
  2404.     return reason >= 0 && (size_t)reason < sizeof(disc_reasons)/sizeof(disc_reasons[0]) ? disc_reasons[reason] : "unknown";
  2405. }
  2406.  
  2407. void disconnect_client(int n, int reason)
  2408. {
  2409.     if(!clients.inrange(n) || clients[n]->type!=ST_TCPIP) return;
  2410.     sdropflag(n);
  2411.     client &c = *clients[n];
  2412.     const char *scoresaved = "";
  2413.     if(c.haswelcome)
  2414.     {
  2415.         savedscore *sc = findscore(c, true);
  2416.         if(sc)
  2417.         {
  2418.             sc->save(c.state);
  2419.             scoresaved = ", score saved";
  2420.         }
  2421.     }
  2422.     int sp = (servmillis - c.connectmillis) / 1000;
  2423.     if(reason>=0) logline(ACLOG_INFO, "[%s] disconnecting client %s (%s) cn %d, %d seconds played%s", c.hostname, c.name, disc_reason(reason), n, sp, scoresaved);
  2424.     else logline(ACLOG_INFO, "[%s] disconnected client %s cn %d, %d seconds played%s", c.hostname, c.name, n, sp, scoresaved);
  2425.     c.peer->data = (void *)-1;
  2426.     if(reason>=0) enet_peer_disconnect(c.peer, reason);
  2427.         clients[n]->zap();
  2428.     sendf(-1, 1, "rii", SV_CDIS, n);
  2429.     if(curvote) curvote->evaluate();
  2430. }
  2431.  
  2432. void sendwhois(int sender, int cn)
  2433. {
  2434.     if(!valid_client(sender) || !valid_client(cn)) return;
  2435.     if(clients[cn]->type == ST_TCPIP)
  2436.     {
  2437.         uint ip = clients[cn]->peer->address.host;
  2438.         if(clients[sender]->role != CR_ADMIN) ip &= 0xFFFF; // only admin gets full IP
  2439.         sendf(sender, 1, "ri3", SV_WHOISINFO, cn, ip);
  2440.     }
  2441. }
  2442.  
  2443. // sending of maps between clients
  2444.  
  2445. string copyname;
  2446. int copysize, copymapsize, copycfgsize, copycfgsizegz;
  2447. uchar *copydata = NULL;
  2448. bool copyrw = false;
  2449.  
  2450. int mapavailable(const char *mapname) { return copydata && !strcmp(copyname, behindpath(mapname)) ? copymapsize : 0; }
  2451.  
  2452. bool sendmapserv(int n, string mapname, int mapsize, int cfgsize, int cfgsizegz, uchar *data)
  2453. {
  2454.     string name;
  2455.     FILE *fp;
  2456.     bool written = false;
  2457.  
  2458.     if(!mapname[0] || mapsize <= 0 || mapsize + cfgsizegz > MAXMAPSENDSIZE || cfgsize > MAXCFGFILESIZE) return false;
  2459.     if(smode != 1 && (strcmp(behindpath(mapname), behindpath(smapname)) || (mapavailable(smapname) && !copyrw))) return false; // map is R/O
  2460.     s_strcpy(copyname, mapname);
  2461.     copymapsize = mapsize;
  2462.     copycfgsize = cfgsize;
  2463.     copycfgsizegz = cfgsizegz;
  2464.     copysize = mapsize + cfgsizegz;
  2465.     copyrw = true;
  2466.     DELETEA(copydata);
  2467.     copydata = new uchar[copysize];
  2468.     memcpy(copydata, data, copysize);
  2469.  
  2470.     s_sprintf(name)(SERVERMAP_PATH_INCOMING "%s.cgz", behindpath(copyname));
  2471.     path(name);
  2472.     fp = fopen(name, "wb");
  2473.     if(fp)
  2474.     {
  2475.         fwrite(copydata, 1, copymapsize, fp);
  2476.         fclose(fp);
  2477.         s_sprintf(name)(SERVERMAP_PATH_INCOMING "%s.cfg", behindpath(copyname));
  2478.         path(name);
  2479.         fp = fopen(name, "wb");
  2480.         if(fp)
  2481.         {
  2482.             uchar *rawcfg = new uchar[copycfgsize];
  2483.             uLongf rawsize = copycfgsize;
  2484.             if(uncompress(rawcfg, &rawsize, copydata + copymapsize, copycfgsizegz) == Z_OK && rawsize - copycfgsize == 0)
  2485.                 fwrite(rawcfg, 1, copycfgsize, fp);
  2486.             fclose(fp);
  2487.             DELETEA(rawcfg);
  2488.             written = true;
  2489.         }
  2490.     }
  2491.     return written;
  2492. }
  2493.  
  2494. ENetPacket *getmapserv(int n)
  2495. {
  2496.     if(!mapavailable(smapname)) return NULL;
  2497.     ENetPacket *packet = enet_packet_create(NULL, MAXTRANS + copysize, ENET_PACKET_FLAG_RELIABLE);
  2498.     ucharbuf p(packet->data, packet->dataLength);
  2499.     putint(p, SV_RECVMAP);
  2500.     sendstring(copyname, p);
  2501.     putint(p, copymapsize);
  2502.     putint(p, copycfgsize);
  2503.     putint(p, copycfgsizegz);
  2504.     p.put(copydata, copysize);
  2505.     enet_packet_resize(packet, p.length());
  2506.     return packet;
  2507. }
  2508.  
  2509. // provide maps by the server
  2510.  
  2511. mapstats *getservermapstats(const char *mapname, bool getlayout)
  2512. {
  2513.     const char *name = behindpath(mapname);
  2514.     s_sprintfd(filename)(SERVERMAP_PATH "%s.cgz", name);
  2515.     path(filename);
  2516.     bool found = fileexists(filename, "r");
  2517.     if(!found)
  2518.     {
  2519.         s_sprintf(filename)(SERVERMAP_PATH_INCOMING "%s.cgz", name);
  2520.         path(filename);
  2521.         found = fileexists(filename, "r");
  2522.         if(!found)
  2523.         {
  2524.             s_sprintf(filename)(SERVERMAP_PATH_BUILTIN "%s.cgz", name);
  2525.             path(filename);
  2526.             found = fileexists(filename, "r");
  2527.         }
  2528.     }
  2529.     if(getlayout) DELETEA(maplayout);
  2530.     return found ? loadmapstats(filename, getlayout) : NULL;
  2531. }
  2532.  
  2533. #define GZBUFSIZE ((MAXCFGFILESIZE * 11) / 10)
  2534.  
  2535. void getservermap(void)
  2536. {
  2537.     static uchar *gzbuf = NULL;
  2538.     string cgzname, cfgname;
  2539.     int cgzsize, cfgsize, cfgsizegz;
  2540.     const char *name = behindpath(smapname);   // no paths allowed here
  2541.     bool mapisrw = false;
  2542.  
  2543.     if(!gzbuf) gzbuf = new uchar[GZBUFSIZE];
  2544.     if(!gzbuf) return;
  2545.     if(!strcmp(name, behindpath(copyname))) return;
  2546.     s_sprintf(cgzname)(SERVERMAP_PATH "%s.cgz", name);
  2547.     path(cgzname);
  2548.     if(fileexists(cgzname, "r"))
  2549.     {
  2550.         s_sprintf(cfgname)(SERVERMAP_PATH "%s.cfg", name);
  2551.     }
  2552.     else
  2553.     {
  2554.         s_sprintf(cgzname)(SERVERMAP_PATH_INCOMING "%s.cgz", name);
  2555.         path(cgzname);
  2556.         s_sprintf(cfgname)(SERVERMAP_PATH_INCOMING "%s.cfg", name);
  2557.         mapisrw = true;
  2558.     }
  2559.     path(cfgname);
  2560.     uchar *cgzdata = (uchar *)loadfile(cgzname, &cgzsize);
  2561.     uchar *cfgdata = (uchar *)loadfile(cfgname, &cfgsize);
  2562.     if(cgzdata && (!cfgdata || cfgsize < MAXCFGFILESIZE))
  2563.     {
  2564.         uLongf gzbufsize = GZBUFSIZE;
  2565.         if(!cfgdata || compress2(gzbuf, &gzbufsize, cfgdata, cfgsize, 9) != Z_OK)
  2566.         {
  2567.             cfgsize = 0;
  2568.             gzbufsize = 0;
  2569.         }
  2570.         cfgsizegz = (int) gzbufsize;
  2571.         if(cgzsize + cfgsizegz < MAXMAPSENDSIZE)
  2572.         {
  2573.             s_strcpy(copyname, name);
  2574.             copymapsize = cgzsize;
  2575.             copycfgsize = cfgsize;
  2576.             copycfgsizegz = cfgsizegz;
  2577.             copysize = cgzsize + cfgsizegz;
  2578.             copyrw = mapisrw;
  2579.             DELETEA(copydata);
  2580.             copydata = new uchar[copysize];
  2581.             memcpy(copydata, cgzdata, cgzsize);
  2582.             memcpy(copydata + cgzsize, gzbuf, cfgsizegz);
  2583.             logline(ACLOG_INFO,"loaded map %s, %d + %d(%d) bytes.", cgzname, cgzsize, cfgsize, cfgsizegz);
  2584.         }
  2585.     }
  2586.     DELETEA(cgzdata);
  2587.     DELETEA(cfgdata);
  2588. }
  2589.  
  2590. void sendresume(client &c, bool broadcast)
  2591. {
  2592.     sendf(broadcast ? -1 : c.clientnum, 1, "rxii9vvi", broadcast ? c.clientnum : -1, SV_RESUME,
  2593.             c.clientnum,
  2594.             c.state.state,
  2595.             c.state.lifesequence,
  2596.             c.state.gunselect,
  2597.             c.state.flagscore,
  2598.             c.state.frags,
  2599.             c.state.deaths,
  2600.             c.state.health,
  2601.             c.state.armour,
  2602.             NUMGUNS, c.state.ammo,
  2603.             NUMGUNS, c.state.mag,
  2604.             -1);
  2605. }
  2606.  
  2607. void sendinits2c(client &c)
  2608. {
  2609.     sendf(c.clientnum, 1, "ri5", SV_INITS2C, c.clientnum, isdedicated ? SERVER_PROTOCOL_VERSION : PROTOCOL_VERSION, c.salt, scl.serverpassword[0] ? 1 : 0);
  2610. }
  2611.  
  2612. void welcomepacket(ucharbuf &p, int n, ENetPacket *packet, bool forcedeath)
  2613. {
  2614.     #define CHECKSPACE(n) \
  2615.     { \
  2616.         int space = (n); \
  2617.         if(p.remaining() < space) \
  2618.         { \
  2619.            enet_packet_resize(packet, packet->dataLength + max(MAXTRANS, space - p.remaining())); \
  2620.            p.buf = packet->data; \
  2621.            p.maxlen = (int)packet->dataLength; \
  2622.         } \
  2623.     }
  2624.  
  2625.     if(!smapname[0] && configsets.length()) nextcfgset(false);
  2626.  
  2627.     client *c = valid_client(n) ? clients[n] : NULL;
  2628.     int numcl = numclients();
  2629.  
  2630.     putint(p, SV_WELCOME);
  2631.     putint(p, smapname[0] && !m_demo ? numcl : -1);
  2632.     if(smapname[0] && !m_demo)
  2633.     {
  2634.         putint(p, SV_MAPCHANGE);
  2635.         sendstring(smapname, p);
  2636.         putint(p, smode);
  2637.         putint(p, mapavailable(smapname));
  2638.         if(smode>1 || (smode==0 && numnonlocalclients()>0))
  2639.         {
  2640.             putint(p, SV_TIMEUP);
  2641.             putint(p, minremain);
  2642.         }
  2643.         if(numcl>1)
  2644.         {
  2645.             putint(p, SV_ITEMLIST);
  2646.             loopv(sents) if(sents[i].spawned)
  2647.             {
  2648.                 putint(p, i);
  2649.                 putint(p, sents[i].type);
  2650.                 CHECKSPACE(256);
  2651.             }
  2652.             putint(p, -1);
  2653.         }
  2654.         if(m_flags)
  2655.         {
  2656.             CHECKSPACE(256);
  2657.             loopi(2) putflaginfo(p, i);
  2658.         }
  2659.     }
  2660.     if(c && c->type == ST_TCPIP && serveroperator() != -1) sendserveropinfo(n);
  2661.     if(numcl>1)
  2662.     {
  2663.         putint(p, SV_FORCETEAM);
  2664.         putint(p, freeteam(n));
  2665.         putint(p, 0);
  2666.     }
  2667.     if(c) c->lastforce = servmillis;
  2668.     bool restored = false;
  2669.     if(c)
  2670.     {
  2671.         if(c->type==ST_TCPIP)
  2672.         {
  2673.             savedscore *sc = findscore(*c, false);
  2674.             if(sc)
  2675.             {
  2676.                 sc->restore(c->state);
  2677.                 restored = true;
  2678.             }
  2679.         }
  2680.  
  2681.         CHECKSPACE(256);
  2682.         if(!canspawn(c, true) || forcedeath)
  2683.         {
  2684.             putint(p, SV_FORCEDEATH);
  2685.             putint(p, n);
  2686.             sendf(-1, 1, "ri2x", SV_FORCEDEATH, n, n);
  2687.         }
  2688.         else
  2689.         {
  2690.             clientstate &gs = c->state;
  2691.             spawnstate(c);
  2692.             putint(p, SV_SPAWNSTATE);
  2693.             putint(p, gs.lifesequence);
  2694.             putint(p, gs.health);
  2695.             putint(p, gs.armour);
  2696.             putint(p, gs.primary);
  2697.             putint(p, gs.gunselect);
  2698.             putint(p, -1);
  2699.             loopi(NUMGUNS) putint(p, gs.ammo[i]);
  2700.             loopi(NUMGUNS) putint(p, gs.mag[i]);
  2701.             gs.lastspawn = gamemillis;
  2702.         }
  2703.     }
  2704.     if(clients.length()>1 || restored)
  2705.     {
  2706.         putint(p, SV_RESUME);
  2707.         loopv(clients)
  2708.         {
  2709.             client &c = *clients[i];
  2710.             if(c.type!=ST_TCPIP || (c.clientnum==n && !restored)) continue;
  2711.             CHECKSPACE(256);
  2712.             putint(p, c.clientnum);
  2713.             putint(p, c.state.state);
  2714.             putint(p, c.state.lifesequence);
  2715.             putint(p, c.state.gunselect);
  2716.             putint(p, c.state.flagscore);
  2717.             putint(p, c.state.frags);
  2718.             putint(p, c.state.deaths);
  2719.             putint(p, c.state.health);
  2720.             putint(p, c.state.armour);
  2721.             loopi(NUMGUNS) putint(p, c.state.ammo[i]);
  2722.             loopi(NUMGUNS) putint(p, c.state.mag[i]);
  2723.         }
  2724.         putint(p, -1);
  2725.     }
  2726.     putint(p, SV_AUTOTEAM);
  2727.     putint(p, autoteam ? 1 : 0);
  2728.     if(scl.motd[0])
  2729.     {
  2730.         CHECKSPACE(5+2*(int)strlen(scl.motd)+1);
  2731.         putint(p, SV_TEXT);
  2732.         sendstring(scl.motd, p);
  2733.     }
  2734.  
  2735.     #undef CHECKSPACE
  2736. }
  2737.  
  2738. void sendwelcome(client *cl, int chan, bool forcedeath)
  2739. {
  2740.     ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
  2741.     ucharbuf p(packet->data, packet->dataLength);
  2742.     welcomepacket(p, cl->clientnum, packet, forcedeath);
  2743.     enet_packet_resize(packet, p.length());
  2744.     sendpacket(cl->clientnum, chan, packet);
  2745.     if(!packet->referenceCount) enet_packet_destroy(packet);
  2746.     cl->haswelcome = true;
  2747. }
  2748.  
  2749. int checktype(int type, client *cl)
  2750. {
  2751.     if(cl && cl->type==ST_LOCAL) return type;
  2752.     // only allow edit messages in coop-edit mode
  2753.     static int edittypes[] = { SV_EDITENT, SV_EDITH, SV_EDITT, SV_EDITS, SV_EDITD, SV_EDITE, SV_NEWMAP };
  2754.     if(cl && smode!=GMODE_COOPEDIT) loopi(sizeof(edittypes)/sizeof(int)) if(type == edittypes[i]) return -1;
  2755.     // server only messages
  2756.     static int servtypes[] = { SV_INITS2C, SV_WELCOME, SV_CDIS, SV_GIBDIED, SV_DIED,
  2757.                         SV_GIBDAMAGE, SV_DAMAGE, SV_HITPUSH, SV_SHOTFX,
  2758.                         SV_SPAWNSTATE, SV_FORCEDEATH, SV_RESUME, SV_TIMEUP,
  2759.                         SV_MAPRELOAD, SV_ITEMACC, SV_MAPCHANGE, SV_ITEMSPAWN,
  2760.                         SV_PONG, SV_SERVMSG, SV_MODELSKIN,
  2761.                         SV_FLAGINFO, SV_FLAGMSG, SV_FLAGCNT,
  2762.                         SV_ARENAWIN, SV_SERVOPINFO,
  2763.                         SV_CALLVOTESUC, SV_CALLVOTEERR, SV_VOTERESULT,
  2764.                         SV_FORCETEAM, SV_AUTOTEAM, SV_WHOISINFO,
  2765.                         SV_SENDDEMOLIST, SV_SENDDEMO, SV_DEMOPLAYBACK, SV_CLIENT,
  2766.                         SV_FORCENOTIFY };
  2767.     if(cl) loopi(sizeof(servtypes)/sizeof(int)) if(type == servtypes[i]) return -1;
  2768.     if (type < 0 || type >= SV_NUM) return -1;
  2769.     return type;
  2770. }
  2771.  
  2772. // server side processing of updates: does very little and most state is tracked client only
  2773. // could be extended to move more gameplay to server (at expense of lag)
  2774.  
  2775. void process(ENetPacket *packet, int sender, int chan)   // sender may be -1
  2776. {
  2777.     ucharbuf p(packet->data, packet->dataLength);
  2778.     char text[MAXTRANS];
  2779.     client *cl = sender>=0 ? clients[sender] : NULL;
  2780.     pwddetail pd;
  2781.     int type;
  2782.  
  2783.     if(cl && !cl->isauthed)
  2784.     {
  2785.         int clientrole = CR_DEFAULT;
  2786.  
  2787.         if(chan==0) return;
  2788.         else if(chan!=1 || getint(p)!=SV_CONNECT) disconnect_client(sender, DISC_TAGT);
  2789.         else
  2790.         {
  2791.             getstring(text, p);
  2792.             filtertext(text, text, 0, MAXNAMELEN);
  2793.             if(!text[0]) s_strcpy(text, "unarmed");
  2794.             s_strncpy(cl->name, text, MAXNAMELEN+1);
  2795.  
  2796.             getstring(text, p);
  2797.             s_strcpy(cl->pwd, text);
  2798.             int wantrole = getint(p);
  2799.             cl->state.nextprimary = getint(p);
  2800.             bool banned = isbanned(sender);
  2801.             bool srvfull = numnonlocalclients() > scl.maxclients;
  2802.             bool srvprivate = mastermode == MM_PRIVATE;
  2803.             int bl = 0, wl = nbl.checknickwhitelist(*cl);
  2804.             const char *wlp = wl == NWL_PASS ? ", nickname whitelist match" : "";
  2805.             if(wl == NWL_UNLISTED) bl = nbl.checknickblacklist(cl->name);
  2806.             if(wl == NWL_IPFAIL || wl == NWL_PWDFAIL)
  2807.             { // nickname matches whitelist, but IP is not in the required range or PWD doesn't match
  2808.                 logline(ACLOG_INFO, "[%s] '%s' matches nickname whitelist: wrong %s", cl->hostname, cl->name, wl == NWL_IPFAIL ? "IP" : "PWD");
  2809.                 disconnect_client(sender, DISC_MKICK);
  2810.             }
  2811.             else if(bl > 0)
  2812.             { // nickname matches blacklist
  2813.                 logline(ACLOG_INFO, "[%s] '%s' matches nickname blacklist line %d", cl->hostname, cl->name, bl);
  2814.                 disconnect_client(sender, DISC_MKICK);
  2815.             }
  2816.             else if(checkadmin(cl->name, text, cl->salt, &pd) && (!pd.denyadmin || (banned && !srvfull && !srvprivate))) // pass admins always through
  2817.             { // admin (or deban) password match
  2818.                 bool banremoved = false;
  2819.                 cl->isauthed = true;
  2820.                 if(!pd.denyadmin && wantrole == CR_ADMIN) clientrole = CR_ADMIN;
  2821.                 if(banned)
  2822.                 {
  2823.                     loopv(bans) if(bans[i].address.host == cl->peer->address.host) { banremoved = true; bans.remove(i); break; } // remove admin bans
  2824.                 }
  2825.                 if(srvfull)
  2826.                 {
  2827.                     loopv(clients) if(i != sender && clients[i]->type==ST_TCPIP)
  2828.                     {
  2829.                         disconnect_client(i, DISC_MAXCLIENTS); // disconnect someone else to fit maxclients again
  2830.                         break;
  2831.                     }
  2832.                 }
  2833.                 logline(ACLOG_INFO, "[%s] %s logged in using the admin password in line %d%s%s", cl->hostname, cl->name, pd.line, wlp, banremoved ? ", (ban removed)" : "");
  2834.             }
  2835.             else if(scl.serverpassword[0] && !(srvprivate || srvfull || banned))
  2836.             { // server password required
  2837.                 if(!strcmp(genpwdhash(cl->name, scl.serverpassword, cl->salt), text))
  2838.                 {
  2839.                     cl->isauthed = true;
  2840.                     logline(ACLOG_INFO, "[%s] %s client logged in (using serverpassword)%s", cl->hostname, cl->name, wlp);
  2841.                 }
  2842.                 else disconnect_client(sender, DISC_WRONGPW);
  2843.             }
  2844.             else if(srvprivate) disconnect_client(sender, DISC_MASTERMODE);
  2845.             else if(srvfull) disconnect_client(sender, DISC_MAXCLIENTS);
  2846.             else if(banned) disconnect_client(sender, DISC_BANREFUSE);
  2847.             else
  2848.             {
  2849.                 cl->isauthed = true;
  2850.                 logline(ACLOG_INFO, "[%s] %s logged in (default)%s", cl->hostname, cl->name, wlp);
  2851.             }
  2852.         }
  2853.         if(!cl->isauthed) return;
  2854.  
  2855.         if(cl->type==ST_TCPIP)
  2856.         {
  2857.             loopv(clients) if(i != sender)
  2858.             {
  2859.                 client *dup = clients[i];
  2860.                 if(dup->type==ST_TCPIP && dup->peer->address.host==cl->peer->address.host && dup->peer->address.port==cl->peer->address.port)
  2861.                     disconnect_client(i, DISC_DUP);
  2862.             }
  2863.         }
  2864.  
  2865.         sendwelcome(cl);
  2866.         if(findscore(*cl, false)) sendresume(*cl, true);
  2867.         if(clientrole != CR_DEFAULT) changeclientrole(sender, clientrole, NULL, true);
  2868.     }
  2869.  
  2870.     if(packet->flags&ENET_PACKET_FLAG_RELIABLE) reliablemessages = true;
  2871.  
  2872.     #define QUEUE_MSG { if(cl->type==ST_TCPIP) while(curmsg<p.length()) cl->messages.add(p.buf[curmsg++]); }
  2873.     #define QUEUE_BUF(size, body) { \
  2874.         if(cl->type==ST_TCPIP) \
  2875.         { \
  2876.             curmsg = p.length(); \
  2877.             ucharbuf buf = cl->messages.reserve(size); \
  2878.             { body; } \
  2879.             cl->messages.addbuf(buf); \
  2880.         } \
  2881.     }
  2882.     #define QUEUE_INT(n) QUEUE_BUF(5, putint(buf, n))
  2883.     #define QUEUE_UINT(n) QUEUE_BUF(4, putuint(buf, n))
  2884.     #define QUEUE_STR(text) QUEUE_BUF(2*(int)strlen(text)+1, sendstring(text, buf))
  2885.     #define MSG_PACKET(packet) \
  2886.         ENetPacket *packet = enet_packet_create(NULL, 16 + p.length() - curmsg, ENET_PACKET_FLAG_RELIABLE); \
  2887.         ucharbuf buf(packet->data, packet->dataLength); \
  2888.         putint(buf, SV_CLIENT); \
  2889.         putint(buf, cl->clientnum); \
  2890.         putuint(buf, p.length() - curmsg); \
  2891.         buf.put(&p.buf[curmsg], p.length() - curmsg); \
  2892.         enet_packet_resize(packet, buf.length());
  2893.  
  2894.     int curmsg;
  2895.     while((curmsg = p.length()) < p.maxlen)
  2896.     {
  2897.         type = checktype(getint(p), cl);
  2898.  
  2899.         #ifdef _DEBUG
  2900.         if(type!=SV_POS && type!=SV_CLIENTPING && type!=SV_PING && type!=SV_CLIENT)
  2901.         {
  2902.             DEBUGVAR(cl->name);
  2903.             ASSERT(type>=0 && type<SV_NUM);
  2904.             DEBUGVAR(messagenames[type]);
  2905.             protocoldebug(true);
  2906.         }
  2907.         else protocoldebug(false);
  2908.         #endif
  2909.  
  2910.         switch(type)
  2911.         {
  2912.             case SV_TEAMTEXT:
  2913.                 getstring(text, p);
  2914.                 filtertext(text, text);
  2915.                 if(!spamdetect(cl, text))
  2916.                 {
  2917.                     logline(ACLOG_INFO, "[%s] %s says to team %s: '%s'", cl->hostname, cl->name, cl->team, text);
  2918.                     sendteamtext(text, sender);
  2919.                 }
  2920.                 else
  2921.                 {
  2922.                     logline(ACLOG_INFO, "[%s] %s says to team %s: '%s', SPAM detected", cl->hostname, cl->name, cl->team, text);
  2923.                     sendservmsg("\f3please do not spam", sender);
  2924.                 }
  2925.                 break;
  2926.  
  2927.             case SV_TEXT:
  2928.             {
  2929.                 int mid1 = curmsg, mid2 = p.length();
  2930.                 getstring(text, p);
  2931.                 filtertext(text, text);
  2932.                 if(!spamdetect(cl, text))
  2933.                 {
  2934.                     logline(ACLOG_INFO, "[%s] %s says: '%s'", cl->hostname, cl->name, text);
  2935.                     if(cl->type==ST_TCPIP) while(mid1<mid2) cl->messages.add(p.buf[mid1++]);
  2936.                     QUEUE_STR(text);
  2937.                 }
  2938.                 else
  2939.                 {
  2940.                     logline(ACLOG_INFO, "[%s] %s says: '%s', SPAM detected", cl->hostname, cl->name, text);
  2941.                     sendservmsg("\f3please do not spam", sender);
  2942.                 }
  2943.                 break;
  2944.             }
  2945.  
  2946.             case SV_VOICECOM:
  2947.                 getint(p);
  2948.                 QUEUE_MSG;
  2949.                 break;
  2950.  
  2951.             case SV_VOICECOMTEAM:
  2952.                 sendvoicecomteam(getint(p), sender);
  2953.                 break;
  2954.  
  2955.             case SV_INITC2S:
  2956.             {
  2957.                 QUEUE_MSG;
  2958.                 getstring(text, p);
  2959.                 filtertext(text, text, 0, MAXNAMELEN);
  2960.                 if(!text[0]) s_strcpy(text, "unarmed");
  2961.                 QUEUE_STR(text);
  2962.                 bool namechanged = strcmp(cl->name, text) != 0;
  2963.                 if(namechanged) logline(ACLOG_INFO,"[%s] %s changed his name to %s", cl->hostname, cl->name, text);
  2964.                 s_strncpy(cl->name, text, MAXNAMELEN+1);
  2965.                 getstring(text, p);
  2966.                 filtertext(cl->team, text, 0, MAXTEAMLEN);
  2967.                 QUEUE_STR(text);
  2968.                 cl->skin = getint(p);
  2969.                 QUEUE_MSG;
  2970.                 if(namechanged)
  2971.                 {
  2972.                     switch(nbl.checknickwhitelist(*cl))
  2973.                     {
  2974.                         case NWL_PWDFAIL:
  2975.                         case NWL_IPFAIL:
  2976.                             logline(ACLOG_INFO, "[%s] '%s' matches nickname whitelist: wrong IP/PWD", cl->hostname, cl->name);
  2977.                             disconnect_client(sender, DISC_MKICK);
  2978.                             break;
  2979.  
  2980.                         case NWL_UNLISTED:
  2981.                         {
  2982.                             int l = nbl.checknickblacklist(cl->name);
  2983.                             if(l >= 0)
  2984.                             {
  2985.                                 logline(ACLOG_INFO, "[%s] '%s' matches nickname blacklist line %d", cl->hostname, cl->name, l);
  2986.                                 disconnect_client(sender, DISC_MKICK);
  2987.                             }
  2988.                             break;
  2989.                         }
  2990.                     }
  2991.                 }
  2992.                 break;
  2993.             }
  2994.  
  2995.             case SV_ITEMLIST:
  2996.             {
  2997.                 int n;
  2998.                 while((n = getint(p))!=-1)
  2999.                 {
  3000.                     server_entity se = { getint(p), false, false, 0, 0, 0};
  3001.                     if(notgotitems)
  3002.                     {
  3003.                         while(sents.length()<=n) sents.add(se);
  3004.                         sents[n].spawned = true;
  3005.                     }
  3006.                 }
  3007.                 notgotitems = false;
  3008.                 break;
  3009.             }
  3010.  
  3011.             case SV_SPAWNLIST:
  3012.             {
  3013.                 if(getint(p) > 0)
  3014.                 {
  3015.                     loopi(3) clnumspawn[i] = getint(p);
  3016.                     loopi(2) clnumflagspawn[i] = getint(p);
  3017.                 }
  3018.                 QUEUE_MSG;
  3019.                 break;
  3020.             }
  3021.  
  3022.             case SV_ITEMPICKUP:
  3023.             {
  3024.                 int n = getint(p);
  3025.                 if(!arenaround || arenaround - gamemillis > 2000)
  3026.                 {
  3027.                     gameevent &pickup = cl->addevent();
  3028.                     pickup.type = GE_PICKUP;
  3029.                     pickup.pickup.ent = n;
  3030.                 }
  3031.                 else
  3032.                 { // no nade pickup during last two seconds of lss intermission
  3033.                     if(sents.inrange(n) && sents[n].spawned)
  3034.                         sendf(sender, 1, "ri2", SV_ITEMSPAWN, n);
  3035.                 }
  3036.                 break;
  3037.             }
  3038.  
  3039.             case SV_WEAPCHANGE:
  3040.             {
  3041.                 int gunselect = getint(p);
  3042.                 if(gunselect<0 && gunselect>=NUMGUNS) break;
  3043.                 cl->state.gunselect = gunselect;
  3044.                 QUEUE_MSG;
  3045.                 break;
  3046.             }
  3047.  
  3048.             case SV_PRIMARYWEAP:
  3049.             {
  3050.                 int nextprimary = getint(p);
  3051.                 if(nextprimary<0 && nextprimary>=NUMGUNS) break;
  3052.                 cl->state.nextprimary = nextprimary;
  3053.                 break;
  3054.             }
  3055.  
  3056.             case SV_CHANGETEAM:
  3057.                 if(cl->state.state==CS_ALIVE)
  3058.                 {
  3059.                     cl->state.state = CS_DEAD;
  3060.                     cl->state.respawn();
  3061.                     sendf(-1, 1, "rii", SV_FORCEDEATH, cl->clientnum);
  3062.                 }
  3063.                 break;
  3064.  
  3065.             case SV_TRYSPAWN:
  3066.                 if(cl->state.state!=CS_DEAD || cl->state.lastspawn>=0 || !canspawn(cl)) break;
  3067.                 if(cl->state.lastdeath) cl->state.respawn();
  3068.                 sendspawn(cl);
  3069.                 break;
  3070.  
  3071.             case SV_SPAWN:
  3072.             {
  3073.                 int ls = getint(p), gunselect = getint(p);
  3074.                 if((cl->state.state!=CS_ALIVE && cl->state.state!=CS_DEAD) || ls!=cl->state.lifesequence || cl->state.lastspawn<0 || gunselect<0 || gunselect>=NUMGUNS) break;
  3075.                 cl->state.lastspawn = -1;
  3076.                 cl->state.state = CS_ALIVE;
  3077.                 cl->state.gunselect = gunselect;
  3078.                 QUEUE_BUF(5*(5 + 2*NUMGUNS),
  3079.                 {
  3080.                     putint(buf, SV_SPAWN);
  3081.                     putint(buf, cl->state.lifesequence);
  3082.                     putint(buf, cl->state.health);
  3083.                     putint(buf, cl->state.armour);
  3084.                     putint(buf, cl->state.gunselect);
  3085.                     loopi(NUMGUNS) putint(buf, cl->state.ammo[i]);
  3086.                     loopi(NUMGUNS) putint(buf, cl->state.mag[i]);
  3087.                 });
  3088.                 break;
  3089.             }
  3090.  
  3091.             case SV_SUICIDE:
  3092.             {
  3093.                 gameevent &suicide = cl->addevent();
  3094.                 suicide.type = GE_SUICIDE;
  3095.                 break;
  3096.             }
  3097.  
  3098.             case SV_SHOOT:
  3099.             {
  3100.                 gameevent &shot = cl->addevent();
  3101.                 shot.type = GE_SHOT;
  3102.                 #define seteventmillis(event) \
  3103.                 { \
  3104.                     event.id = getint(p); \
  3105.                     if(!cl->timesync || (cl->events.length()==1 && cl->state.waitexpired(gamemillis))) \
  3106.                     { \
  3107.                         cl->timesync = true; \
  3108.                         cl->gameoffset = gamemillis - event.id; \
  3109.                         event.millis = gamemillis; \
  3110.                     } \
  3111.                     else event.millis = cl->gameoffset + event.id; \
  3112.                 }
  3113.                 seteventmillis(shot.shot);
  3114.                 shot.shot.gun = getint(p);
  3115.                 loopk(3) shot.shot.from[k] = getint(p)/DMF;
  3116.                 loopk(3) shot.shot.to[k] = getint(p)/DMF;
  3117.                 int hits = getint(p);
  3118.                 loopk(hits)
  3119.                 {
  3120.                     gameevent &hit = cl->addevent();
  3121.                     hit.type = GE_HIT;
  3122.                     hit.hit.target = getint(p);
  3123.                     hit.hit.lifesequence = getint(p);
  3124.                     hit.hit.info = getint(p);
  3125.                     loopk(3) hit.hit.dir[k] = getint(p)/DNF;
  3126.                 }
  3127.                 break;
  3128.             }
  3129.  
  3130.             case SV_EXPLODE:
  3131.             {
  3132.                 gameevent &exp = cl->addevent();
  3133.                 exp.type = GE_EXPLODE;
  3134.                 seteventmillis(exp.explode);
  3135.                 exp.explode.gun = getint(p);
  3136.                 exp.explode.id = getint(p);
  3137.                 int hits = getint(p);
  3138.                 loopk(hits)
  3139.                 {
  3140.                     gameevent &hit = cl->addevent();
  3141.                     hit.type = GE_HIT;
  3142.                     hit.hit.target = getint(p);
  3143.                     hit.hit.lifesequence = getint(p);
  3144.                     hit.hit.dist = getint(p)/DMF;
  3145.                     loopk(3) hit.hit.dir[k] = getint(p)/DNF;
  3146.                 }
  3147.                 break;
  3148.             }
  3149.  
  3150.             case SV_AKIMBO:
  3151.             {
  3152.                 gameevent &akimbo = cl->addevent();
  3153.                 akimbo.type = GE_AKIMBO;
  3154.                 seteventmillis(akimbo.akimbo);
  3155.                 break;
  3156.             }
  3157.  
  3158.             case SV_RELOAD:
  3159.             {
  3160.                 gameevent &reload = cl->addevent();
  3161.                 reload.type = GE_RELOAD;
  3162.                 seteventmillis(reload.reload);
  3163.                 reload.reload.gun = getint(p);
  3164.                 break;
  3165.             }
  3166.  
  3167.             case SV_PING:
  3168.                 sendf(sender, 1, "ii", SV_PONG, getint(p));
  3169.                 break;
  3170.  
  3171.  
  3172.             case SV_CLIENTPING:
  3173.             {
  3174.                 int ping = getint(p);
  3175.                 if(cl) cl->ping = cl->ping == 9999 ? ping : (cl->ping * 4 + ping) / 5;
  3176.                 QUEUE_MSG;
  3177.                 break;
  3178.             }
  3179.  
  3180.             case SV_POS:
  3181.             {
  3182.                 int cn = getint(p);
  3183.                 if(cn!=sender)
  3184.                 {
  3185.                     disconnect_client(sender, DISC_CN);
  3186.     #ifndef STANDALONE
  3187.                     conoutf("ERROR: invalid client (msg %i)", type);
  3188.     #endif
  3189.                     return;
  3190.                 }
  3191.                 loopi(3) clients[cn]->state.o[i] = getuint(p)/DMF;
  3192.                 getuint(p);
  3193.                 loopi(5) getint(p);
  3194.                 getuint(p);
  3195.                 if(cl->type==ST_TCPIP && (cl->state.state==CS_ALIVE || cl->state.state==CS_EDITING))
  3196.                 {
  3197.                     cl->position.setsizenodelete(0);
  3198.                     while(curmsg<p.length()) cl->position.add(p.buf[curmsg++]);
  3199.                 }
  3200.                 if(maplayout)
  3201.                 {
  3202.                     vec &po = clients[cn]->state.o;
  3203.                     int ls = (1 << maplayout_factor) - 1;
  3204.                     if(po.x < 0 || po.y < 0 || po.x > ls || po.y > ls || maplayout[((int) po.x) + (((int) po.y) << maplayout_factor)] > po.z + 3)
  3205.                     {
  3206.                         if(gamemillis > 10000 && (servmillis - clients[cn]->connectmillis) > 10000) clients[cn]->mapcollisions++;    // assume map to be loaded after 10 seconds: fixme
  3207.                         if((clients[cn]->mapcollisions % 25) == 1)
  3208.                         {
  3209.                             logline(ACLOG_INFO, "[%s] %s collides with the map (%d)", clients[cn]->hostname, clients[cn]->name, clients[cn]->mapcollisions);
  3210.                         }
  3211.                     }
  3212.                 }
  3213.                 break;
  3214.             }
  3215.  
  3216.             case SV_NEXTMAP:
  3217.             {
  3218.                 getstring(text, p);
  3219.                 filtertext(text, text);
  3220.                 int mode = getint(p);
  3221.                 if(mapreload || numclients() == 1) resetmap(text, mode);
  3222.                 break;
  3223.             }
  3224.  
  3225.             case SV_SENDMAP:
  3226.             {
  3227.                 getstring(text, p);
  3228.                 filtertext(text, text);
  3229.                 int mapsize = getint(p);
  3230.                 int cfgsize = getint(p);
  3231.                 int cfgsizegz = getint(p);
  3232.                 if(p.remaining() < mapsize + cfgsizegz)
  3233.                 {
  3234.                     p.forceoverread();
  3235.                     break;
  3236.                 }
  3237.                 if(sendmapserv(sender, text, mapsize, cfgsize, cfgsizegz, &p.buf[p.len]))
  3238.                 {
  3239.                     logline(ACLOG_INFO,"[%s] %s sent map %s, %d + %d(%d) bytes written",
  3240.                                 clients[sender]->hostname, clients[sender]->name, text, mapsize, cfgsize, cfgsizegz);
  3241.                 }
  3242.                 else
  3243.                 {
  3244.                     logline(ACLOG_INFO,"[%s] %s sent map %s, not written to file",
  3245.                                 clients[sender]->hostname, clients[sender]->name, text);
  3246.                 }
  3247.                 p.len += mapsize + cfgsizegz;
  3248.                 break;
  3249.             }
  3250.  
  3251.             case SV_RECVMAP:
  3252.             {
  3253.                 ENetPacket *mappacket = getmapserv(cl->clientnum);
  3254.                 if(mappacket)
  3255.                 {
  3256.                     resetflag(cl->clientnum); // drop ctf flag
  3257.                     // save score
  3258.                     savedscore *sc = findscore(*cl, true);
  3259.                     if(sc) sc->save(cl->state);
  3260.                     // resend state properly
  3261.                     sendpacket(cl->clientnum, 2, mappacket);
  3262.                     cl->mapchange();
  3263.                     sendwelcome(cl, 2, true);
  3264.  
  3265.                 }
  3266.                 else sendservmsg("no map to get", cl->clientnum);
  3267.                 break;
  3268.             }
  3269.  
  3270.                     case SV_FLAGACTION:
  3271.                     {
  3272.                         int action = getint(p);
  3273.                         int flag = getint(p);
  3274.                         if(!m_flags || flag < 0 || flag > 1 || action < 0 || action > FA_NUM) break;
  3275.                             flagaction(flag, action, sender);
  3276.                             break;
  3277.                     }
  3278.  
  3279.             case SV_SETADMIN:
  3280.                     {
  3281.                             bool claim = getint(p) != 0;
  3282.                             getstring(text, p);
  3283.                 changeclientrole(sender, claim ? CR_ADMIN : CR_DEFAULT, text);
  3284.                             break;
  3285.                     }
  3286.  
  3287.             case SV_CALLVOTE:
  3288.             {
  3289.                 voteinfo *vi = new voteinfo;
  3290.                 int type = getint(p);
  3291.                 switch(type)
  3292.                 {
  3293.                     case SA_MAP:
  3294.                     {
  3295.                         getstring(text, p);
  3296.                         filtertext(text, text);
  3297.                         int mode = getint(p);
  3298.                         if(mode==GMODE_DEMO) vi->action = new demoplayaction(text);
  3299.                         else vi->action = new mapaction(newstring(text), mode, sender);
  3300.                         break;
  3301.                     }
  3302.                     case SA_KICK:
  3303.                         vi->action = new kickaction(getint(p));
  3304.                         break;
  3305.                     case SA_BAN:
  3306.                         vi->action = new banaction(getint(p));
  3307.                         break;
  3308.                     case SA_REMBANS:
  3309.                         vi->action = new removebansaction();
  3310.                         break;
  3311.                     case SA_MASTERMODE:
  3312.                         vi->action = new mastermodeaction(getint(p));
  3313.                         break;
  3314.                     case SA_AUTOTEAM:
  3315.                         vi->action = new autoteamaction(getint(p) > 0);
  3316.                         break;
  3317.                     case SA_SHUFFLETEAMS:
  3318.                         vi->action = new shuffleteamaction();
  3319.                         break;
  3320.                     case SA_FORCETEAM:
  3321.                         vi->action = new forceteamaction(getint(p), sender);
  3322.                         break;
  3323.                     case SA_GIVEADMIN:
  3324.                         vi->action = new giveadminaction(getint(p));
  3325.                         break;
  3326.                     case SA_RECORDDEMO:
  3327.                         vi->action = new recorddemoaction(getint(p)!=0);
  3328.                         break;
  3329.                     case SA_STOPDEMO:
  3330.                         vi->action = new stopdemoaction();
  3331.                         break;
  3332.                     case SA_CLEARDEMOS:
  3333.                         vi->action = new cleardemosaction(getint(p));
  3334.                         break;
  3335.                     case SA_SERVERDESC:
  3336.                         getstring(text, p);
  3337.                         filtertext(text, text);
  3338.                         vi->action = new serverdescaction(newstring(text), sender);
  3339.                         break;
  3340.                 }
  3341.                 vi->owner = sender;
  3342.                 vi->callmillis = servmillis;
  3343.                 MSG_PACKET(msg);
  3344.                 if(!scallvote(vi, msg)) delete vi;
  3345.                 if(!msg->referenceCount) enet_packet_destroy(msg);
  3346.                 break;
  3347.             }
  3348.  
  3349.             case SV_VOTE:
  3350.             {
  3351.                 int n = getint(p);
  3352.                 MSG_PACKET(msg);
  3353.                 svote(sender, n, msg);
  3354.                 if(valid_client(sender) && !msg->referenceCount) enet_packet_destroy(msg); // check sender existence first because he might have been disconnected due to a vote
  3355.                 break;
  3356.             }
  3357.  
  3358.             case SV_WHOIS:
  3359.             {
  3360.                 sendwhois(sender, getint(p));
  3361.                 break;
  3362.             }
  3363.  
  3364.             case SV_LISTDEMOS:
  3365.                 listdemos(sender);
  3366.                 break;
  3367.  
  3368.             case SV_GETDEMO:
  3369.                 senddemo(sender, getint(p));
  3370.                 break;
  3371.  
  3372.             case SV_EXTENSION:
  3373.             {
  3374.                 // AC server extensions
  3375.                 //
  3376.                 // rules:
  3377.                 // 1. extensions MUST NOT modify gameplay or the beavior of the game in any way
  3378.                 // 2. extensions may ONLY be used to extend or automate server administration tasks
  3379.                 // 3. extensions may ONLY operate on the server and must not send any additional data to the connected clients
  3380.                 // 4. extensions not adhering to these rules may cause the hosting server being banned from the masterserver
  3381.                 //
  3382.                 // also note that there is no guarantee that custom extensions will work in future AC versions
  3383.  
  3384.  
  3385.                 getstring(text, p, 64);
  3386.                 char *ext = text;   // extension specifier in the form of OWNER::EXTENSION, see sample below
  3387.                 int n = getint(p);  // length of data after the specifier
  3388.                 if(n > 50) return;
  3389.  
  3390.                 // sample
  3391.                 if(!strcmp(ext, "driAn::writelog"))
  3392.                 {
  3393.                     // owner:       driAn - root@sprintf.org
  3394.                     // extension:   writelog - WriteLog v1.0
  3395.                     // description: writes a custom string to the server log
  3396.                     // access:      requires admin privileges
  3397.                     // usage:       /serverextension driAn::writelog "your log message here.."
  3398.  
  3399.                     getstring(text, p, n);
  3400.                     if(valid_client(sender) && clients[sender]->role==CR_ADMIN) logline(ACLOG_INFO, "%s", text);
  3401.                 }
  3402.                 else if(!strcmp(ext, "official::clientstring"))
  3403.                 {
  3404.                     // not for public use
  3405.  
  3406.                     if(valid_client(sender))
  3407.                     {
  3408.                         client *c = clients[sender];
  3409.                         int version = getint(p);
  3410.                         int cdefs = getint(p);
  3411.                         logline(ACLOG_INFO, "[%s] runs AC %d (defs: %02x)", c->hostname, version, cdefs);
  3412.                     }
  3413.                 }
  3414.  
  3415.                 // add other extensions here
  3416.  
  3417.                 else for(; n > 0; n--) getint(p); // ignore unknown extensions
  3418.  
  3419.                 break;
  3420.             }
  3421.  
  3422.             default:
  3423.             {
  3424.                 int size = msgsizelookup(type);
  3425.                 if(size==-1) { if(sender>=0) disconnect_client(sender, DISC_TAGT); return; }
  3426.                 loopi(size-1) getint(p);
  3427.                 QUEUE_MSG;
  3428.                 break;
  3429.             }
  3430.         }
  3431.     }
  3432.  
  3433.     if(p.overread() && sender>=0) disconnect_client(sender, DISC_EOP);
  3434.  
  3435.     #ifdef _DEBUG
  3436.     protocoldebug(false);
  3437.     #endif
  3438. }
  3439.  
  3440. void localclienttoserver(int chan, ENetPacket *packet)
  3441. {
  3442.     process(packet, 0, chan);
  3443. }
  3444.  
  3445. client &addclient()
  3446. {
  3447.     client *c = NULL;
  3448.     loopv(clients) if(clients[i]->type==ST_EMPTY) { c = clients[i]; break; }
  3449.     if(!c)
  3450.     {
  3451.         c = new client;
  3452.         c->clientnum = clients.length();
  3453.         clients.add(c);
  3454.     }
  3455.     c->reset();
  3456.     return *c;
  3457. }
  3458.  
  3459. void checkintermission()
  3460. {
  3461.     if(minremain>0)
  3462.     {
  3463.         minremain = gamemillis>=gamelimit || forceintermission ? 0 : (gamelimit - gamemillis + 60000 - 1)/60000;
  3464.         sendf(-1, 1, "ri2", SV_TIMEUP, minremain);
  3465.     }
  3466.     if(!interm && minremain<=0) interm = gamemillis+10000;
  3467.     forceintermission = false;
  3468. }
  3469.  
  3470. void resetserverifempty()
  3471. {
  3472.     loopv(clients) if(clients[i]->type!=ST_EMPTY) return;
  3473.     resetmap("", 0, 10, false);
  3474.     mastermode = MM_OPEN;
  3475.     autoteam = true;
  3476.     nextmapname[0] = '\0';
  3477. }
  3478.  
  3479. void sendworldstate()
  3480. {
  3481.     static enet_uint32 lastsend = 0;
  3482.     if(clients.empty()) return;
  3483.     enet_uint32 curtime = enet_time_get()-lastsend;
  3484.     if(curtime<40) return;
  3485.     bool flush = buildworldstate();
  3486.     lastsend += curtime - (curtime%40);
  3487.     if(flush) enet_host_flush(serverhost);
  3488.     if(demorecord) recordpackets = true; // enable after 'old' worldstate is sent
  3489. }
  3490.  
  3491. void rereadcfgs(void)
  3492. {
  3493.     readscfg(NULL);
  3494.     readpwdfile(NULL);
  3495.     readipblacklist(NULL);
  3496.     nbl.readnickblacklist(NULL);
  3497. }
  3498.  
  3499. void loggamestatus(const char *reason)
  3500. {
  3501.     int fragscore[2] = {0, 0}, flagscore[2] = {0, 0}, pnum[2] = {0, 0}, n;
  3502.     string text;
  3503.     s_sprintf(text)("%d minutes remaining", minremain);
  3504.     logline(ACLOG_INFO, "");
  3505.     logline(ACLOG_INFO, "Game status: %s on %s, %s, %s%c %s",
  3506.                       modestr(gamemode), smapname, reason ? reason : text, mmfullname(mastermode), custom_servdesc ? ',' : '\0', servdesc_current);
  3507.     logline(ACLOG_INFO, "cn name             %s%sfrag death %sping role    host", m_teammode ? "team " : "", m_flags ? "flag " : "", m_teammode ? "tk " : "");
  3508.     loopv(clients)
  3509.     {
  3510.         client &c = *clients[i];
  3511.         if(c.type == ST_EMPTY || !c.name[0]) continue;
  3512.         s_sprintf(text)("%2d %-16s ", c.clientnum, c.name);         // cn name
  3513.         if(m_teammode) s_strcatf(text, "%-4s ", c.team);            // team
  3514.         if(m_flags) s_strcatf(text, "%4d ", c.state.flagscore);     // flag
  3515.         s_strcatf(text, "%4d %5d", c.state.frags, c.state.deaths);  // frag death
  3516.         if(m_teammode) s_strcatf(text, " %2d", c.state.teamkills);  // tk
  3517.         logline(ACLOG_INFO, "%s%5d %s  %s", text, c.ping, c.role == CR_ADMIN ? "admin " : "normal", c.hostname);
  3518.         n = team_int(c.team);
  3519.         flagscore[n] += c.state.flagscore;
  3520.         fragscore[n] += c.state.frags;
  3521.         pnum[n] += 1;
  3522.     }
  3523.     if(m_teammode)
  3524.     {
  3525.         loopi(2) logline(ACLOG_INFO, "Team %4s:%3d players,%5d frags%c%5d flags", team_string(i), pnum[i], fragscore[i], m_flags ? ',' : '\0', flagscore[i]);
  3526.     }
  3527.     logline(ACLOG_INFO, "");
  3528. }
  3529.  
  3530. void serverslice(uint timeout)   // main server update, called from cube main loop in sp, or dedicated server loop
  3531. {
  3532. #ifdef STANDALONE
  3533.     int nextmillis = (int)enet_time_get();
  3534.     if(svcctrl) svcctrl->keepalive();
  3535. #else
  3536.     int nextmillis = isdedicated ? (int)enet_time_get() : lastmillis;
  3537. #endif
  3538.     int diff = nextmillis - servmillis;
  3539.     gamemillis += diff;
  3540.     servmillis = nextmillis;
  3541.  
  3542.     if(m_demo) readdemo();
  3543.  
  3544.     if(minremain>0)
  3545.     {
  3546.         processevents();
  3547.         checkitemspawns(diff);
  3548.         bool ktfflagingame = false;
  3549.         if(m_flags) loopi(2)
  3550.         {
  3551.             sflaginfo &f = sflaginfos[i];
  3552.             if(f.state == CTFF_DROPPED && gamemillis-f.lastupdate > (m_ctf ? 30000 : 10000)) flagaction(i, FA_RESET, -1);
  3553.             if(m_htf && f.state == CTFF_INBASE && gamemillis-f.lastupdate > (clnumflagspawn[0] && clnumflagspawn[1] ? 10000 : 1000))
  3554.             {
  3555.                 htf_forceflag(i);
  3556.             }
  3557.             if(m_ktf && f.state == CTFF_STOLEN && gamemillis-f.lastupdate > 15000)
  3558.             {
  3559.                 flagaction(i, FA_SCORE, -1);
  3560.             }
  3561.             if(f.state == CTFF_INBASE || f.state == CTFF_STOLEN) ktfflagingame = true;
  3562.         }
  3563.         if(m_ktf && !ktfflagingame) flagaction(rnd(2), FA_RESET, -1); // ktf flag watchdog
  3564.         if(m_arena) arenacheck();
  3565.     }
  3566.  
  3567.     if(curvote)
  3568.     {
  3569.         if(!curvote->isalive()) curvote->evaluate(true);
  3570.         if(curvote->result!=VOTE_NEUTRAL) DELETEP(curvote);
  3571.     }
  3572.  
  3573.     int nonlocalclients = numnonlocalclients();
  3574.  
  3575.     if(forceintermission || ((smode>1 || (gamemode==0 && nonlocalclients)) && gamemillis-diff>0 && gamemillis/60000!=(gamemillis-diff)/60000))
  3576.         checkintermission();
  3577.     if(interm && gamemillis>interm)
  3578.     {
  3579.         loggamestatus("game finished");
  3580.         if(demorecord) enddemorecord();
  3581.         interm = 0;
  3582.  
  3583.         //start next game
  3584.         if(nextmapname[0]) resetmap(nextmapname, nextgamemode);
  3585.         else if(configsets.length()) nextcfgset();
  3586.         else loopv(clients) if(clients[i]->type!=ST_EMPTY)
  3587.         {
  3588.             sendf(i, 1, "rii", SV_MAPRELOAD, 0);    // ask a client to trigger map reload
  3589.             mapreload = true;
  3590.             break;
  3591.         }
  3592.     }
  3593.  
  3594.     resetserverifempty();
  3595.  
  3596.     if(!isdedicated) return;     // below is network only
  3597.  
  3598.     serverms(smode, numclients(), minremain, smapname, servmillis, serverhost->address, SERVER_PROTOCOL_VERSION);
  3599.  
  3600.     if(autoteam && m_teammode && !m_arena && !interm && servmillis - lastfillup > 5000 && refillteams()) lastfillup = servmillis;
  3601.  
  3602.     if(servmillis-laststatus>60*1000)   // display bandwidth stats, useful for server ops
  3603.     {
  3604.         laststatus = servmillis;
  3605.         rereadcfgs();
  3606.         if(nonlocalclients || bsend || brec)
  3607.         {
  3608.             if(nonlocalclients) loggamestatus(NULL);
  3609.             logline(ACLOG_INFO, "Status at %s: %d remote clients, %.1f send, %.1f rec (K/sec)", timestring(true, "%d-%m-%Y %H:%M:%S"), nonlocalclients, bsend/60.0f/1024, brec/60.0f/1024);
  3610.         }
  3611.         bsend = brec = 0;
  3612.     }
  3613.  
  3614.     ENetEvent event;
  3615.     bool serviced = false;
  3616.     while(!serviced)
  3617.     {
  3618.         if(enet_host_check_events(serverhost, &event) <= 0)
  3619.         {
  3620.             if(enet_host_service(serverhost, &event, timeout) <= 0) break;
  3621.             serviced = true;
  3622.         }
  3623.         switch(event.type)
  3624.         {
  3625.             case ENET_EVENT_TYPE_CONNECT:
  3626.             {
  3627.                 client &c = addclient();
  3628.                 c.type = ST_TCPIP;
  3629.                 c.peer = event.peer;
  3630.                 c.peer->data = (void *)(size_t)c.clientnum;
  3631.                 c.connectmillis = servmillis;
  3632.                 c.salt = rand()*((servmillis%1000)+1);
  3633.                                 char hn[1024];
  3634.                                 s_strcpy(c.hostname, (enet_address_get_host_ip(&c.peer->address, hn, sizeof(hn))==0) ? hn : "unknown");
  3635.                 logline(ACLOG_INFO,"[%s] client connected", c.hostname);
  3636.                 sendinits2c(c);
  3637.                                 break;
  3638.             }
  3639.  
  3640.             case ENET_EVENT_TYPE_RECEIVE:
  3641.                         {
  3642.                 brec += (int)event.packet->dataLength;
  3643.                                 int cn = (int)(size_t)event.peer->data;
  3644.                                 if(valid_client(cn)) process(event.packet, cn, event.channelID);
  3645.                 if(event.packet->referenceCount==0) enet_packet_destroy(event.packet);
  3646.                 break;
  3647.                         }
  3648.  
  3649.             case ENET_EVENT_TYPE_DISCONNECT:
  3650.             {
  3651.                                 int cn = (int)(size_t)event.peer->data;
  3652.                                 if(!valid_client(cn)) break;
  3653.                 disconnect_client(cn);
  3654.                 break;
  3655.             }
  3656.  
  3657.             default:
  3658.                 break;
  3659.         }
  3660.     }
  3661.     sendworldstate();
  3662. }
  3663.  
  3664. void cleanupserver()
  3665. {
  3666.     if(serverhost) enet_host_destroy(serverhost);
  3667.     if(svcctrl)
  3668.     {
  3669.         svcctrl->stop();
  3670.         DELETEP(svcctrl);
  3671.     }
  3672.     exitlogging();
  3673. }
  3674.  
  3675. int getpongflags(enet_uint32 ip)
  3676. {
  3677.     int flags = mastermode << PONGFLAG_MASTERMODE;
  3678.     flags |= scl.serverpassword[0] ? 1 << PONGFLAG_PASSWORD : 0;
  3679.     loopv(bans) if(bans[i].address.host == ip) { flags |= 1 << PONGFLAG_BANNED; break; }
  3680.     flags |= checkipblacklist(ip) ? 1 << PONGFLAG_BLACKLIST : 0;
  3681.     return flags;
  3682. }
  3683.  
  3684. void extping_namelist(ucharbuf &p)
  3685. {
  3686.     loopv(clients)
  3687.     {
  3688.         if(clients[i]->type == ST_TCPIP && clients[i]->isauthed) sendstring(clients[i]->name, p);
  3689.     }
  3690.     sendstring("", p);
  3691. }
  3692.  
  3693. #define MAXINFOLINELEN 100  // including color codes
  3694.  
  3695. const char *readserverinfo(const char *lang)
  3696. {
  3697.     s_sprintfd(fname)("%s_%s.txt", scl.infopath, lang);
  3698.     path(fname);
  3699.     int len, n;
  3700.     char *c, *s, *t, *buf = loadfile(fname, &len);
  3701.     if(!buf) return NULL;
  3702.     char *nbuf = new char[len + 2];
  3703.     for(t = nbuf, s = strtok(buf, "\n\r"); s; s = strtok(NULL, "\n\r"))
  3704.     {
  3705.         c = strstr(s, "//");
  3706.         if(c) *c = '\0'; // strip comments
  3707.         for(n = strlen(s) - 1; n >= 0 && s[n] == ' '; n--) s[n] = '\0'; // strip trailing blanks
  3708.         filterrichtext(t, s + strspn(s, " "), MAXINFOLINELEN); // skip leading blanks
  3709.         n = strlen(t);
  3710.         if(n) t += n + 1;
  3711.     }
  3712.     *t = '\0';
  3713.     delete[] buf;
  3714.     if(!*nbuf) DELETEA(nbuf);
  3715.     return nbuf;
  3716. }
  3717.  
  3718. struct serverinfotext { char lang[3]; const char *info; };
  3719. vector<serverinfotext> serverinfotexts;
  3720.  
  3721. const char *getserverinfo(const char *lang)
  3722. {
  3723.     if(!islower(lang[0]) || !islower(lang[1])) return NULL;
  3724.     serverinfotext s;
  3725.     loopi(3) s.lang[i] = lang[i];
  3726.     loopv(serverinfotexts)
  3727.     {
  3728.         if(!strcmp(s.lang, serverinfotexts[i].lang) && serverinfotexts[i].info) return serverinfotexts[i].info;
  3729.     }
  3730.     s.info = readserverinfo(lang);
  3731.     serverinfotexts.add(s);
  3732.     return s.info;
  3733. }
  3734.  
  3735. void extping_serverinfo(ucharbuf &pi, ucharbuf &po)
  3736. {
  3737.     char lang[3];
  3738.     lang[0] = tolower(getint(pi)); lang[1] = tolower(getint(pi)); lang[2] = '\0';
  3739.     const char *reslang = lang, *buf = getserverinfo(lang); // try client language
  3740.     if(!buf) buf = getserverinfo(reslang = "en");     // try english
  3741.     sendstring(buf ? reslang : "", po);
  3742.     if(buf)
  3743.     {
  3744.         for(const char *c = buf; *c && po.remaining() > MAXINFOLINELEN + 10; c += strlen(c) + 1) sendstring(c, po);
  3745.         sendstring("", po);
  3746.     }
  3747. }
  3748.  
  3749. void extping_maprot(ucharbuf &po)
  3750. {
  3751.     putint(po, CONFIG_MAXPAR);
  3752.     string text;
  3753.     bool abort = false;
  3754.     loopv(configsets)
  3755.     {
  3756.         if(po.remaining() < 100) abort = true;
  3757.         configset &c = configsets[i];
  3758.         filtertext(text, c.mapname, 0);
  3759.         text[30] = '\0';
  3760.         sendstring(abort ? "-- list truncated --" : text, po);
  3761.         loopi(CONFIG_MAXPAR) putint(po, c.par[i]);
  3762.         if(abort) break;
  3763.     }
  3764.     sendstring("", po);
  3765. }
  3766.  
  3767. void extinfo_cnbuf(ucharbuf &p, int cn)
  3768. {
  3769.     if(cn == -1) // add all available player ids
  3770.     {
  3771.         loopv(clients) if(clients[i]->type != ST_EMPTY)
  3772.             putint(p,clients[i]->clientnum);
  3773.     }
  3774.     else if(valid_client(cn)) // add single player only
  3775.     {
  3776.         putint(p,clients[cn]->clientnum);
  3777.     }
  3778. }
  3779.  
  3780. void extinfo_statsbuf(ucharbuf &p, int pid, int bpos, ENetSocket &pongsock, ENetAddress &addr, ENetBuffer &buf, int len)
  3781. {
  3782.     loopv(clients)
  3783.     {
  3784.         if(clients[i]->type != ST_TCPIP) continue;
  3785.         if(pid>-1 && clients[i]->clientnum!=pid) continue;
  3786.  
  3787.         putint(p,EXT_PLAYERSTATS_RESP_STATS);  // send player stats following
  3788.         putint(p,clients[i]->clientnum);  //add player id
  3789.         putint(p,clients[i]->ping);             //Ping
  3790.         sendstring(clients[i]->name,p);         //Name
  3791.         sendstring(clients[i]->team,p);         //Team
  3792.         putint(p,clients[i]->state.frags);      //Frags
  3793.         putint(p,clients[i]->state.flagscore);  //Flagscore
  3794.         putint(p,clients[i]->state.deaths);     //Death
  3795.         putint(p,clients[i]->state.teamkills);  //Teamkills
  3796.         putint(p,clients[i]->state.damage*100/max(clients[i]->state.shotdamage,1)); //Accuracy
  3797.         putint(p,clients[i]->state.health);     //Health
  3798.         putint(p,clients[i]->state.armour);     //Armour
  3799.         putint(p,clients[i]->state.gunselect);  //Gun selected
  3800.         putint(p,clients[i]->role);             //Role
  3801.         putint(p,clients[i]->state.state);      //State (Alive,Dead,Spawning,Lagged,Editing)
  3802.         uint ip = clients[i]->peer->address.host; // only 3 byte of the ip address (privacy protected)
  3803.         p.put((uchar*)&ip,3);
  3804.  
  3805.         buf.dataLength = len + p.length();
  3806.         enet_socket_send(pongsock, &addr, &buf, 1);
  3807.  
  3808.         if(pid>-1) break;
  3809.         p.len=bpos;
  3810.     }
  3811. }
  3812.  
  3813. void extinfo_teamscorebuf(ucharbuf &p)
  3814. {
  3815.     putint(p, m_teammode ? EXT_ERROR_NONE : EXT_ERROR);
  3816.     putint(p, gamemode);
  3817.     putint(p, minremain);
  3818.     if(!m_teammode) return;
  3819.  
  3820.     cvector teams;
  3821.     bool addteam;
  3822.     loopv(clients) if(clients[i]->type!=ST_EMPTY)
  3823.     {
  3824.         addteam = true;
  3825.         loopvj(teams)
  3826.         {
  3827.             if(strcmp(clients[i]->team,teams[j])==0 || !clients[i]->team[0])
  3828.             {
  3829.                 addteam = false;
  3830.                 break;
  3831.             }
  3832.         }
  3833.         if(addteam) teams.add(clients[i]->team);
  3834.     }
  3835.  
  3836.     loopv(teams)
  3837.     {
  3838.         sendstring(teams[i],p); //team
  3839.         int fragscore = 0;
  3840.         int flagscore = 0;
  3841.         loopvj(clients) if(clients[j]->type!=ST_EMPTY)
  3842.         {
  3843.             if(!(strcmp(clients[j]->team,teams[i])==0)) continue;
  3844.             fragscore += clients[j]->state.frags;
  3845.             flagscore += clients[j]->state.flagscore;
  3846.         }
  3847.         putint(p,fragscore); //add fragscore per team
  3848.         if(m_flags) //when capture mode
  3849.         {
  3850.             putint(p,flagscore); //add flagscore per team
  3851.         }
  3852.         else //all other team modes
  3853.         {
  3854.             putint(p,-1); //flagscore not available
  3855.         }
  3856.         putint(p,-1);
  3857.     }
  3858. }
  3859.  
  3860.  
  3861. #ifndef STANDALONE
  3862. void localdisconnect()
  3863. {
  3864.     loopv(clients) if(clients[i]->type==ST_LOCAL) clients[i]->zap();
  3865. }
  3866.  
  3867. void localconnect()
  3868. {
  3869.     modprotocol = false;
  3870.     client &c = addclient();
  3871.     c.type = ST_LOCAL;
  3872.     c.role = CR_ADMIN;
  3873.     s_strcpy(c.hostname, "local");
  3874.     sendinits2c(c);
  3875. }
  3876. #endif
  3877.  
  3878. void initserver(bool dedicated)
  3879. {
  3880.     srand(time(NULL));
  3881.  
  3882.     string identity;
  3883.     if(scl.logident[0]) filtertext(identity, scl.logident, 0);
  3884.     else s_sprintf(identity)("%s#%d", scl.ip[0] ? scl.ip : "local", scl.serverport);
  3885.     int conthres = scl.verbose > 1 ? ACLOG_DEBUG : (scl.verbose ? ACLOG_VERBOSE : ACLOG_INFO);
  3886.     if(dedicated && !initlogging(identity, scl.syslogfacility, conthres, scl.filethres, scl.syslogthres, scl.logtimestamp))
  3887.         printf("WARNING: logging not started!\n");
  3888.     logline(ACLOG_INFO, "logging local AssaultCube server (version %d, protocol %d/%d) now..", AC_VERSION, SERVER_PROTOCOL_VERSION, EXT_VERSION);
  3889.  
  3890.     s_strcpy(servdesc_current, scl.servdesc_full);
  3891.     servermsinit(scl.master ? scl.master : AC_MASTER_URI, scl.ip, CUBE_SERVINFO_PORT(scl.serverport), dedicated);
  3892.  
  3893.     if((isdedicated = dedicated))
  3894.     {
  3895.         ENetAddress address = { ENET_HOST_ANY, scl.serverport };
  3896.         if(scl.ip[0] && enet_address_set_host(&address, scl.ip)<0) logline(ACLOG_WARNING, "server ip not resolved!");
  3897.         serverhost = enet_host_create(&address, scl.maxclients+1, 0, scl.uprate);
  3898.         if(!serverhost) fatal("could not create server host");
  3899.         loopi(scl.maxclients) serverhost->peers[i].data = (void *)-1;
  3900.  
  3901.         readscfg(scl.maprot);
  3902.         readpwdfile(scl.pwdfile);
  3903.         readipblacklist(scl.blfile);
  3904.         nbl.readnickblacklist(scl.nbfile);
  3905.         getserverinfo("en"); // cache 'en' serverinfo
  3906.         if(scl.demoeverymatch) logline(ACLOG_VERBOSE, "recording demo of every game (holding up to %d in memory)", scl.maxdemos);
  3907.         if(scl.demopath[0]) logline(ACLOG_VERBOSE,"all recorded demos will be written to: \"%s\"", scl.demopath);
  3908.         if(scl.voteperm[0]) logline(ACLOG_VERBOSE,"vote permission string: \"%s\"", scl.voteperm);
  3909.         logline(ACLOG_VERBOSE,"server description: \"%s\"", scl.servdesc_full);
  3910.         if(scl.servdesc_pre[0] || scl.servdesc_suf[0]) logline(ACLOG_VERBOSE,"custom server description: \"%sCUSTOMPART%s\"", scl.servdesc_pre, scl.servdesc_suf);
  3911.         logline(ACLOG_VERBOSE,"maxclients: %d, kick threshold: %d, ban threshold: %d", scl.maxclients, scl.kickthreshold, scl.banthreshold);
  3912.         if(scl.master) logline(ACLOG_VERBOSE,"master server URL: \"%s\"", scl.master);
  3913.         if(scl.serverpassword[0]) logline(ACLOG_VERBOSE,"server password: \"%s\"", hiddenpwd(scl.serverpassword));
  3914.     }
  3915.  
  3916.     resetserverifempty();
  3917.  
  3918.     if(isdedicated)       // do not return, this becomes main loop
  3919.     {
  3920.         #ifdef WIN32
  3921.         SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
  3922.         #endif
  3923.         logline(ACLOG_INFO, "dedicated server started, waiting for clients...");
  3924.         logline(ACLOG_INFO, "Ctrl-C to exit");
  3925.         atexit(enet_deinitialize);
  3926.         atexit(cleanupserver);
  3927.         enet_time_set(0);
  3928.         for(;;) serverslice(5);
  3929.     }
  3930. }
  3931.  
  3932. #ifdef STANDALONE
  3933.  
  3934. void localservertoclient(int chan, uchar *buf, int len) {}
  3935. void fatal(const char *s, ...)
  3936. {
  3937.     s_sprintfdlv(msg,s,s);
  3938.     s_sprintfd(out)("AssaultCube fatal error: %s", msg);
  3939.     if (logline(ACLOG_ERROR, "%s", out));
  3940.     else puts(out);
  3941.     cleanupserver();
  3942.     exit(EXIT_FAILURE);
  3943. }
  3944.  
  3945. int main(int argc, char **argv)
  3946. {
  3947.     #ifdef WIN32
  3948.     //atexit((void (__cdecl *)(void))_CrtDumpMemoryLeaks);
  3949.     #ifndef _DEBUG
  3950.     #ifndef __GNUC__
  3951.     __try {
  3952.     #endif
  3953.     #endif
  3954.     #endif
  3955.  
  3956.     const char *service = NULL;
  3957.  
  3958.     for(int i = 1; i<argc; i++)
  3959.     {
  3960.         if(!scl.checkarg(argv[i]))
  3961.         {
  3962.             char *a = &argv[i][2];
  3963.             if(!scl.checkarg(argv[i]) && argv[i][0]=='-') switch(argv[i][1])
  3964.             {
  3965.                 case '-':
  3966.                     if(!strncmp(argv[i], "--wizard", 8))
  3967.                     {
  3968.                         return wizardmain(argc-1, argv+1);
  3969.                     }
  3970.                     break;
  3971.                 case 'S': service = a; break;
  3972.                 default: printf("WARNING: unknown commandline option\n");
  3973.             }
  3974.             else printf("WARNING: unknown commandline argument\n");
  3975.         }
  3976.     }
  3977.  
  3978.     if(service && !svcctrl)
  3979.     {
  3980.         #ifdef WIN32
  3981.         svcctrl = new winservice(service);
  3982.         #endif
  3983.         if(svcctrl)
  3984.         {
  3985.             svcctrl->argc = argc; svcctrl->argv = argv;
  3986.             svcctrl->start();
  3987.         }
  3988.     }
  3989.  
  3990.     if(enet_initialize()<0) fatal("Unable to initialise network module");
  3991.     initserver(true);
  3992.     return EXIT_SUCCESS;
  3993.  
  3994.     #if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__)
  3995.     } __except(stackdumper(0, GetExceptionInformation()), EXCEPTION_CONTINUE_SEARCH) { return 0; }
  3996.     #endif
  3997. }
  3998. #endif
Submit a correction or amendment below. Make A New Post