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

  1. // client.cpp, mostly network related client game code
  2.  
  3. #include "cube.h"
  4. #include "bot/bot.h"
  5.  
  6. VAR(connected, 1, 0, 0);
  7.  
  8. ENetHost *clienthost = NULL;
  9. ENetPeer *curpeer = NULL, *connpeer = NULL;
  10. int connmillis = 0, connattempts = 0, discmillis = 0;
  11. bool c2sinit = false;       // whether we need to tell the other clients our stats
  12.  
  13. int getclientnum() { return player1 ? player1->clientnum : -1; }
  14.  
  15. bool multiplayer(bool msg)
  16. {
  17.     // check not correct on listen server?
  18.     if(curpeer && msg) conoutf("operation not available in multiplayer");
  19.     return curpeer!=NULL;
  20. }
  21.  
  22. bool allowedittoggle()
  23. {
  24.     bool allow = !curpeer || gamemode==1;
  25.     if(!allow) conoutf("editing in multiplayer requires coopedit mode (1)");
  26.     return allow; 
  27. }
  28.  
  29. void setrate(int rate)
  30. {
  31.    if(!curpeer) return;
  32.    enet_host_bandwidth_limit(clienthost, rate, rate);
  33. }
  34.  
  35. VARF(rate, 0, 0, 25000, setrate(rate));
  36.  
  37. void throttle();
  38.  
  39. VARF(throttle_interval, 0, 5, 30, throttle());
  40. VARF(throttle_accel,    0, 2, 32, throttle());
  41. VARF(throttle_decel,    0, 2, 32, throttle());
  42.  
  43. void throttle()
  44. {
  45.     if(!curpeer) return;
  46.     ASSERT(ENET_PEER_PACKET_THROTTLE_SCALE==32);
  47.     enet_peer_throttle_configure(curpeer, throttle_interval*1000, throttle_accel, throttle_decel);
  48. }
  49.  
  50. string clientpassword = "";
  51.  
  52. void abortconnect()
  53. {
  54.     if(!connpeer) return;
  55.     if(connpeer->state!=ENET_PEER_STATE_DISCONNECTED) enet_peer_reset(connpeer);
  56.     connpeer = NULL;
  57.     if(curpeer) return;
  58.     enet_host_destroy(clienthost);
  59.     clienthost = NULL;
  60. }
  61.  
  62. void connects(char *servername, char *password)
  63. {   
  64.     if(connpeer)
  65.     {
  66.         conoutf("aborting connection attempt");
  67.         abortconnect();
  68.     }
  69.  
  70.     s_strcpy(clientpassword, password ? password : "");
  71.  
  72.     ENetAddress address;
  73.     address.port = CUBE_SERVER_PORT;
  74.  
  75.     if(servername)
  76.     {
  77.         addserver(servername);
  78.         conoutf("attempting to connect to %s", servername);
  79.         if(!resolverwait(servername, &address))
  80.         {
  81.             conoutf("\f3could not resolve server %s", servername);
  82.             return;
  83.         }
  84.     }
  85.     else
  86.     {
  87.         conoutf("attempting to connect over LAN");
  88.         address.host = ENET_HOST_BROADCAST;
  89.     }
  90.  
  91.     if(!clienthost) clienthost = enet_host_create(NULL, 2, rate, rate);
  92.  
  93.     if(clienthost)
  94.     {
  95.         connpeer = enet_host_connect(clienthost, &address, 3); 
  96.         enet_host_flush(clienthost);
  97.         connmillis = lastmillis;
  98.         connattempts = 0;
  99.     }
  100.     else conoutf("\f3could not connect to server");
  101. }
  102.  
  103. void connectadmin(char *servername, char *password)
  104. {
  105.     if(!password) return;
  106.     connects(servername, password);
  107.     if(clienthost) addmsg(SV_SETADMIN, "ris", 1, password); // in case the server is not private locked or pwd protected
  108. }
  109.  
  110. void lanconnect()
  111. {
  112.     connects(0);
  113. }  
  114.  
  115. void disconnect(int onlyclean, int async)
  116. {
  117.     bool cleanup = onlyclean!=0;
  118.     if(curpeer)
  119.     {
  120.         if(!discmillis)
  121.         {
  122.             enet_peer_disconnect(curpeer, DISC_NONE);
  123.             enet_host_flush(clienthost);
  124.             discmillis = lastmillis;
  125.         }
  126.         if(curpeer->state!=ENET_PEER_STATE_DISCONNECTED)
  127.         {
  128.             if(async) return;
  129.             enet_peer_reset(curpeer);
  130.         }
  131.         curpeer = NULL;
  132.         discmillis = 0;
  133.         connected = 0;
  134.         conoutf("disconnected");
  135.         cleanup = true;
  136.     }
  137.     else if(demoplayback)
  138.     {
  139.         // todo: additional cleanups
  140.         cleanup = true;
  141.     }
  142.  
  143.     if(cleanup)
  144.     {
  145.         stop();
  146.         c2sinit = false;
  147.         player1->clientnum = -1;
  148.         player1->lifesequence = 0;
  149.         player1->clientrole = CR_DEFAULT;
  150.         if(m_botmode) BotManager.EndMap();
  151.         loopv(players) zapplayer(players[i]);
  152.         localdisconnect();
  153.     }
  154.     if(!connpeer && clienthost)
  155.     {
  156.         enet_host_destroy(clienthost);
  157.         clienthost = NULL;
  158.     }
  159.     if(!onlyclean) localconnect();
  160. }
  161.  
  162. void trydisconnect()
  163. {
  164.     if(connpeer)
  165.     {
  166.         conoutf("aborting connection attempt");
  167.         abortconnect();
  168.         return;
  169.     }
  170.     if(!curpeer)
  171.     {
  172.         conoutf("not connected");
  173.         return;
  174.     }
  175.     conoutf("attempting to disconnect...");
  176.     disconnect(0, !discmillis);
  177. }
  178.  
  179. void toserver(char *text) 
  180.     bool toteam = text && text[0] == '%';
  181.     if(toteam) text++;
  182.     conoutf("%s:\f%d %s", colorname(player1), toteam ? 1 : 0, text);
  183.     addmsg(toteam ? SV_TEAMTEXT : SV_TEXT, "rs", text);
  184. }
  185.  
  186. void echo(char *text) { conoutf("%s", text); }
  187.  
  188. COMMAND(echo, ARG_VARI);
  189. COMMANDN(say, toserver, ARG_VARI);
  190. COMMANDN(connect, connects, ARG_2STR);
  191. COMMAND(connectadmin, ARG_2STR);
  192. COMMAND(lanconnect, ARG_NONE);
  193. COMMANDN(disconnect, trydisconnect, ARG_NONE);
  194.  
  195. // collect c2s messages conveniently
  196.  
  197. vector<uchar> messages;
  198.  
  199. void addmsg(int type, const char *fmt, ...)
  200. {
  201.     if(demoplayback) return;
  202.     static uchar buf[MAXTRANS];
  203.     ucharbuf p(buf, MAXTRANS);
  204.     putint(p, type);
  205.     int numi = 1, nums = 0;
  206.     bool reliable = false;
  207.     if(fmt)
  208.     {
  209.         va_list args;
  210.         va_start(args, fmt);
  211.         while(*fmt) switch(*fmt++)
  212.         {
  213.             case 'r': reliable = true; break;
  214.             case 'i':
  215.             {
  216.                 int n = isdigit(*fmt) ? *fmt++-'0' : 1;
  217.                 loopi(n) putint(p, va_arg(args, int));
  218.                 numi += n;
  219.                 break;
  220.             }
  221.             case 's': sendstring(va_arg(args, const char *), p); nums++; break;
  222.         }
  223.         va_end(args);
  224.     }
  225.     int num = nums?0:numi;
  226.     if(num!=msgsizelookup(type)) { s_sprintfd(s)("inconsistant msg size for %d (%d != %d)", type, num, msgsizelookup(type)); fatal(s); }
  227.     int len = p.length();
  228.     messages.add(len&0xFF);
  229.     messages.add((len>>8)|(reliable ? 0x80 : 0));
  230.     loopi(len) messages.add(buf[i]);
  231. }
  232.  
  233. static int lastupdate = 0, lastping = 0, laststate = -1;
  234. bool senditemstoserver = false;     // after a map change, since server doesn't have map data
  235.  
  236. void sendpackettoserv(int chan, ENetPacket *packet)
  237. {
  238.     if(curpeer) enet_peer_send(curpeer, chan, packet);
  239.     else localclienttoserver(chan, packet);
  240. }
  241.  
  242. void c2skeepalive()
  243. {
  244.     if(clienthost) enet_host_service(clienthost, NULL, 0);
  245. }
  246.  
  247. extern string masterpwd;
  248.  
  249. void c2sinfo(playerent *d)                  // send update to the server
  250. {
  251.     if(d->clientnum<0) return;              // we haven't had a welcome message from the server yet
  252.     if(lastmillis-lastupdate<40) return;    // don't update faster than 25fps
  253.     
  254.     bool hasmsg = gun_changed || senditemstoserver || !c2sinit || messages.length() || lastmillis-lastping>250;
  255.     // limit updates for dead players to the ping rate of 4fps, as above
  256.     if(!hasmsg && laststate==CS_DEAD && player1->state==CS_DEAD) return;
  257.     
  258.     laststate = player1->state;
  259.  
  260.     ENetPacket *packet = enet_packet_create(NULL, 100, 0);
  261.     ucharbuf q(packet->data, packet->dataLength);
  262.  
  263.     putint(q, SV_POS);
  264.     putint(q, d->clientnum);
  265.     putuint(q, (int)(d->o.x*DMF));       // quantize coordinates to 1/16th of a cube, between 1 and 3 bytes
  266.     putuint(q, (int)(d->o.y*DMF));
  267.     putuint(q, (int)(d->o.z*DMF));
  268.     putuint(q, (int)(d->yaw*DAF));
  269.     putint(q, (int)(d->pitch*DAF));
  270.     putint(q, (int)(d->roll*DAF));
  271.     putint(q, (int)(d->vel.x*DVF));     // quantize to 1/100, almost always 1 byte
  272.     putint(q, (int)(d->vel.y*DVF));
  273.     putint(q, (int)(d->vel.z*DVF));
  274.     // pack rest in 1 int: strafe:2, move:2, onfloor:1, state:3, onladder: 1
  275.     putint(q, (d->strafe&3) | ((d->move&3)<<2) | (((int)d->onfloor)<<4) | ((editmode ? CS_EDITING : d->state)<<5) | (((int)d->onladder)<<8) );
  276.  
  277.     enet_packet_resize(packet, q.length());
  278.     incomingdemodata(0, q.buf, q.length(), true);
  279.     sendpackettoserv(0, packet);
  280.  
  281.     bool serveriteminitdone = false;
  282.     if(hasmsg)
  283.     {
  284.         packet = enet_packet_create (NULL, MAXTRANS, 0);
  285.         ucharbuf p(packet->data, packet->dataLength);
  286.     
  287.         if(!c2sinit)    // tell other clients who I am
  288.         {
  289.             packet->flags = ENET_PACKET_FLAG_RELIABLE;
  290.             c2sinit = true;
  291.             if(clientpassword[0])
  292.             {
  293.                 putint(p, SV_PWD);
  294.                 sendstring(clientpassword, p);
  295.                 clientpassword[0] = 0;
  296.             }
  297.             putint(p, SV_INITC2S);
  298.             sendstring(player1->name, p);
  299.             sendstring(player1->team, p);
  300.             putint(p, player1->skin);
  301.             putint(p, player1->lifesequence);
  302.         }
  303.         if(gun_changed)
  304.         {
  305.             packet->flags = ENET_PACKET_FLAG_RELIABLE;
  306.             putint(p, SV_WEAPCHANGE);
  307.             putint(p, player1->gunselect);
  308.             gun_changed = false;       
  309.         }
  310.         if(senditemstoserver)
  311.         {
  312.             packet->flags = ENET_PACKET_FLAG_RELIABLE;
  313.             putint(p, SV_ITEMLIST);
  314.             if(!m_noitems) putitems(p);
  315.             putint(p, -1);
  316.             senditemstoserver = false;
  317.             serveriteminitdone = true;
  318.         }
  319.         int i = 0;
  320.         while(i < messages.length()) // send messages collected during the previous frames
  321.         {
  322.             int len = messages[i] | ((messages[i+1]&0x7F)<<8);
  323.             if(p.remaining() < len) break;
  324.             if(messages[i+1]&0x80) packet->flags = ENET_PACKET_FLAG_RELIABLE;
  325.             p.put(&messages[i+2], len);
  326.             i += 2 + len;
  327.         }
  328.         messages.remove(0, i);
  329.         if(lastmillis-lastping>250)
  330.         {
  331.             putint(p, SV_PING);
  332.             putint(p, lastmillis);
  333.             lastping = lastmillis;
  334.         }
  335.         if(!p.length()) enet_packet_destroy(packet);
  336.         else
  337.         {
  338.             enet_packet_resize(packet, p.length());
  339.             incomingdemodata(42, p.buf, p.length());
  340.             sendpackettoserv(1, packet);
  341.         }
  342.     }
  343.     if(clienthost) enet_host_flush(clienthost);
  344.     lastupdate = lastmillis;
  345.     if(serveriteminitdone) loadgamerest();  // hack
  346. }
  347.  
  348. void gets2c()           // get updates from the server
  349. {
  350.     ENetEvent event;
  351.     if(!clienthost) return;
  352.     if(connpeer && lastmillis/3000 > connmillis/3000)
  353.     {
  354.         conoutf("attempting to connect...");
  355.         connmillis = lastmillis;
  356.         ++connattempts;
  357.         if(connattempts > 3)
  358.         {
  359.             conoutf("\f3could not connect to server");
  360.             abortconnect();
  361.             return;
  362.         }
  363.     }
  364.     while(clienthost!=NULL && enet_host_service(clienthost, &event, 0)>0)
  365.     switch(event.type)
  366.     {
  367.         case ENET_EVENT_TYPE_CONNECT:
  368.             disconnect(1);
  369.             curpeer = connpeer;
  370.             connpeer = NULL;
  371.             connected = 1;
  372.             conoutf("connected to server");
  373.             throttle();
  374.             if(rate) setrate(rate);
  375.             if(editmode) toggleedit();
  376.             break;
  377.          
  378.         case ENET_EVENT_TYPE_RECEIVE:
  379.             if(discmillis) conoutf("attempting to disconnect...");
  380.             else localservertoclient(event.channelID, event.packet->data, (int)event.packet->dataLength);
  381.             enet_packet_destroy(event.packet);
  382.             break;
  383.  
  384.         case ENET_EVENT_TYPE_DISCONNECT:
  385.             extern const char *disc_reason(int reason);
  386.             if(event.data>=DISC_NUM) event.data = DISC_NONE;
  387.             if(event.peer==connpeer)
  388.             {
  389.                 conoutf("\f3could not connect to server");
  390.                 abortconnect();
  391.             }
  392.             else
  393.             {
  394.                 if(!discmillis || event.data) conoutf("\f3server network error, disconnecting (%s) ...", disc_reason(event.data));
  395.                 disconnect();
  396.             }
  397.             return;
  398.  
  399.         default:
  400.             break;
  401.     }
  402. }
  403.  
  404. // sendmap/getmap commands, should be replaced by more intuitive map downloading
  405.  
  406. cvector securemaps;
  407.  
  408. void clearsecuremaps() { securemaps.deletecontentsa(); }
  409. void securemap(char *map) { if(map) securemaps.add(newstring(map)); }
  410. bool securemapcheck(char *map)
  411. {
  412.     if(strstr(map, "maps/")==map || strstr(map, "maps\\")==map) map += strlen("maps/");
  413.     loopv(securemaps) if(!strcmp(securemaps[i], map))
  414.     {
  415.         conoutf("\f3map %s is secured, you can not send or overwrite it", map);
  416.         return true;
  417.     }
  418.     return false;
  419. }
  420.  
  421.  
  422. void sendmap(char *mapname)
  423. {
  424.     if(*mapname)
  425.     {
  426.         save_world(mapname);
  427.         changemap(mapname);
  428.     }    
  429.     mapname = getclientmap();
  430.     if(securemapcheck(mapname)) return;
  431.     int mapsize;
  432.     uchar *mapdata = readmap(mapname, &mapsize); 
  433.     if(!mapdata) return;
  434.     ENetPacket *packet = enet_packet_create(NULL, MAXTRANS + mapsize, ENET_PACKET_FLAG_RELIABLE);
  435.     ucharbuf p(packet->data, packet->dataLength);
  436.     putint(p, SV_SENDMAP);
  437.     sendstring(mapname, p);
  438.     putint(p, mapsize);
  439.     if(65535 - p.length() < mapsize)
  440.     {
  441.         conoutf("map %s is too large to send", mapname);
  442.         delete[] mapdata;
  443.         enet_packet_destroy(packet);
  444.         return;
  445.     }
  446.     p.put(mapdata, mapsize);
  447.     delete[] mapdata; 
  448.     enet_packet_resize(packet, p.length());
  449.     sendpackettoserv(2, packet);
  450.     conoutf("sending map %s to server...", mapname);
  451.     s_sprintfd(msg)("[map %s uploaded to server, \"getmap\" to receive it]", mapname);
  452.     toserver(msg);
  453. }
  454.  
  455. void getmap()
  456. {
  457.     if(securemapcheck(getclientmap())) return;
  458.     ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
  459.     ucharbuf p(packet->data, packet->dataLength);
  460.     putint(p, SV_RECVMAP);
  461.     enet_packet_resize(packet, p.length());
  462.     sendpackettoserv(2, packet);
  463.     conoutf("requesting map from server...");
  464. }
  465.  
  466. COMMAND(sendmap, ARG_1STR);
  467. COMMAND(getmap, ARG_NONE);
  468. COMMAND(clearsecuremaps, ARG_NONE);
  469. COMMAND(securemap, ARG_1STR);
  470.  
  471.