src/network/index_server.cpp

Go to the documentation of this file.
00001 /******************************************************************************
00002  *  Wormux is a convivial mass murder game.
00003  *  Copyright (C) 2001-2004 Lawrence Azzoug.
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
00018  ******************************************************************************
00019  * Notify an index server of an opened wormux server
00020  * Obtain information about running games from an index server
00021  *****************************************************************************/
00022 
00023 #include <SDL_net.h>
00024 #include <fstream>
00025 #include "download.h"
00026 #include "index_server.h"
00027 #include "index_svr_msg.h"
00028 #include "network.h"
00029 #include "gui/question.h"
00030 #include "include/app.h"
00031 #include "include/constant.h"
00032 #include "tool/debug.h"
00033 #include "tool/i18n.h"
00034 #include "tool/random.h"
00035 
00036 IndexServer index_server;
00037 
00038 IndexServer::IndexServer()
00039 {
00040   hidden_server = false;
00041   connected = false;
00042   server_lst.clear();
00043   first_server = server_lst.end();
00044   current_server = server_lst.end();
00045 }
00046 
00047 IndexServer::~IndexServer()
00048 {
00049   server_lst.clear();
00050   if(connected)
00051     Disconnect();
00052 }
00053 
00054 /*************  Connection  /  Disconnection  ******************/
00055 bool IndexServer::Connect()
00056 {
00057   MSG_DEBUG("index_server", "Connecting..");
00058   assert(!connected);
00059 
00060   if( hidden_server )
00061     return true;
00062 
00063   if( !GetServerList() )
00064     return false;
00065 
00066   std::string addr;
00067   int port;
00068 
00069   // Cycle through the list of server
00070   // Until we find one running
00071   while( GetServerAddress( addr, port) )
00072   {
00073     if( ConnectTo( addr, port) )
00074       return true;
00075   }
00076 
00077   Question question;
00078   question.Set(_("Unable to contact an index server!"),1,0);
00079   question.Ask();
00080 
00081   return false;
00082 }
00083 
00084 bool IndexServer::ConnectTo(const std::string & address, const int & port)
00085 {
00086   MSG_DEBUG("index_server", "Connecting to %s %i", address.c_str(), port);
00087   Question question;
00088   question.Set(_("Contacting main server..."),1,0);
00089   question.Draw();
00090   AppWormux::GetInstance()->video.Flip();
00091 
00092   network.Init(); // To get SDL_net initialized
00093 
00094   MSG_DEBUG("index_server", "Opening connection");
00095 
00096 //  if( SDLNet_ResolveHost(&ip, "localhost" , port) == -1 )
00097   if( SDLNet_ResolveHost(&ip, address.c_str() , port) == -1 )
00098   {
00099     question.Set(_("Invalid index server adress!"),1,0);
00100     question.Ask();
00101     printf("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
00102     return false;
00103   }
00104 
00105   socket = SDLNet_TCP_Open(&ip);
00106 
00107   if(!socket)
00108   {
00109     printf("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
00110     return false;
00111   }
00112 
00113   connected = true;
00114 
00115   return HandShake();
00116 }
00117 
00118 void IndexServer::Disconnect()
00119 {
00120   if( hidden_server )
00121   {
00122     hidden_server = false;
00123     return;
00124   }
00125 
00126   if( !connected )
00127     return;
00128 
00129   MSG_DEBUG("index_server", "Closing connection");
00130   first_server = server_lst.end();
00131   current_server = server_lst.end();
00132 
00133   SDLNet_TCP_Close(socket);
00134   connected = false;
00135 }
00136 
00137 static ssize_t getline(std::string& line, std::ifstream& file)
00138 {
00139         line.clear();
00140         std::getline(file, line);
00141         if(file.eof())
00142                 return -1;
00143         return line.size();
00144 }
00145 
00146 bool IndexServer::GetServerList()
00147 {
00148   MSG_DEBUG("index_server", "Retrieving server list");
00149   // If we already have it, no need to redownload it
00150   if(server_lst.size() != 0)
00151     return true;
00152 
00153   // Download the list of user
00154   const std::string server_file = Config::GetInstance()->GetPersonalDir() + "server_list";
00155 
00156   if( !downloader.Get(server_list_url.c_str(), server_file.c_str()) )
00157     return false;
00158 
00159   // Parse the file
00160   std::ifstream fin;
00161   fin.open(server_file.c_str(), std::ios::in);
00162   if(!fin)
00163         return false;
00164 
00165   /*char * line = NULL;
00166   size_t len = 0;*/
00167   ssize_t read;
00168   std::string line;
00169  
00170   // GNU getline isn't available on *BSD and Win32, so we use a new function, see getline above
00171   while ((read = getline(line, fin)) >= 0)
00172   {
00173     if(line.at(0) == '#' || line.at(0) == '\n' || line.at(0) == '\0')
00174       continue;
00175 
00176     std::string::size_type port_pos = line.find(':', 0);
00177     if(port_pos == std::string::npos)
00178       continue;
00179 
00180     std::string hostname = line.substr(0, port_pos);
00181     std::string portstr = line.substr(port_pos+1);
00182     int port = atoi(portstr.c_str());
00183 
00184     server_lst[ hostname ] = port;
00185   }
00186 
00187   fin.close();
00188 
00189   first_server = server_lst.end();
00190   current_server = server_lst.end();
00191   MSG_DEBUG("index_server", "Server list retrieved. %i servers are running", server_lst.size());
00192 
00193   return (server_lst.size() != 0);
00194 }
00195 
00196 bool IndexServer::GetServerAddress( std::string & address, int & port)
00197 {
00198   MSG_DEBUG("index_server", "Trying a new server");
00199   // Cycle through the server list to find the first one
00200   // accepting connection
00201   if( first_server == server_lst.end() )
00202   {
00203     // First try :
00204     // Randomly select a server in the list
00205     int nbr = randomObj.GetLong( 0, server_lst.size()-1 );
00206     first_server = server_lst.begin();
00207     while(nbr--)
00208       ++first_server;
00209 
00210     assert(first_server != server_lst.end());
00211 
00212     current_server = first_server;
00213 
00214     address = current_server->first;
00215     port = current_server->second;
00216     return true;
00217   }
00218 
00219   ++current_server;
00220   if( current_server == server_lst.end() )
00221     current_server = server_lst.begin();
00222 
00223   address = current_server->first;
00224   port = current_server->second;
00225 
00226   return (current_server != first_server);
00227 }
00228 
00229 /*************  Basic transmissions  ******************/
00230 void IndexServer::Send(const int& nbr)
00231 {
00232   char packet[4];
00233   // this is not cute, but we don't want an int -> uint conversion here
00234   Uint32 u_nbr = *((Uint32*)&nbr);
00235 
00236   SDLNet_Write32(u_nbr, packet);
00237   SDLNet_TCP_Send(socket, packet, sizeof(packet));
00238 }
00239 
00240 void IndexServer::Send(const std::string &str)
00241 {
00242   Send(str.size());
00243   SDLNet_TCP_Send(socket, (void*)str.c_str(), str.size());
00244 }
00245 
00246 int IndexServer::ReceiveInt()
00247 {
00248   char packet[4];
00249   if( SDLNet_TCP_Recv(socket, packet, sizeof(packet)) < 1 )
00250   {
00251     Disconnect();
00252     return 0;
00253   }
00254 
00255   Uint32 u_nbr = SDLNet_Read32(packet);
00256   int nbr = *((int*)&u_nbr);
00257   return nbr;
00258 }
00259 
00260 std::string IndexServer::ReceiveStr()
00261 {
00262   int size = ReceiveInt();
00263 
00264   if(!connected)
00265     return "";
00266 
00267   assert(size > 0);
00268   char* str = new char[size+1];
00269 
00270   if( SDLNet_TCP_Recv(socket, str, size) < 1 )
00271   {
00272     Disconnect();
00273     return "";
00274   }
00275 
00276   str[size] = '\0';
00277 
00278   std::string st(str);
00279   delete []str;
00280   return st;
00281 }
00282 
00283 bool IndexServer::HandShake()
00284 {
00285   Send(TS_MSG_VERSION);
00286   Send(Constants::VERSION);
00287 
00288   int msg = ReceiveInt();
00289   std::string sign;
00290 
00291   if(msg == TS_MSG_VERSION)
00292     sign = ReceiveStr();
00293 
00294   if(msg != TS_MSG_VERSION || sign != "MassMurder!")
00295   {
00296     Question question;
00297     question.Set(_("It doesn't seem to be a valid Wormux server..."),1,0);
00298     question.Ask();
00299     Disconnect();
00300     return false;
00301   }
00302   return true;
00303 }
00304 
00305 void IndexServer::SendServerStatus()
00306 {
00307   assert(network.IsServer());
00308 
00309   if(hidden_server)
00310     return;
00311   Send(TS_MSG_HOSTING);
00312   Send(network.GetPort());
00313 }
00314 
00315 std::list<address_pair> IndexServer::GetHostList()
00316 {
00317   Send(TS_MSG_GET_LIST);
00318   int lst_size = ReceiveInt();
00319   std::list<address_pair> lst;
00320   while(lst_size--)
00321   {
00322     IPaddress ip;
00323     ip.host = ReceiveInt();
00324     ip.port = ReceiveInt();
00325     const char* addr = SDLNet_ResolveIP(&ip);
00326     char port[10];
00327     sprintf(port, "%d", ip.port);
00328 
00329     address_pair addr_pair;
00330     addr_pair.second = std::string(port);
00331 
00332     if(addr == NULL)
00333     {
00334       // We can't resolve the hostname, so just show the ip address
00335       unsigned char* str_ip = (unsigned char*)&ip.host;
00336       char formated_ip[16];
00337       snprintf(formated_ip, 16, "%i.%i.%i.%i", (int)str_ip[0],
00338                                            (int)str_ip[1],
00339                                            (int)str_ip[2],
00340                                            (int)str_ip[3]);
00341       addr_pair.first = std::string(formated_ip);
00342     }
00343     else
00344       addr_pair.first = std::string(addr);
00345     lst.push_back(addr_pair);
00346   }
00347   return lst;
00348 }

Generated on Mon Jan 1 13:10:59 2007 for Wormux by  doxygen 1.4.7