home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2007 September / maximum-cd-2007-09.iso / Assets / data / AssaultCube_v0.93.exe / source / src / server.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2007-06-05  |  38.2 KB  |  1,321 lines

  1. // server.cpp: little more than enhanced multicaster
  2. // runs dedicated or as client coroutine
  3.  
  4. #include "cube.h" 
  5.  
  6. #define valid_client(c) (clients.inrange(c) && clients[c]->type!=ST_EMPTY)
  7. #define valid_flag(f) (f >= 0 && f < 2)
  8.  
  9. enum { ST_EMPTY, ST_LOCAL, ST_TCPIP };
  10. enum { MM_OPEN, MM_PRIVATE, MM_NUM };
  11.  
  12. int mastermode = MM_OPEN;
  13.  
  14. struct clientscore
  15. {
  16.     int frags, flags, lifesequence;
  17.  
  18.     void reset()
  19.     {
  20.         frags = flags = lifesequence = 0;
  21.     }
  22. };
  23.  
  24. struct savedscore : clientscore
  25. {
  26.     string name;
  27.     uint ip;
  28. };
  29.  
  30. static vector<savedscore> scores;
  31.  
  32. struct client                   // server side version of "dynent" type
  33. {
  34.     int type;
  35.     int clientnum;
  36.     ENetPeer *peer;
  37.     string hostname;
  38.     string mapvote;
  39.     string name, team;
  40.     int modevote;
  41.     uint pos[3];
  42.     clientscore score;
  43.     bool arenadeath;
  44.     int role;
  45.     bool isauthed; // for passworded servers
  46.     vector<uchar> position, messages;
  47.  
  48.     void reset()
  49.     {
  50.         name[0] = 0;
  51.         score.reset();
  52.         arenadeath = true;
  53.         position.setsizenodelete(0);
  54.         messages.setsizenodelete(0);
  55.         isauthed = false;
  56.         role = CR_DEFAULT;
  57.     }
  58. };
  59.  
  60. vector<client *> clients;
  61.  
  62. struct worldstate
  63. {
  64.     enet_uint32 uses;
  65.     vector<uchar> positions, messages;
  66. };
  67.  
  68. vector<worldstate *> worldstates;
  69.  
  70. void cleanworldstate(ENetPacket *packet)
  71. {
  72.    loopv(worldstates)
  73.    {
  74.        worldstate *ws = worldstates[i];
  75.        if(packet->data >= ws->positions.getbuf() && packet->data <= &ws->positions.last()) ws->uses--;
  76.        else if(packet->data >= ws->messages.getbuf() && packet->data <= &ws->messages.last()) ws->uses--;
  77.        else continue;
  78.        if(!ws->uses)
  79.        {
  80.            delete ws;
  81.            worldstates.remove(i);
  82.        }
  83.        break;
  84.    }
  85. }
  86.  
  87. int bsend = 0, brec = 0, laststatus = 0, lastsec = 0;
  88.  
  89. void sendpacket(int n, int chan, ENetPacket *packet)
  90. {
  91.     if(n<0)
  92.     {
  93.         loopv(clients) sendpacket(i, chan, packet);
  94.         return;
  95.     }
  96.     switch(clients[n]->type)
  97.     {
  98.         case ST_TCPIP:
  99.         {
  100.             enet_peer_send(clients[n]->peer, chan, packet);
  101.             bsend += (int)packet->dataLength;
  102.             break;
  103.         }
  104.  
  105.         case ST_LOCAL:
  106.             localservertoclient(chan, packet->data, (int)packet->dataLength);
  107.             break;
  108.     }
  109. }
  110.  
  111. static bool reliablemessages = false;
  112.  
  113. bool buildworldstate()
  114. {
  115.     static struct { int posoff, msgoff, msglen; } pkt[MAXCLIENTS];
  116.     worldstate &ws = *new worldstate;
  117.     loopv(clients) 
  118.     {
  119.         client &c = *clients[i];
  120.         if(c.type!=ST_TCPIP) continue;
  121.         if(c.position.empty()) pkt[i].posoff = -1;
  122.         else
  123.         {
  124.             pkt[i].posoff = ws.positions.length();
  125.             loopvj(c.position) ws.positions.add(c.position[j]);
  126.         }
  127.         if(c.messages.empty()) pkt[i].msgoff = -1;
  128.         else
  129.         {
  130.             pkt[i].msgoff = ws.messages.length();
  131.             ucharbuf p = ws.messages.reserve(16);
  132.             putint(p, SV_CLIENT);
  133.             putint(p, c.clientnum);
  134.             putuint(p, c.messages.length());
  135.             ws.messages.addbuf(p);
  136.             loopvj(c.messages) ws.messages.add(c.messages[j]);
  137.             pkt[i].msglen = ws.messages.length()-pkt[i].msgoff;
  138.         }
  139.     }
  140.     int psize = ws.positions.length(), msize = ws.messages.length();
  141.     loopi(psize) { uchar c = ws.positions[i]; ws.positions.add(c); }
  142.     loopi(msize) { uchar c = ws.messages[i]; ws.messages.add(c); }
  143.     ws.uses = 0;
  144.     loopv(clients)
  145.     {
  146.         client &c = *clients[i];
  147.         if(c.type!=ST_TCPIP) continue;
  148.         ENetPacket *packet;
  149.         if(psize && (pkt[i].posoff<0 || psize-c.position.length()>0))
  150.         {
  151.             packet = enet_packet_create(&ws.positions[pkt[i].posoff<0 ? 0 : pkt[i].posoff+c.position.length()],
  152.                                         pkt[i].posoff<0 ? psize : psize-c.position.length(),
  153.                                         ENET_PACKET_FLAG_NO_ALLOCATE);
  154.             sendpacket(c.clientnum, 0, packet);
  155.             if(!packet->referenceCount) enet_packet_destroy(packet);
  156.             else { ++ws.uses; packet->freeCallback = cleanworldstate; }
  157.         }
  158.         c.position.setsizenodelete(0);
  159.  
  160.         if(msize && (pkt[i].msgoff<0 || msize-pkt[i].msglen>0))
  161.         {
  162.             packet = enet_packet_create(&ws.messages[pkt[i].msgoff<0 ? 0 : pkt[i].msgoff+pkt[i].msglen],
  163.                                         pkt[i].msgoff<0 ? msize : msize-pkt[i].msglen,
  164.                                         (reliablemessages ? ENET_PACKET_FLAG_RELIABLE : 0) | ENET_PACKET_FLAG_NO_ALLOCATE);
  165.             sendpacket(c.clientnum, 1, packet);
  166.             if(!packet->referenceCount) enet_packet_destroy(packet);
  167.             else { ++ws.uses; packet->freeCallback = cleanworldstate; }
  168.         }
  169.         c.messages.setsizenodelete(0);
  170.     }
  171.     reliablemessages = false;
  172.     if(!ws.uses)
  173.     {
  174.         delete &ws;
  175.         return false;
  176.     }
  177.     else
  178.     {
  179.         worldstates.add(&ws);
  180.         return true;
  181.     }
  182. }
  183.  
  184. int maxclients = DEFAULTCLIENTS, scorethreshold = -5;
  185. string smapname;
  186.  
  187. char *adminpasswd = NULL, *motd = NULL;
  188.  
  189. int numclients()
  190. {
  191.     int num = 0;
  192.     loopv(clients) if(clients[i]->type!=ST_EMPTY) num++;
  193.     return num;
  194. }
  195.  
  196. void zapclient(int c)
  197. {
  198.     if(!clients.inrange(c)) return;
  199.     clients[c]->type = ST_EMPTY;
  200.     clients[c]->isauthed = false;
  201.     clients[c]->role = CR_DEFAULT;
  202. }
  203.  
  204. int freeteam()
  205. {
  206.     int teamsize[2] = {0, 0};
  207.     loopv(clients) if(clients[i]->type!=ST_EMPTY) teamsize[team_int(clients[i]->team)]++;
  208.     if(teamsize[0] == teamsize[1]) return rnd(2);
  209.     return teamsize[0] < teamsize[1] ? 0 : 1;
  210. }
  211.  
  212. clientscore *findscore(client &c, bool insert)
  213. {
  214.     if(c.type!=ST_TCPIP) return NULL;
  215.     if(!insert) loopv(clients)
  216.     {
  217.         client &o = *clients[i];
  218.         if(o.type!=ST_TCPIP) continue;
  219.         if(o.clientnum!=c.clientnum && o.peer->address.host==c.peer->address.host && !strcmp(o.name, c.name)) return &o.score;
  220.     }
  221.     loopv(scores)
  222.     {
  223.         savedscore &sc = scores[i];
  224.         if(!strcmp(sc.name, c.name) && sc.ip==c.peer->address.host) return ≻
  225.     }
  226.     if(!insert) return NULL;
  227.     savedscore &sc = scores.add();
  228.     sc.reset();
  229.     s_strcpy(sc.name, c.name);
  230.     sc.ip = c.peer->address.host;
  231.     return ≻
  232. }
  233.  
  234. void resetscores()
  235. {
  236.     loopv(clients) if(clients[i]->type==ST_TCPIP)
  237.     {
  238.         clients[i]->score.reset();
  239.     }
  240.     scores.setsize(0);
  241. }
  242.  
  243. vector<server_entity> sents;
  244.  
  245. bool notgotitems = true;        // true when map has changed and waiting for clients to send item
  246.  
  247. // allows the gamemode macros to work with the server mode
  248. #define gamemode smode
  249. int smode = 0;
  250.  
  251. void restoreserverstate(vector<entity> &ents)   // hack: called from savegame code, only works in SP
  252. {
  253.     loopv(sents)
  254.     {
  255.         sents[i].spawned = ents[i].spawned;
  256.         sents[i].spawnsecs = 0;
  257.     } 
  258. }
  259.  
  260. int interm = 0, minremain = 0, mapend = 0;
  261. bool mapreload = false, autoteam = true;
  262.  
  263. string serverpassword = "";
  264.  
  265. bool isdedicated;
  266. ENetHost *serverhost = NULL;
  267.  
  268. void process(ENetPacket *packet, int sender, int chan);
  269.  
  270. void sendf(int cn, int chan, const char *format, ...)
  271. {
  272.     bool reliable = false;
  273.     if(*format=='r') { reliable = true; ++format; }
  274.     ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, reliable ? ENET_PACKET_FLAG_RELIABLE : 0);
  275.     ucharbuf p(packet->data, packet->dataLength);
  276.     va_list args;
  277.     va_start(args, format);
  278.     while(*format) switch(*format++)
  279.     {
  280.         case 'i':
  281.         {
  282.             int n = isdigit(*format) ? *format++-'0' : 1;
  283.             loopi(n) putint(p, va_arg(args, int));
  284.             break;
  285.         }
  286.         case 's': sendstring(va_arg(args, const char *), p); break;
  287.     }
  288.     va_end(args);
  289.     enet_packet_resize(packet, p.length());
  290.     sendpacket(cn, chan, packet);
  291.     if(packet->referenceCount==0) enet_packet_destroy(packet);
  292. }
  293.  
  294. void sendservmsg(char *msg, int client=-1)
  295. {
  296.     sendf(client, 1, "ris", SV_SERVMSG, msg);
  297. }
  298.  
  299. struct sflaginfo
  300. {
  301.     int state;
  302.     int actor_cn;
  303.     int pos[3];
  304.     int lastupdate;
  305. } sflaginfos[2];
  306.  
  307. void sendflaginfo(int flag, int action, int cn = -1)
  308. {
  309.     sflaginfo &f = sflaginfos[flag];
  310.     ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
  311.     ucharbuf p(packet->data, packet->dataLength);
  312.     putint(p, SV_FLAGINFO);
  313.     putint(p, flag);
  314.     putint(p, f.state);
  315.     putint(p, action);
  316.     if(f.state==CTFF_STOLEN || action==SV_FLAGRETURN) putint(p, f.actor_cn);
  317.     else if(f.state==CTFF_DROPPED) loopi(3) putint(p, f.pos[i]);
  318.     enet_packet_resize(packet, p.length());
  319.     sendpacket(cn, 1, packet);
  320.     if(packet->referenceCount==0) enet_packet_destroy(packet);
  321. }
  322.  
  323. void flagaction(int flag, int action, int sender)
  324. {
  325.     if(!valid_flag(flag)) return;
  326.     sflaginfo &f = sflaginfos[flag];
  327.  
  328.     switch(action)
  329.     {
  330.         case SV_FLAGPICKUP:
  331.         {
  332.             if(f.state == CTFF_STOLEN) return;
  333.             f.state = CTFF_STOLEN;
  334.             f.actor_cn = sender;
  335.             break;
  336.         }
  337.         case SV_FLAGDROP:
  338.         {
  339.             if(f.state!=CTFF_STOLEN || (sender != -1 && f.actor_cn != sender)) return;
  340.             f.state = CTFF_DROPPED;
  341.             loopi(3) f.pos[i] = clients[sender]->pos[i];
  342.             break;
  343.         }
  344.         case SV_FLAGRETURN:
  345.         {
  346.             if(f.state!=CTFF_DROPPED) return;
  347.             f.state = CTFF_INBASE;
  348.             f.actor_cn = sender;
  349.             break;
  350.         }
  351.         case SV_FLAGRESET:
  352.         {
  353.             if(sender != -1 && f.actor_cn != sender) return;
  354.             f.state = CTFF_INBASE;
  355.             break;
  356.         }
  357.         case SV_FLAGSCORE:
  358.         {
  359.             if(f.state != CTFF_STOLEN) return;
  360.             f.state = CTFF_INBASE;
  361.             break;
  362.         }
  363.         default: return;
  364.     }
  365.  
  366.     f.lastupdate = lastsec;
  367.     sendflaginfo(flag, action);
  368. }
  369.  
  370. void ctfreset()
  371. {
  372.     loopi(2) 
  373.     {
  374.         sflaginfos[i].actor_cn = 0;
  375.         sflaginfos[i].state = CTFF_INBASE;
  376.         sflaginfos[i].lastupdate = -1;
  377.     }
  378. }
  379.  
  380. int arenaround = 0;
  381.  
  382. void arenareset()
  383. {
  384.     if(!m_arena) return;
  385.  
  386.     arenaround = 0;
  387.     loopv(clients) clients[i]->arenadeath = false;
  388.     sendf(-1, 1, "ri", SV_ARENASPAWN);
  389. }
  390.  
  391. void arenacheck(int secs)
  392. {
  393.     if(!m_arena || interm || secs<arenaround || clients.empty()) return;
  394.  
  395.     if(arenaround)
  396.     {
  397.         arenareset();
  398.         return;
  399.     }
  400.  
  401. #ifndef STANDALONE
  402.     if(m_botmode && clients[0]->type==ST_LOCAL)
  403.     {
  404.         bool alive = false, dead = false;
  405.         loopv(players) if(players[i])
  406.         {
  407.             if(players[i]->state==CS_DEAD) dead = true;
  408.             else alive = true;
  409.         }
  410.         if((dead && !alive) || clients[0]->arenadeath)
  411.         {
  412.             sendf(-1, 1, "riis", SV_ARENAWIN, alive || player1->state!=CS_DEAD ? 1 : 0, player1->state==CS_DEAD ? "" : player1->name);
  413.             arenaround = secs+5;
  414.         }
  415.         return;
  416.     }
  417. #endif
  418.     client *alive = NULL;
  419.     bool dead = false;
  420.     loopv(clients)
  421.     {
  422.         client &c = *clients[i];
  423.         if(c.type==ST_EMPTY) continue;
  424.         if(c.arenadeath) dead = true;
  425.         else if(!alive) alive = &c;
  426.         else if(!m_teammode || strcmp(alive->team, c.team)) return;
  427.     }
  428.     if(!dead) return;
  429.     sendf(-1, 1, "riis", SV_ARENAWIN, alive ? 1 : 0, !alive ? "" : (m_teammode ? alive->team : alive->name));
  430.     arenaround = secs+5;
  431. }
  432.  
  433. void sendteamtext(char *text, int sender)
  434. {
  435.     if(!m_teammode || !clients[sender]->team[0]) return;
  436.     ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
  437.     ucharbuf p(packet->data, packet->dataLength);
  438.     putint(p, SV_TEAMTEXT);
  439.     putint(p, sender);
  440.     sendstring(text, p);
  441.     enet_packet_resize(packet, p.length());
  442.     loopv(clients) if(i!=sender && !strcmp(clients[i]->team, clients[sender]->team)) sendpacket(i, 1, packet);
  443.     if(packet->referenceCount==0) enet_packet_destroy(packet);
  444. }
  445.  
  446. const char *disc_reason(int reason)
  447. {
  448.     static 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?" };
  449.     return reason >= 0 && (size_t)reason < sizeof(disc_reasons)/sizeof(disc_reasons[0]) ? disc_reasons[reason] : "unknown";
  450. }
  451.  
  452. void disconnect_client(int n, int reason = -1)
  453. {
  454.     if(!clients.inrange(n) || clients[n]->type!=ST_TCPIP) return;
  455.     if(m_ctf) loopi(2) if(sflaginfos[i].state==CTFF_STOLEN && sflaginfos[i].actor_cn==n) flagaction(i, SV_FLAGDROP, n);
  456.     client &c = *clients[n];
  457.     clientscore *sc = findscore(c, true);
  458.     if(sc) *sc = c.score;
  459.     if(reason>=0) printf("disconnecting client (%s) [%s]\n", c.hostname, disc_reason(reason));
  460.     else printf("disconnected client (%s)\n", c.hostname);
  461.     c.peer->data = (void *)-1;
  462.     if(reason>=0) enet_peer_disconnect(c.peer, reason);
  463.     zapclient(n);
  464.     sendf(-1, 1, "rii", SV_CDIS, n);
  465. }
  466.  
  467. void resetitems() { sents.setsize(0); notgotitems = true; }
  468.  
  469. bool serverpickup(uint i, int sec, int sender)         // server side item pickup, acknowledge first client that gets it
  470. {
  471.     if(i>=(uint)sents.length()) return false;
  472.     if(sents[i].spawned)
  473.     {
  474.         sents[i].spawned = false;
  475.         sents[i].spawnsecs = sec;
  476.         if(sender>=0) sendf(sender, 1, "rii", SV_ITEMACC, i);
  477.         return true;
  478.     }
  479.     return false;
  480. }
  481.  
  482. struct configset
  483. {
  484.     string mapname;
  485.     int mode;
  486.     int time;
  487.     bool vote;
  488. };
  489.  
  490. vector<configset> configsets;
  491. int curcfgset = -1;
  492.  
  493. void readscfg(char *cfg)
  494. {
  495.     configsets.setsize(0);
  496.  
  497.     string s;
  498.     s_strcpy(s, cfg);
  499.     char *buf = loadfile(path(s), NULL);
  500.     if(!buf) return;
  501.     char *p, *l;
  502.     
  503.     p = buf;
  504.     while((p = strstr(p, "//")) != NULL) // remove comments
  505.         while(p[0] != '\n' && p[0] != '\0') p++[0] = ' ';
  506.     
  507.     l = buf;
  508.     bool lastline = false;
  509.     while((p = strstr(l, "\n")) != NULL || (l[0] && (lastline=true))) // remove empty/invalid lines
  510.     {
  511.         size_t len = lastline ? strlen(l) : p-l;
  512.         string line;
  513.         s_strncpy(line, l, len+1);
  514.         char *d = line;
  515.         int n = 0;
  516.         while((p = strstr(d, ":")) != NULL) { d = p+1; n++; }
  517.         if(n!=3) memset(l, ' ', len+1);
  518.         if(lastline) { l[len+1] = 0; break; }
  519.         l += len+1;
  520.     }
  521.          
  522.     configset c;
  523.     int argc = 0;
  524.     string argv[4];
  525.  
  526.     p = strtok(buf, ":\n\0");
  527.     while(p != NULL)
  528.     {
  529.         strcpy(argv[argc], p);
  530.         if(++argc==4)
  531.         {
  532.             int numspaces;
  533.             for(numspaces = 0; argv[0][numspaces]==' '; numspaces++){} // ingore space crap
  534.             strcpy(c.mapname, argv[0]+numspaces);
  535.             c.mode = atoi(argv[1]);
  536.             c.time = atoi(argv[2]);
  537.             c.vote = atoi(argv[3]) > 0;
  538.             configsets.add(c);
  539.             argc = 0;
  540.         }
  541.         p = strtok(NULL, ":\n\0");
  542.     }
  543. }
  544.  
  545. void resetvotes()
  546. {
  547.     loopv(clients) clients[i]->mapvote[0] = 0;
  548. }
  549.  
  550. void forceteam(int client, int team)
  551. {
  552.     if(!valid_client(client) || team < 0 || team > 1) return;
  553.     sendf(client, 1, "rii", SV_FORCETEAM, team);
  554. }
  555.  
  556. void shuffleteams()
  557. {
  558.     int numplayers = numclients();
  559.     int teamsize[2] = {0, 0};
  560.     loopv(clients) if(clients[i]->type!=ST_EMPTY)
  561.     {
  562.         int team = rnd(2);
  563.         if(teamsize[team] >= numplayers/2) team = team_opposite(team);
  564.         forceteam(i, team);
  565.         teamsize[team]++;
  566.     }
  567. }
  568.  
  569. void resetmap(const char *newname, int newmode, int newtime = -1, bool notify = true)
  570. {
  571.     bool lastteammode = m_teammode;
  572.     smode = newmode;
  573.     minremain = newtime >= 0 ? newtime : (m_teammode ? 15 : 10);
  574.     s_strcpy(smapname, newname);
  575.  
  576.     mapend = lastsec+minremain*60;
  577.     mapreload = false;
  578.     interm = 0;
  579.     laststatus = lastsec-61;
  580.     resetvotes();
  581.     resetitems();
  582.     resetscores();
  583.     ctfreset();
  584.     if(notify) sendf(-1, 1, "risi", SV_MAPCHANGE, smapname, smode);
  585.     if(m_teammode && !lastteammode) shuffleteams();
  586.     arenareset();
  587. }
  588.  
  589. void nextcfgset(bool notify = true) // load next maprotation set
  590. {   
  591.     curcfgset++;
  592.     if(curcfgset>=configsets.length() || curcfgset<0) curcfgset=0;
  593.     
  594.     configset &c = configsets[curcfgset];
  595.     resetmap(c.mapname, c.mode, c.time, notify);
  596. }
  597.  
  598. bool vote(char *map, int reqmode, int sender)
  599. {
  600.     if(!valid_client(sender)) return false;
  601.  
  602.     if(configsets.length() && curcfgset < configsets.length() && !configsets[curcfgset].vote && clients[sender]->role == CR_DEFAULT)
  603.     {
  604.         s_sprintfd(msg)("%s voted, but voting is currently disabled", clients[sender]->name);
  605.         sendservmsg(msg);
  606.         return false;
  607.     }
  608.  
  609.     s_strcpy(clients[sender]->mapvote, map);
  610.     clients[sender]->modevote = reqmode;
  611.     int yes = 0, no = 0; 
  612.     loopv(clients) if(clients[i]->type!=ST_EMPTY)
  613.     {
  614.         if(clients[i]->mapvote[0]) { if(strcmp(clients[i]->mapvote, map)==0 && clients[i]->modevote==reqmode) yes++; else no++; }
  615.         else no++;
  616.     }
  617.     if(yes==1 && no==0) return true;  // single player
  618.     s_sprintfd(msg)("%s suggests mode \"%s\" on map %s (set map to vote)", clients[sender]->name, modestr(reqmode), map);
  619.     sendservmsg(msg);
  620.     if(yes/(float)(yes+no) <= 0.5f && clients[sender]->role == CR_DEFAULT) return false;
  621.     sendservmsg("vote passed");
  622.     resetvotes();
  623.     return true;
  624. }
  625.  
  626. struct ban
  627. {
  628.     ENetAddress address;
  629.     int secs;
  630. };
  631.  
  632. vector<ban> bans;
  633.  
  634. bool isbanned(int cn)
  635. {
  636.     if(!valid_client(cn)) return false;
  637.     client &c = *clients[cn];
  638.     loopv(bans)
  639.     {
  640.         ban &b = bans[i];
  641.         if(b.secs < lastsec) { bans.remove(i--); }
  642.         if(b.address.host == c.peer->address.host) { return true; }
  643.     }
  644.     return false;
  645. }
  646.  
  647. int serveroperator()
  648. {
  649.     loopv(clients) if(clients[i]->type!=ST_EMPTY && clients[i]->role > CR_DEFAULT) return i;
  650.     return -1;
  651. }
  652.  
  653. void sendserveropinfo(int receiver)
  654. {
  655.     int op = serveroperator();
  656.     sendf(receiver, 1, "riii", SV_SERVOPINFO, op, op >= 0 ? clients[op]->role : -1);
  657. }
  658.  
  659. void changeclientrole(int client, int role, char *pwd = NULL, bool force=false)
  660. {
  661.     if(!isdedicated || !valid_client(client)) return;
  662.     int serverop = serveroperator();
  663.     if(force || role == CR_DEFAULT || (role == CR_MASTER && serverop < 0) || (role == CR_ADMIN && pwd && pwd[0] && adminpasswd && !strcmp(adminpasswd, pwd)))
  664.     {
  665.         if(role == clients[client]->role) return;
  666.         if(role > CR_DEFAULT) loopv(clients) clients[i]->role = CR_DEFAULT;
  667.         clients[client]->role = role;
  668.         sendserveropinfo(-1);
  669.     }
  670.     else if(pwd && pwd[0]) disconnect_client(client, DISC_SOPLOGINFAIL); // avoid brute-force
  671. }
  672.  
  673. void serveropcmddenied(int receiver, int requiredrole)
  674. {
  675.     sendf(receiver, 1, "rii", SV_SERVOPCMDDENIED, requiredrole);
  676. }
  677.  
  678. void serveropcmd(int sender, int cmd, int a)
  679. {
  680.     if(!isdedicated || !valid_client(sender) || cmd < 0 || cmd >= SOPCMD_NUM) return;
  681.     #define requirerole(r) if(clients[sender]->role < r) { sendf(sender, 1, "rii", SV_SERVOPCMDDENIED, r); return; }
  682.  
  683.     switch(cmd)
  684.     {
  685.         case SOPCMD_KICK:
  686.         {
  687.             requirerole(CR_MASTER);
  688.             if(!valid_client(a)) return;
  689.             disconnect_client(a, DISC_MKICK);
  690.             break;
  691.         }
  692.         case SOPCMD_MASTERMODE:
  693.         {
  694.             requirerole(CR_MASTER);
  695.             if(a < 0 || a >= MM_NUM) return;
  696.             mastermode = a;
  697.             break;
  698.         }
  699.         case SOPCMD_AUTOTEAM:
  700.         {
  701.             requirerole(CR_MASTER);
  702.             if(a < 0 || a > 1) return;
  703.             if((autoteam = a == 1) == true && m_teammode) shuffleteams();
  704.             break;
  705.         }
  706.         case SOPCMD_BAN:
  707.         {
  708.             requirerole(CR_MASTER);
  709.             if(!valid_client(a)) return;
  710.             ban b = { clients[a]->peer->address, lastsec+20*60 };
  711.             bans.add(b);
  712.             disconnect_client(a, DISC_MBAN);
  713.             break;
  714.         }
  715.         case SOPCMD_REMBANS:
  716.         {
  717.             requirerole(CR_MASTER);
  718.             if(bans.length()) bans.setsize(0);
  719.             break;
  720.         }
  721.         case SOPCMD_FORCETEAM:
  722.         {
  723.             requirerole(CR_MASTER);
  724.             if(!valid_client(a)) return;
  725.             forceteam(a, team_opposite(team_int(clients[a]->team)));
  726.             break;
  727.         }
  728.         case SOPCMD_GIVEMASTER:
  729.         {
  730.             requirerole(CR_ADMIN);
  731.             if(!valid_client(a)) return;
  732.             changeclientrole(a, CR_MASTER, NULL, true);
  733.             break;
  734.         }
  735.     }
  736.     sendf(-1, 1, "riii", SV_SERVOPCMD, cmd, a);
  737. }
  738.  
  739. int numnonlocalclients() 
  740. {
  741.     int nonlocalclients = 0;
  742.     loopv(clients) if(clients[i]->type==ST_TCPIP) nonlocalclients++;
  743.     return nonlocalclients;
  744. }
  745.  
  746. // sending of maps between clients
  747.  
  748. string copyname; 
  749. int copysize;
  750. uchar *copydata = NULL;
  751.  
  752. void sendmapserv(int n, string mapname, int mapsize, uchar *mapdata)
  753. {   
  754.     if(!mapname[0] || mapsize <= 0 || mapsize > 256*256) return;
  755.     s_strcpy(copyname, mapname);
  756.     copysize = mapsize;
  757.     DELETEA(copydata);
  758.     copydata = new uchar[mapsize];
  759.     memcpy(copydata, mapdata, mapsize);
  760. }
  761.  
  762. ENetPacket *getmapserv(int n)
  763. {
  764.     if(!copydata) return NULL;
  765.     ENetPacket *packet = enet_packet_create(NULL, MAXTRANS + copysize, ENET_PACKET_FLAG_RELIABLE);
  766.     ucharbuf p(packet->data, packet->dataLength);
  767.     putint(p, SV_RECVMAP);
  768.     sendstring(copyname, p);
  769.     putint(p, copysize);
  770.     p.put(copydata, copysize);
  771.     enet_packet_resize(packet, p.length());
  772.     return packet;
  773. }
  774.  
  775. int checktype(int type, client *cl)
  776. {
  777.     if(cl && cl->type==ST_LOCAL) return type;
  778.     // only allow edit messages in coop-edit mode
  779.     static int edittypes[] = { SV_EDITENT, SV_EDITH, SV_EDITT, SV_EDITS, SV_EDITD, SV_EDITE };
  780.     if(cl && smode!=1) loopi(sizeof(edittypes)/sizeof(int)) if(type == edittypes[i]) return -1;
  781.     // server only messages
  782.     static int servtypes[] = { SV_INITS2C, SV_MAPRELOAD, SV_SERVMSG, SV_ITEMACC, SV_ITEMSPAWN, SV_TIMEUP, SV_CDIS, SV_PONG, SV_RESUME, SV_FLAGINFO, SV_ARENASPAWN, SV_ARENAWIN, SV_CLIENT };
  783.     if(cl) loopi(sizeof(servtypes)/sizeof(int)) if(type == servtypes[i]) return -1;
  784.     return type;
  785. }
  786.  
  787. // server side processing of updates: does very little and most state is tracked client only
  788. // could be extended to move more gameplay to server (at expense of lag)
  789.  
  790. void process(ENetPacket *packet, int sender, int chan)   // sender may be -1
  791. {
  792.     ucharbuf p(packet->data, packet->dataLength);
  793.     char text[MAXTRANS];
  794.     client *cl = sender>=0 ? clients[sender] : NULL;
  795.     int type;
  796.  
  797.     if(cl && !cl->isauthed)
  798.     {
  799.         int nonlocalclients = numnonlocalclients();
  800.         int primaryreason = serverpassword[0] ? DISC_WRONGPW : (mastermode!=MM_OPEN ? DISC_MASTERMODE : (nonlocalclients>maxclients ? DISC_MAXCLIENTS : (isbanned(sender) ? DISC_MBAN : DISC_NONE)));
  801.  
  802.         if(primaryreason == DISC_NONE) cl->isauthed = true;
  803.         else if(chan==0) return;
  804.         else if(chan!=1 || getint(p)!=SV_PWD) disconnect_client(sender, primaryreason);
  805.         else
  806.         {
  807.             getstring(text, p);
  808.             if(adminpasswd[0] && !strcmp(text, adminpasswd)) // pass admins always through
  809.             { 
  810.                 cl->isauthed = true;
  811.                 changeclientrole(sender, CR_ADMIN, NULL, true);
  812.                 loopv(bans) if(bans[i].address.host == cl->peer->address.host) { bans.remove(i); break; } // remove admin bans
  813.                 if(nonlocalclients>maxclients) for(int i = 0; i < nonlocalclients; i++) if(i != sender && clients[i]->type==ST_TCPIP) 
  814.                 {
  815.                     disconnect_client(i, DISC_MAXCLIENTS); // disconnect someone else to fit maxclients again
  816.                     break;
  817.                 }
  818.             }
  819.             else if(serverpassword[0])
  820.             {
  821.                 if(!strcmp(text, serverpassword)) cl->isauthed = true;
  822.                 else disconnect_client(sender, DISC_WRONGPW);
  823.             }
  824.             else if(mastermode==MM_PRIVATE) disconnect_client(sender, DISC_MASTERMODE);
  825.             else if(nonlocalclients>maxclients) disconnect_client(sender, DISC_MAXCLIENTS);
  826.             else disconnect_client(sender, DISC_MBAN);
  827.         }
  828.         if(!cl->isauthed) return;
  829.     }
  830.  
  831.     if(packet->flags&ENET_PACKET_FLAG_RELIABLE) reliablemessages = true;
  832.  
  833.     #define QUEUE_MSG { if(cl->type==ST_TCPIP) while(curmsg<p.length()) cl->messages.add(p.buf[curmsg++]); }
  834.     #define QUEUE_INT(n) { if(cl->type==ST_TCPIP) { curmsg = p.length(); ucharbuf buf = cl->messages.reserve(5); putint(buf, n); cl->messages.addbuf(buf); } }
  835.     #define QUEUE_STR(text) { if(cl->type==ST_TCPIP) { curmsg = p.length(); ucharbuf buf = cl->messages.reserve(2*strlen(text)+1); sendstring(text, buf); cl->messages.addbuf(buf); } }
  836.  
  837.     int curmsg;
  838.     while((curmsg = p.length()) < p.maxlen) switch(type = checktype(getint(p), cl))
  839.     {
  840.         case SV_PWD:
  841.             getstring(text, p);
  842.             break;
  843.  
  844.         case SV_TEAMTEXT:
  845.             getstring(text, p);
  846.             filtertext(text, text);
  847.             sendteamtext(text, sender);
  848.             break;
  849.  
  850.         case SV_TEXT:
  851.             QUEUE_MSG;
  852.             getstring(text, p);
  853.             filtertext(text, text);
  854.             QUEUE_STR(text);
  855.             break;
  856.  
  857.         case SV_INITC2S:
  858.         {
  859.             QUEUE_MSG;
  860.             bool newclient = false;
  861.             if(!cl->name[0]) newclient = true;
  862.             getstring(text, p);
  863.             filtertext(text, text, false, MAXNAMELEN);
  864.             if(!text[0]) s_strcpy(text, "unarmed");
  865.             QUEUE_STR(text);
  866.             s_strncpy(cl->name, text, MAXNAMELEN+1);
  867.             if(newclient && cl->type==ST_TCPIP)
  868.             {
  869.                 clientscore *sc = findscore(*cl, false);
  870.                 if(sc)
  871.                 {
  872.                     cl->score = *sc; 
  873.                     sendf(-1, 1, "ri5", SV_RESUME, sender, sc->frags, sc->flags, sc->lifesequence);
  874.                 }
  875.             }
  876.             getstring(text, p);
  877.             filtertext(cl->team, text, false, MAXTEAMLEN);
  878.             QUEUE_STR(text);
  879.             getint(p);
  880.             getint(p);
  881.             QUEUE_MSG;
  882.             break;
  883.         }
  884.  
  885.         case SV_MAPCHANGE:
  886.         {
  887.             getstring(text, p);
  888.             filtertext(text, text);
  889.             int reqmode = getint(p);
  890.             if(cl->type==ST_TCPIP && !m_mp(reqmode)) reqmode = 0;
  891.             if(smapname[0] && !mapreload && !vote(text, reqmode, sender)) return;
  892.             resetmap(text, reqmode);
  893.             break;
  894.         }
  895.         
  896.         case SV_ITEMLIST:
  897.         {
  898.             int n;
  899.             while((n = getint(p))!=-1) if(notgotitems)
  900.             {
  901.                 server_entity se = { false, 0 };
  902.                 while(sents.length()<=n) sents.add(se);
  903.                 sents[n].spawned = true;
  904.             }
  905.             notgotitems = false;
  906.             QUEUE_MSG;
  907.             break;
  908.         }
  909.  
  910.         case SV_ITEMPICKUP:
  911.         {
  912.             int n = getint(p);
  913.             serverpickup(n, getint(p), sender);
  914.             QUEUE_MSG;
  915.             break;
  916.         }
  917.         
  918.         case SV_PING:
  919.             sendf(sender, 1, "ii", SV_PONG, getint(p));
  920.             break;
  921.  
  922.         case SV_POS:
  923.         {
  924.             int cn = getint(p);
  925.             if(cn!=sender)
  926.             {
  927.                 disconnect_client(sender, DISC_CN);
  928. #ifndef STANDALONE
  929.                 conoutf("ERROR: invalid client (msg %i)", type);
  930. #endif
  931.                 return;
  932.             }
  933.             loopi(3) clients[cn]->pos[i] = getuint(p);
  934.             getuint(p);
  935.             loopi(6) getint(p);
  936.             if(cl->type==ST_TCPIP)
  937.             {
  938.                 cl->position.setsizenodelete(0);
  939.                 while(curmsg<p.length()) cl->position.add(p.buf[curmsg++]);
  940.             }
  941.             break;
  942.         }
  943.  
  944.         case SV_SENDMAP:
  945.         {
  946.             getstring(text, p);
  947.             filtertext(text, text);
  948.             int mapsize = getint(p);
  949.             if(p.remaining() < mapsize)
  950.             {
  951.                 p.forceoverread();
  952.                 break;
  953.             }
  954.             sendmapserv(sender, text, mapsize, &p.buf[p.len]);
  955.             p.len += mapsize;
  956.             break;
  957.         }
  958.  
  959.         case SV_RECVMAP:
  960.         {
  961.             ENetPacket *mappacket = getmapserv(sender);
  962.             if(mappacket) sendpacket(sender, 2, mappacket);
  963.             else sendservmsg("no map to get", sender);
  964.             break;
  965.         }
  966.             
  967.         case SV_FLAGPICKUP:
  968.         case SV_FLAGDROP:
  969.         case SV_FLAGRETURN:
  970.         case SV_FLAGSCORE:
  971.         case SV_FLAGRESET:
  972.         {
  973.             flagaction(getint(p), type, sender);
  974.             break;
  975.         }
  976.  
  977.         case SV_SETMASTER:
  978.         {
  979.             changeclientrole(sender, getint(p) != 0 ? CR_MASTER : CR_DEFAULT, NULL);
  980.             break;
  981.         }
  982.  
  983.         case SV_SETADMIN:
  984.         {
  985.             bool claim = getint(p) != 0;
  986.             getstring(text, p);
  987.             changeclientrole(sender, claim ? CR_ADMIN : CR_DEFAULT, text);
  988.             break;
  989.         }
  990.  
  991.         case SV_SERVOPCMD:
  992.         {
  993.             int cmd = getint(p);
  994.             int arg = getint(p);
  995.             serveropcmd(sender, cmd, arg);
  996.             break;
  997.         }
  998.  
  999.         case SV_FRAGS:
  1000.             cl->score.frags = getint(p);
  1001.             if(cl->score.frags < scorethreshold) disconnect_client(sender, DISC_AUTOKICK);
  1002.             QUEUE_MSG;
  1003.             break;
  1004.  
  1005.         case SV_FLAGS:
  1006.             cl->score.flags = getint(p);
  1007.             QUEUE_MSG;
  1008.             break;
  1009.  
  1010.         case SV_GIBDIED:
  1011.         case SV_DIED:
  1012.             getint(p);
  1013.             if(m_arena) cl->arenadeath = true;
  1014.             cl->score.lifesequence++;
  1015.             QUEUE_MSG;
  1016.             break;
  1017.         default:
  1018.         {
  1019.             int size = msgsizelookup(type);
  1020.             if(size==-1) { if(sender>=0) disconnect_client(sender, DISC_TAGT); return; }
  1021.             loopi(size-1) getint(p);
  1022.             QUEUE_MSG;
  1023.             break;
  1024.         }
  1025.     }
  1026.  
  1027.     if(p.overread() && sender>=0) disconnect_client(sender, DISC_EOP);
  1028. }
  1029.  
  1030. void send_welcome(int n)
  1031. {
  1032.     ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
  1033.     ucharbuf p(packet->data, packet->dataLength);
  1034.     putint(p, SV_INITS2C);
  1035.     putint(p, n);
  1036.     putint(p, PROTOCOL_VERSION);
  1037.     if(!smapname[0] && configsets.length()) nextcfgset(false);
  1038.     int numcl = numclients();
  1039.     putint(p, smapname[0] ? numcl : -1);
  1040.     putint(p, serverpassword[0] ? 1 : 0);
  1041.     if(smapname[0])
  1042.     {
  1043.         putint(p, SV_MAPCHANGE);
  1044.         sendstring(smapname, p);
  1045.         putint(p, smode);
  1046.         if(!configsets.length() || numcl > 1)
  1047.         {
  1048.             putint(p, SV_ITEMLIST);
  1049.             loopv(sents) if(sents[i].spawned) putint(p, i);
  1050.             putint(p, -1);
  1051.         }
  1052.     }
  1053.     if(clients[n]->type == ST_TCPIP && serveroperator() != -1) sendserveropinfo(n);
  1054.     loopv(clients)
  1055.     {
  1056.         client &c = *clients[i];
  1057.         if(c.type!=ST_TCPIP || c.clientnum==n) continue;
  1058.         if(!c.score.frags && !c.score.flags) continue;
  1059.         putint(p, SV_RESUME);
  1060.         putint(p, c.clientnum);
  1061.         putint(p, c.score.frags);
  1062.         putint(p, c.score.flags);
  1063.         putint(p, c.score.lifesequence);
  1064.     }
  1065.     if(autoteam)
  1066.     {
  1067.         putint(p, SV_FORCETEAM);
  1068.         putint(p, freeteam());
  1069.     }
  1070.     putint(p, SV_SERVOPCMD);
  1071.     putint(p, SOPCMD_AUTOTEAM);
  1072.     putint(p, autoteam);
  1073.     if(motd)
  1074.     {
  1075.         putint(p, SV_TEXT);
  1076.         sendstring(motd, p);
  1077.     }
  1078.     enet_packet_resize(packet, p.length());
  1079.     sendpacket(n, 1, packet);
  1080.     if(smapname[0] && m_ctf) loopi(2) sendflaginfo(i, -1, n);
  1081.     if(m_arena && numcl<=2) clients[n]->arenadeath = false;
  1082. }
  1083.  
  1084. void localclienttoserver(int chan, ENetPacket *packet)
  1085. {
  1086.     process(packet, 0, chan);
  1087.     if(!packet->referenceCount) enet_packet_destroy(packet);
  1088. }
  1089.  
  1090. client &addclient()
  1091. {
  1092.     client *c = NULL; 
  1093.     loopv(clients) if(clients[i]->type==ST_EMPTY) { c = clients[i]; break; }
  1094.     if(!c) 
  1095.     { 
  1096.         c = new client; 
  1097.         c->clientnum = clients.length(); 
  1098.         clients.add(c);
  1099.     }
  1100.     c->reset();
  1101.     return *c;
  1102. }
  1103.  
  1104. void checkintermission()
  1105. {
  1106.     if(!minremain)
  1107.     {
  1108.         interm = lastsec+10;
  1109.         mapend = lastsec+1000;
  1110.     }
  1111.     sendf(-1, 1, "rii", SV_TIMEUP, minremain--);
  1112. }
  1113.  
  1114. /*void startintermission() { minremain = 0; checkintermission(); }*/
  1115.  
  1116. void resetserverifempty()
  1117. {
  1118.     loopv(clients) if(clients[i]->type!=ST_EMPTY) return;
  1119.     //clients.setsize(0);
  1120.     resetmap("", 0, 10, false);
  1121.     mastermode = MM_OPEN;
  1122.     autoteam = true;
  1123. }
  1124.  
  1125. void sendworldstate()
  1126. {
  1127.     static enet_uint32 lastsend = 0;
  1128.     if(clients.empty()) return;
  1129.     enet_uint32 curtime = enet_time_get()-lastsend;
  1130.     if(curtime<40) return;
  1131.     bool flush = buildworldstate();
  1132.     lastsend += curtime - (curtime%40);
  1133.     if(flush) enet_host_flush(serverhost);
  1134. }
  1135.  
  1136. void serverslice(int seconds, unsigned int timeout)   // main server update, called from cube main loop in sp, or dedicated server loop
  1137. {
  1138.     loopv(sents)        // spawn entities when timer reached
  1139.     {
  1140.         if(sents[i].spawnsecs && (sents[i].spawnsecs -= seconds-lastsec)<=0)
  1141.         {
  1142.             sents[i].spawnsecs = 0;
  1143.             sents[i].spawned = true;
  1144.             sendf(-1, 1, "rii", SV_ITEMSPAWN, i);
  1145.         }
  1146.     }
  1147.     
  1148.     if(m_ctf) loopi(2)
  1149.     {
  1150.         sflaginfo &f = sflaginfos[i];
  1151.         if(f.state==CTFF_DROPPED && seconds-f.lastupdate>30) flagaction(i, SV_FLAGRESET, -1);
  1152.     }
  1153.     if(m_arena) arenacheck(seconds);
  1154.  
  1155.     lastsec = seconds;
  1156.    
  1157.     int nonlocalclients = numnonlocalclients();
  1158.  
  1159.     if((smode>1 || (smode==0 && nonlocalclients)) && seconds>mapend-minremain*60) checkintermission();
  1160.     if(interm && seconds>interm)
  1161.     {
  1162.         interm = 0;
  1163.  
  1164.         if(configsets.length()) nextcfgset();
  1165.         else loopv(clients) if(clients[i]->type!=ST_EMPTY)
  1166.         {
  1167.             sendf(i, 1, "rii", SV_MAPRELOAD, 0);    // ask a client to trigger map reload
  1168.             mapreload = true;
  1169.             break;
  1170.         }
  1171.     }
  1172.  
  1173.     resetserverifempty();
  1174.     
  1175.     if(!isdedicated) return;     // below is network only
  1176.  
  1177.     serverms(smode, numclients(), minremain, smapname, seconds);
  1178.  
  1179.     if(seconds-laststatus>60)   // display bandwidth stats, useful for server ops
  1180.     {
  1181.         laststatus = seconds;     
  1182.         if(nonlocalclients || bsend || brec) 
  1183.         { 
  1184.             printf("status: %d remote clients, %.1f send, %.1f rec (K/sec)\n", nonlocalclients, bsend/60.0f/1024, brec/60.0f/1024); 
  1185. #ifdef _DEBUG
  1186.             fflush(stdout);
  1187. #endif
  1188.         }
  1189.         bsend = brec = 0;
  1190.     }
  1191.  
  1192.     ENetEvent event;
  1193.     if(enet_host_service(serverhost, &event, timeout) > 0)
  1194.     {
  1195.         switch(event.type)
  1196.         {
  1197.             case ENET_EVENT_TYPE_CONNECT:
  1198.             {
  1199.                 client &c = addclient();
  1200.                 c.type = ST_TCPIP;
  1201.                 c.peer = event.peer;
  1202.                 c.peer->data = (void *)(size_t)c.clientnum;
  1203.                 char hn[1024];
  1204.                 s_strcpy(c.hostname, (enet_address_get_host_ip(&c.peer->address, hn, sizeof(hn))==0) ? hn : "unknown");
  1205.                 printf("client connected (%s)\n", c.hostname);
  1206.                 send_welcome(c.clientnum);
  1207.                 break;
  1208.             }
  1209.  
  1210.             case ENET_EVENT_TYPE_RECEIVE:
  1211.             {
  1212.                 brec += (int)event.packet->dataLength;
  1213.                 int cn = (int)(size_t)event.peer->data;
  1214.                 if(valid_client(cn)) process(event.packet, cn, event.channelID); 
  1215.                 if(event.packet->referenceCount==0) enet_packet_destroy(event.packet);
  1216.                 break;
  1217.             }
  1218.  
  1219.             case ENET_EVENT_TYPE_DISCONNECT: 
  1220.             {
  1221.                 int cn = (int)(size_t)event.peer->data;
  1222.                 if(!valid_client(cn)) break;
  1223.                 disconnect_client(cn);
  1224.                 break;
  1225.             }
  1226.  
  1227.             default:
  1228.                 break;
  1229.         }
  1230.     }
  1231.     sendworldstate();
  1232. }
  1233.  
  1234. void cleanupserver()
  1235. {
  1236.     if(serverhost) enet_host_destroy(serverhost);
  1237. }
  1238.  
  1239. void localdisconnect()
  1240. {
  1241.     loopv(clients) if(clients[i]->type==ST_LOCAL) zapclient(i);
  1242. }
  1243.  
  1244. void localconnect()
  1245. {
  1246.     client &c = addclient();
  1247.     c.type = ST_LOCAL;
  1248.     s_strcpy(c.hostname, "local");
  1249.     send_welcome(c.clientnum);
  1250. }
  1251.  
  1252. void initserver(bool dedicated, int uprate, char *sdesc, char *ip, char *master, char *passwd, int maxcl, char *maprot, char *adminpwd, char *srvmsg, int scthreshold)
  1253. {
  1254.     if(passwd) s_strcpy(serverpassword, passwd);
  1255.     maxclients = maxcl > 0 ? min(maxcl, MAXCLIENTS) : DEFAULTCLIENTS;
  1256.     servermsinit(master ? master : "masterserver.cubers.net/cgi-bin/actioncube.pl/", ip, sdesc, dedicated);
  1257.     
  1258.     if(isdedicated = dedicated)
  1259.     {
  1260.         ENetAddress address = { ENET_HOST_ANY, CUBE_SERVER_PORT };
  1261.         if(*ip && enet_address_set_host(&address, ip)<0) printf("WARNING: server ip not resolved");
  1262.         serverhost = enet_host_create(&address, maxclients+1, 0, uprate);
  1263.         if(!serverhost) fatal("could not create server host\n");
  1264.         loopi(maxclients) serverhost->peers[i].data = (void *)-1;
  1265.         if(!maprot || !maprot[0]) maprot = newstring("config/maprot.cfg");
  1266.         readscfg(path(maprot));
  1267.         if(adminpwd && adminpwd[0]) adminpasswd = adminpwd;
  1268.         if(srvmsg && srvmsg[0]) motd = srvmsg;
  1269.         scorethreshold = min(-1, scthreshold);
  1270.     }
  1271.  
  1272.     resetserverifempty();
  1273.  
  1274.     if(isdedicated)       // do not return, this becomes main loop
  1275.     {
  1276.         #ifdef WIN32
  1277.         SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
  1278.         #endif
  1279.         printf("dedicated server started, waiting for clients...\nCtrl-C to exit\n\n");
  1280.         atexit(enet_deinitialize);
  1281.         atexit(cleanupserver);
  1282.         for(;;) serverslice(/*enet_time_get_sec()*/time(NULL), 5);
  1283.     }
  1284. }
  1285.  
  1286. #ifdef STANDALONE
  1287.  
  1288. void localservertoclient(int chan, uchar *buf, int len) {}
  1289. void fatal(char *s, char *o) { cleanupserver(); printf("fatal: %s\n", s); exit(EXIT_FAILURE); }
  1290.  
  1291. int main(int argc, char **argv)
  1292. {   
  1293.     int uprate = 0, maxcl = DEFAULTCLIENTS, scthreshold = -5;
  1294.     char *sdesc = "", *ip = "", *master = NULL, *passwd = "", *maprot = "", *adminpasswd = NULL, *srvmsg = NULL;
  1295.  
  1296.     for(int i = 1; i<argc; i++)
  1297.     {
  1298.         char *a = &argv[i][2];
  1299.         if(argv[i][0]=='-') switch(argv[i][1])
  1300.         {
  1301.             case 'u': uprate = atoi(a); break;
  1302.             case 'n': sdesc  = a; break;
  1303.             case 'i': ip     = a; break;
  1304.             case 'm': master = a; break;
  1305.             case 'p': passwd = a; break;
  1306.             case 'c': maxcl  = atoi(a); break;
  1307.             case 'r': maprot = a; break; 
  1308.             case 'x' : adminpasswd = a; break;
  1309.             case 'o' : srvmsg = a; break;
  1310.             case 'k': scthreshold = atoi(a); break;
  1311.             default: printf("WARNING: unknown commandline option\n");
  1312.         }
  1313.     }
  1314.  
  1315.     if(enet_initialize()<0) fatal("Unable to initialise network module");
  1316.     initserver(true, uprate, sdesc, ip, master, passwd, maxcl, maprot, adminpasswd, srvmsg, scthreshold);
  1317.     return EXIT_SUCCESS;
  1318. }
  1319. #endif
  1320.  
  1321.