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

  1. // serverbrowser.cpp: eihrul's concurrent resolver, and server browser window management
  2.  
  3. #include "cube.h"
  4. #ifdef __APPLE__
  5. #include <pthread.h>
  6. #endif
  7. #include "SDL_thread.h"
  8.  
  9. extern bool isdedicated;
  10.  
  11. struct resolverthread 
  12. {
  13.     SDL_Thread *thread;
  14.     const char *query;
  15.     int starttime;
  16. };  
  17.     
  18. struct resolverresult
  19. {
  20.     const char *query;
  21.     ENetAddress address;
  22. }; 
  23.     
  24. vector<resolverthread> resolverthreads;
  25. vector<const char *> resolverqueries;
  26. vector<resolverresult> resolverresults;
  27. SDL_mutex *resolvermutex;
  28. SDL_cond *querycond, *resultcond;
  29.  
  30. #define RESOLVERTHREADS 1
  31. #define RESOLVERLIMIT 3000
  32.  
  33. int resolverloop(void * data)
  34. {   
  35.     resolverthread *rt = (resolverthread *)data;
  36.     SDL_LockMutex(resolvermutex);
  37.     SDL_Thread *thread = rt->thread;
  38.     SDL_UnlockMutex(resolvermutex);
  39.     if(!thread || SDL_GetThreadID(thread) != SDL_ThreadID())
  40.         return 0;
  41.     while(thread == rt->thread)
  42.     {
  43.         SDL_LockMutex(resolvermutex);
  44.         while(resolverqueries.empty()) SDL_CondWait(querycond, resolvermutex);
  45.         rt->query = resolverqueries.pop();
  46.         rt->starttime = lastmillis;
  47.         SDL_UnlockMutex(resolvermutex);
  48.     
  49.         ENetAddress address = { ENET_HOST_ANY, CUBE_SERVINFO_PORT };
  50.         enet_address_set_host(&address, rt->query);
  51.  
  52.         SDL_LockMutex(resolvermutex);
  53.         if(thread == rt->thread)
  54.         {
  55.             resolverresult &rr = resolverresults.add();
  56.             rr.query = rt->query;
  57.             rr.address = address;
  58.             rt->query = NULL;
  59.             rt->starttime = 0;
  60.             SDL_CondSignal(resultcond);
  61.         }
  62.         SDL_UnlockMutex(resolvermutex);
  63.     }
  64.     return 0;
  65. }
  66.  
  67. void resolverinit()
  68. {
  69.     resolvermutex = SDL_CreateMutex();
  70.     querycond = SDL_CreateCond();
  71.     resultcond = SDL_CreateCond();
  72.  
  73.     SDL_LockMutex(resolvermutex);
  74.     loopi(RESOLVERTHREADS)
  75.     {
  76.         resolverthread &rt = resolverthreads.add();
  77.         rt.query = NULL;
  78.         rt.starttime = 0;
  79.         rt.thread = SDL_CreateThread(resolverloop, &rt);
  80.     }
  81.     SDL_UnlockMutex(resolvermutex);
  82. }
  83.  
  84. void resolverstop(resolverthread &rt)
  85. {
  86.     SDL_LockMutex(resolvermutex);
  87.     if(rt.query)
  88.     {
  89. #ifndef __APPLE__
  90.         SDL_KillThread(rt.thread);
  91. #endif
  92.         rt.thread = SDL_CreateThread(resolverloop, &rt);
  93.     }
  94.     rt.query = NULL;
  95.     rt.starttime = 0;
  96.     SDL_UnlockMutex(resolvermutex);
  97. }
  98.  
  99. void resolverclear()
  100. {
  101.     if(resolverthreads.empty()) return;
  102.  
  103.     SDL_LockMutex(resolvermutex);
  104.     resolverqueries.setsize(0);
  105.     resolverresults.setsize(0);
  106.     loopv(resolverthreads)
  107.     {
  108.         resolverthread &rt = resolverthreads[i];
  109.         resolverstop(rt);
  110.     }
  111.     SDL_UnlockMutex(resolvermutex);
  112. }
  113.  
  114. void resolverquery(const char *name)
  115. {
  116.     if(resolverthreads.empty()) resolverinit();
  117.  
  118.     SDL_LockMutex(resolvermutex);
  119.     resolverqueries.add(name);
  120.     SDL_CondSignal(querycond);
  121.     SDL_UnlockMutex(resolvermutex);
  122. }
  123.  
  124. bool resolvercheck(const char **name, ENetAddress *address)
  125. {
  126.     bool resolved = false;
  127.     SDL_LockMutex(resolvermutex);
  128.     if(!resolverresults.empty())
  129.     {
  130.         resolverresult &rr = resolverresults.pop();
  131.         *name = rr.query;
  132.         address->host = rr.address.host;
  133.         resolved = true;
  134.     }
  135.     else loopv(resolverthreads)
  136.     {
  137.         resolverthread &rt = resolverthreads[i];
  138.         if(rt.query && lastmillis - rt.starttime > RESOLVERLIMIT)
  139.         {
  140.             resolverstop(rt);
  141.             *name = rt.query;
  142.             resolved = true;
  143.         }
  144.     }
  145.     SDL_UnlockMutex(resolvermutex);
  146.     return resolved;
  147. }
  148.  
  149. extern bool isdedicated;
  150.  
  151. bool resolverwait(const char *name, ENetAddress *address)
  152. {
  153.     if(isdedicated) return enet_address_set_host(address, name) >= 0;
  154.  
  155.     if(resolverthreads.empty()) resolverinit();
  156.  
  157.     s_sprintfd(text)("resolving %s... (esc to abort)", name);
  158.     show_out_of_renderloop_progress(0, text);
  159.  
  160.     SDL_LockMutex(resolvermutex);
  161.     resolverqueries.add(name);
  162.     SDL_CondSignal(querycond);
  163.     int starttime = SDL_GetTicks(), timeout = 0;
  164.     bool resolved = false;
  165.     for(;;)
  166.     {
  167.         SDL_CondWaitTimeout(resultcond, resolvermutex, 250);
  168.         loopv(resolverresults) if(resolverresults[i].query == name)
  169.         {
  170.             address->host = resolverresults[i].address.host;
  171.             resolverresults.remove(i);
  172.             resolved = true;
  173.             break;
  174.         }
  175.         if(resolved) break;
  176.  
  177.         timeout = SDL_GetTicks() - starttime;
  178.         show_out_of_renderloop_progress(min(float(timeout)/RESOLVERLIMIT, 1), text);
  179.         SDL_Event event;
  180.         while(SDL_PollEvent(&event))
  181.         {
  182.             if(event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) timeout = RESOLVERLIMIT + 1;
  183.         }
  184.  
  185.         if(timeout > RESOLVERLIMIT) break;
  186.     }
  187.     if(!resolved && timeout > RESOLVERLIMIT)
  188.     {
  189.         loopv(resolverthreads)
  190.         {
  191.             resolverthread &rt = resolverthreads[i];
  192.             if(rt.query == name) { resolverstop(rt); break; }
  193.         }
  194.     }
  195.     SDL_UnlockMutex(resolvermutex);
  196.     return resolved;
  197. }
  198.  
  199. SDL_Thread *connthread = NULL;
  200. SDL_mutex *connmutex = NULL;
  201. SDL_cond *conncond = NULL;
  202.  
  203. struct connectdata
  204. {
  205.     ENetSocket sock;
  206.     ENetAddress address;
  207.     int result;
  208. };
  209.  
  210. // do this in a thread to prevent timeouts
  211. // could set timeouts on sockets, but this is more reliable and gives more control
  212. int connectthread(void *data)
  213. {
  214.     SDL_LockMutex(connmutex);
  215.     if(!connthread || SDL_GetThreadID(connthread) != SDL_ThreadID())
  216.     {
  217.         SDL_UnlockMutex(connmutex);
  218.         return 0;
  219.     }
  220.     connectdata cd = *(connectdata *)data;
  221.     SDL_UnlockMutex(connmutex);
  222.  
  223.     int result = enet_socket_connect(cd.sock, &cd.address);
  224.  
  225.     SDL_LockMutex(connmutex);
  226.     if(!connthread || SDL_GetThreadID(connthread) != SDL_ThreadID())
  227.     {
  228.         enet_socket_destroy(cd.sock);
  229.         SDL_UnlockMutex(connmutex);
  230.         return 0;
  231.     }
  232.     ((connectdata *)data)->result = result;
  233.     SDL_CondSignal(conncond);
  234.     SDL_UnlockMutex(connmutex);
  235.  
  236.     return 0;
  237. }
  238.  
  239. #define CONNLIMIT 20000
  240.  
  241. int connectwithtimeout(ENetSocket sock, char *hostname, ENetAddress &address)
  242. {   
  243.     if(isdedicated)
  244.     {
  245.         int result = enet_socket_connect(sock, &address);
  246.         if(result<0) enet_socket_destroy(sock);
  247.         return result;
  248.     }
  249.  
  250.     s_sprintfd(text)("connecting to %s... (esc to abort)", hostname);
  251.     show_out_of_renderloop_progress(0, text);
  252.  
  253.     if(!connmutex) connmutex = SDL_CreateMutex();
  254.     if(!conncond) conncond = SDL_CreateCond();
  255.     SDL_LockMutex(connmutex);
  256.     connectdata cd = { sock, address, -1 };
  257.     connthread = SDL_CreateThread(connectthread, &cd);
  258.  
  259.     int starttime = SDL_GetTicks(), timeout = 0;
  260.     for(;;)
  261.     {
  262.         if(!SDL_CondWaitTimeout(conncond, connmutex, 250))
  263.         {
  264.             if(cd.result<0) enet_socket_destroy(sock);
  265.             break;
  266.         }       
  267.         timeout = SDL_GetTicks() - starttime;
  268.         show_out_of_renderloop_progress(min(float(timeout)/CONNLIMIT, 1), text);
  269.         SDL_Event event;
  270.         while(SDL_PollEvent(&event))
  271.         {
  272.             if(event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) timeout = CONNLIMIT + 1;
  273.         }
  274.         if(timeout > CONNLIMIT) break;
  275.     }
  276.  
  277.     /* thread will actually timeout eventually if its still trying to connect
  278.      * so just leave it (and let it destroy socket) instead of causing problems on some platforms by killing it 
  279.      */
  280.     connthread = NULL;
  281.     SDL_UnlockMutex(connmutex);
  282.  
  283.     return cd.result;
  284. }
  285.  
  286. struct serverinfo
  287. {
  288.     char *name;
  289.     string full;
  290.     string map;
  291.     string sdesc;
  292.     string cmd;
  293.     int mode, numplayers, maxclients, ping, protocol, minremain, resolved;
  294.     ENetAddress address;
  295. };
  296.  
  297. enum { UNRESOLVED = 0, RESOLVING, RESOLVED };
  298.  
  299. vector<serverinfo> servers;
  300. ENetSocket pingsock = ENET_SOCKET_NULL;
  301. int lastinfo = 0;
  302.  
  303. char *getservername(int n) { return servers[n].name; }
  304.  
  305. void addserver(char *servername)
  306. {
  307.     loopv(servers) if(strcmp(servers[i].name, servername)==0) return;
  308.     serverinfo &si = servers.insert(0, serverinfo());
  309.     si.name = newstring(servername);
  310.     si.full[0] = 0;
  311.     si.mode = 0;
  312.     si.numplayers = 0;
  313.     si.maxclients = 0;
  314.     si.ping = 9999;
  315.     si.protocol = 0;
  316.     si.minremain = 0;
  317.     si.map[0] = 0;
  318.     si.sdesc[0] = 0;
  319.     si.resolved = UNRESOLVED;
  320.     si.address.host = ENET_HOST_ANY;
  321.     si.address.port = CUBE_SERVINFO_PORT;
  322. }
  323.  
  324. void pingservers()
  325. {
  326.     ENetBuffer buf;
  327.     uchar ping[MAXTRANS];
  328.     loopv(servers)
  329.     {
  330.         serverinfo &si = servers[i];
  331.         if(si.address.host == ENET_HOST_ANY) continue;
  332.         ucharbuf p(ping, sizeof(ping));
  333.         putint(p, lastmillis);
  334.         buf.data = ping;
  335.         buf.dataLength = p.length();
  336.         enet_socket_send(pingsock, &si.address, &buf, 1);
  337.     }
  338.     lastinfo = lastmillis;
  339. }
  340.   
  341. void checkresolver()
  342. {
  343.     int resolving = 0;
  344.     loopv(servers)
  345.     {
  346.         serverinfo &si = servers[i];
  347.         if(si.resolved == RESOLVED) continue;
  348.         if(si.address.host == ENET_HOST_ANY)
  349.         {
  350.             if(si.resolved == UNRESOLVED) { si.resolved = RESOLVING; resolverquery(si.name); }
  351.             resolving++;
  352.         }
  353.     }
  354.     if(!resolving) return;
  355.  
  356.     const char *name = NULL;
  357.     ENetAddress addr = { ENET_HOST_ANY, CUBE_SERVINFO_PORT };
  358.     while(resolvercheck(&name, &addr))
  359.     {
  360.         loopv(servers)
  361.         {
  362.             serverinfo &si = servers[i];
  363.             if(name == si.name)
  364.             {
  365.                 si.resolved = RESOLVED;
  366.                 si.address = addr;
  367.                 addr.host = ENET_HOST_ANY;
  368.                 break;
  369.             }
  370.         }
  371.     }
  372. }
  373.  
  374. void checkpings()
  375. {
  376.     enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
  377.     ENetBuffer buf;
  378.     ENetAddress addr;
  379.     uchar ping[MAXTRANS];
  380.     char text[MAXTRANS];
  381.     buf.data = ping; 
  382.     buf.dataLength = sizeof(ping);
  383.     while(enet_socket_wait(pingsock, &events, 0) >= 0 && events)
  384.     {
  385.         int len = enet_socket_receive(pingsock, &addr, &buf, 1);
  386.         if(len <= 0) return;  
  387.         loopv(servers)
  388.         {
  389.             serverinfo &si = servers[i];
  390.             if(addr.host == si.address.host)
  391.             {
  392.                 ucharbuf p(ping, len);
  393.                 si.ping = lastmillis - getint(p);
  394.                 si.protocol = getint(p);
  395.                 if(si.protocol!=PROTOCOL_VERSION) si.ping = 9998;
  396.                 si.mode = getint(p);
  397.                 si.numplayers = getint(p);
  398.                 si.minremain = getint(p);
  399.                 getstring(text, p);
  400.                 s_strcpy(si.map, text);
  401.                 getstring(text, p);
  402.                 s_strcpy(si.sdesc, text);                
  403.                 si.maxclients = getint(p);
  404.                 break;
  405.             }
  406.         }
  407.     }
  408. }
  409.  
  410. int sicompare(const serverinfo *a, const serverinfo *b)
  411. {
  412.     if((a->protocol==PROTOCOL_VERSION) > (b->protocol==PROTOCOL_VERSION)) return -1;
  413.     if((b->protocol==PROTOCOL_VERSION) > (a->protocol==PROTOCOL_VERSION)) return 1;
  414.     if(a->numplayers<b->numplayers) return 1;
  415.     if(a->numplayers>b->numplayers) return -1;
  416.     if(a->ping>b->ping) return 1;
  417.     if(a->ping<b->ping) return -1;
  418.     return strcmp(a->name, b->name);
  419. }
  420.  
  421. void *servmenu = NULL;
  422.  
  423. void refreshservers(void *menu, bool init)
  424. {
  425.     if(init)
  426.     {
  427.         if(pingsock == ENET_SOCKET_NULL)
  428.         {
  429.             pingsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, NULL);
  430.             resolverinit();
  431.         }
  432.         resolverclear();
  433.         loopv(servers) resolverquery(servers[i].name);
  434.     }
  435.  
  436.     checkresolver();
  437.     checkpings();
  438.     if(lastmillis - lastinfo >= 5000) pingservers();
  439.     servers.sort(sicompare);
  440.     loopv(servers)
  441.     {
  442.         serverinfo &si = servers[i];
  443.         if(si.address.host != ENET_HOST_ANY && si.ping != 9999)
  444.         {
  445.             if(si.protocol!=PROTOCOL_VERSION) s_sprintf(si.full)("%s [%s protocol]", si.name, si.protocol<PROTOCOL_VERSION ? "older" : "newer");
  446.             else s_sprintf(si.full)("%d\t%d/%d\t%s, %s: %s %s", si.ping, si.numplayers, si.maxclients, si.map[0] ? si.map : "[unknown]", modestr(si.mode), si.name, si.sdesc);
  447.         }
  448.         else
  449.         {
  450.             s_sprintf(si.full)(si.address.host != ENET_HOST_ANY ? "%s [waiting for server response]" : "%s [unknown host]\t", si.name);
  451.         }
  452.         si.full[50] = 0; // cut off too long server descriptions
  453.         s_sprintf(si.cmd)("connect %s", si.name);
  454.         menumanual(menu, i, si.full, si.cmd);
  455.     }
  456. }
  457.  
  458. void updatefrommaster()
  459. {
  460.     uchar buf[32000];
  461.     uchar *reply = retrieveservers(buf, sizeof(buf));
  462.     if(!*reply || strstr((char *)reply, "<html>") || strstr((char *)reply, "<HTML>")) conoutf("master server not replying");
  463.     else 
  464.     { 
  465.         loopv(servers) delete[] servers[i].name;
  466.         servers.setsize(0); 
  467.         execute((char *)reply); 
  468.     }
  469. }
  470.  
  471. COMMAND(addserver, ARG_1STR);
  472. COMMAND(updatefrommaster, ARG_NONE);
  473.  
  474. void writeservercfg()
  475. {
  476.     FILE *f = fopen("config/servers.cfg", "w");
  477.     if(!f) return;
  478.     fprintf(f, "// servers connected to are added here automatically\n\n");
  479.     loopvrev(servers) fprintf(f, "addserver %s\n", servers[i].name);
  480.     fclose(f);
  481. }
  482.