src/network/network.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  * Network layer for Wormux.
00020  *****************************************************************************/
00021 
00022 #include "network.h"
00023 //-----------------------------------------------------------------------------
00024 #include <SDL_net.h>
00025 #include <SDL_thread.h>
00026 #include <netinet/in.h>
00027 #include "../game/game_mode.h"
00028 #include "../game/game.h"
00029 #include "../gui/question.h"
00030 #include "../include/action_handler.h"
00031 #include "../tool/debug.h"
00032 #include "../tool/i18n.h"
00033 #include "distant_cpu.h"
00034 
00035 #if defined(DEBUG) && not defined(WIN32)
00036 #include <sys/types.h>
00037 #include <sys/stat.h>
00038 #include <fcntl.h>
00039 #endif
00040 //-----------------------------------------------------------------------------
00041 Network network;
00042 
00043 //-----------------------------------------------------------------------------
00044 
00045 Network::Network()
00046 {
00047   max_player_number = 0;
00048   m_is_connected = false;
00049   m_is_server = false;
00050   m_is_client = false;
00051   state = NETWORK_NOT_CONNECTED;
00052   inited = false;
00053   sync_lock = false;
00054   network_menu = NULL;
00055 
00056   //Set nickname
00057 #ifdef WIN32
00058   nickname = getenv("USERNAME");
00059 #else
00060   nickname = getenv("USER");
00061 #endif
00062 }
00063 
00064 //-----------------------------------------------------------------------------
00065 int net_thread_func(void* no_param)
00066 {
00067   network.ReceiveActions();
00068   network.Disconnect();
00069   return 1;
00070 }
00071 
00072 //-----------------------------------------------------------------------------
00073 
00074 void Network::Init()
00075 {
00076   if(inited) return;
00077   if (SDLNet_Init()) {
00078       Error(_("Failed to initialize network library!"));
00079   }
00080   inited = true;
00081   max_player_number = GameMode::GetInstance()->max_teams;
00082   connected_player = 0;
00083 
00084 #if defined(DEBUG) && not defined(WIN32)
00085   fin = open("./network.in", O_RDWR | O_CREAT | O_SYNC, S_IRWXU | S_IRWXG);
00086   fout = open("./network.out", O_RDWR | O_CREAT | O_SYNC, S_IRWXU | S_IRWXG);
00087 #endif
00088 }
00089 
00090 //-----------------------------------------------------------------------------
00091 
00092 Network::~Network()
00093 {
00094   Disconnect();
00095   if(inited)
00096   {
00097     SDLNet_Quit();
00098 #if defined(DEBUG) && not defined(WIN32)
00099     close(fin);
00100     close(fout);
00101 #endif
00102   }
00103 }
00104 
00105 //-----------------------------------------------------------------------------
00106 
00107 void Network::Disconnect()
00108 {
00109   if(!m_is_connected) return;
00110 
00111   m_is_connected = false; // To make the threads terminate
00112 
00113   SDL_WaitThread(thread,NULL);
00114   printf("Network thread finished\n");
00115   for(std::list<DistantComputer*>::iterator client = cpu.begin();
00116       client != cpu.end();
00117       client++)
00118   {
00119     delete *client;
00120   }
00121   cpu.clear();
00122   SDLNet_FreeSocketSet(socket_set);
00123 
00124   if(m_is_server)
00125     SDLNet_TCP_Close(server_socket);
00126 
00127   m_is_server = false;
00128   m_is_client = false;
00129 }
00130 
00131 //-----------------------------------------------------------------------------
00132 //----------------       Client specific methods   ----------------------------
00133 //-----------------------------------------------------------------------------
00134 void Network::ClientConnect(const std::string &host, const std::string& port)
00135 {
00136   MSG_DEBUG("network", "Client connect to %s:%s", host.c_str(), port.c_str());
00137 
00138   int prt=0;
00139   sscanf(port.c_str(),"%i",&prt);
00140   prt = htons(prt);
00141 
00142   if(SDLNet_ResolveHost(&ip,host.c_str(),prt)==-1)
00143   {
00144     Question question;
00145     question.Set(_("Invalid server adress!"),1,0);
00146     question.Ask();
00147     printf("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
00148     return;
00149   }
00150 
00151   ip.port = prt;
00152 
00153   TCPsocket socket = SDLNet_TCP_Open(&ip);
00154 
00155   if(!socket)
00156   {
00157     Question question;
00158     question.Set(_("Unable to contact server!"),1,0);
00159     question.Ask();
00160     printf("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
00161     return;
00162   }
00163 
00164   m_is_client = true;
00165   m_is_server = false;
00166   state = NETWORK_OPTION_SCREEN;
00167   m_is_connected = true;
00168 
00169   socket_set = SDLNet_AllocSocketSet(1);
00170   connected_player = 1;
00171   cpu.push_back(new DistantComputer(socket));
00172   //Send nickname to server
00173   Action a(Action::ACTION_NICKNAME, nickname);
00174   network.SendAction(&a);
00175 
00176   //Control to net_thread_func
00177   thread = SDL_CreateThread(net_thread_func,NULL);
00178 }
00179 
00180 //-----------------------------------------------------------------------------
00181 //----------------       Server specific methods   ----------------------------
00182 //-----------------------------------------------------------------------------
00183 
00184 void Network::ServerStart(const std::string &port)
00185 {
00186   // The server starts listening for clients
00187   MSG_DEBUG("network", "Start server on port %s", port.c_str());
00188 
00189   cpu.clear();
00190   // Convert port number (std::string port) into SDL port number format:
00191   int prt;
00192   sscanf(port.c_str(),"%i",&prt);
00193   prt = htons(prt);
00194 
00195   if(SDLNet_ResolveHost(&ip,NULL,prt)==-1)
00196   {
00197     Question question;
00198     question.Set(_("Invalid port!"),1,0);
00199     question.Ask();
00200     printf("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
00201     return;
00202   }
00203   ip.port = prt;
00204 
00205   m_is_server = true;
00206   m_is_client = false;
00207   m_is_connected = true;
00208 
00209   // Open the port to listen to
00210   state = NETWORK_OPTION_SCREEN;
00211   AcceptIncoming();
00212   connected_player = 1;
00213   printf("\nConnected\n");
00214   socket_set = SDLNet_AllocSocketSet(GameMode::GetInstance()->max_teams);
00215   thread = SDL_CreateThread(net_thread_func,NULL);
00216 }
00217 
00218 std::list<DistantComputer*>::iterator Network::CloseConnection(std::list<DistantComputer*>::iterator closed)
00219 {
00220   printf("Client disconnected\n");
00221   delete *closed;
00222   if(m_is_server && connected_player == max_player_number)
00223   {
00224     // A new player will be able to connect, so we reopen the socket
00225     // For incoming connections
00226     printf("Allowing new connections\n");
00227     AcceptIncoming();
00228   }
00229 
00230   connected_player--;
00231   return cpu.erase(closed);
00232 }
00233 
00234 void Network::AcceptIncoming()
00235 {
00236   assert(m_is_server);
00237   if(state != NETWORK_OPTION_SCREEN) return;
00238 
00239   server_socket = SDLNet_TCP_Open(&ip);
00240   if(!server_socket)
00241   {
00242     Question question;
00243     question.Set(_("Unable to listen for client!"),1,0);
00244     question.Ask();
00245     printf("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
00246     return;
00247   }
00248   printf("\nStart listening");
00249 }
00250 
00251 void Network::RejectIncoming()
00252 {
00253   assert(m_is_server);
00254   if(!server_socket) return;
00255   SDLNet_TCP_Close(server_socket);
00256   server_socket = NULL;
00257   printf("\nStop listening");
00258 }
00259 
00260 //-----------------------------------------------------------------------------
00261 //----------------       Action handling methods   ----------------------------
00262 //-----------------------------------------------------------------------------
00263 void Network::ReceiveActions()
00264 {
00265   char* packet;
00266 
00267   while(m_is_connected && (cpu.size()==1 || m_is_server))
00268   {
00269     if(state == NETWORK_PLAYING && cpu.size() == 0)
00270     {
00271       // If while playing everybody disconnected, just quit
00272       break;
00273     }
00274 
00275     while(SDLNet_CheckSockets(socket_set, 100) == 0 && m_is_connected) //Loop while nothing is received
00276     if(m_is_server && server_socket)
00277     {
00278       // Check for an incoming connection
00279       TCPsocket incoming;
00280       incoming = SDLNet_TCP_Accept(server_socket);
00281       if(incoming)
00282       {
00283         cpu.push_back(new DistantComputer(incoming));
00284         connected_player++;
00285         printf("New client connected\n");
00286         if(connected_player >= max_player_number)
00287           RejectIncoming();
00288         ActionHandler::GetInstance()->NewAction(new Action(Action::ACTION_ASK_VERSION));
00289       }
00290     }
00291 
00292     std::list<DistantComputer*>::iterator dst_cpu = cpu.begin();
00293     while(dst_cpu != cpu.end() && m_is_connected)
00294     {
00295       if((*dst_cpu)->SocketReady()) // Check if this socket contains data to receive
00296       {
00297         // Read the size of the packet
00298         int packet_size = (*dst_cpu)->ReceiveDatas(packet);
00299         if( packet_size <= 0)
00300         {
00301           dst_cpu = CloseConnection(dst_cpu);
00302           continue;
00303         }
00304 
00305 #if defined(DEBUG) && not defined(WIN32)
00306         int tmp = 0xFFFFFFFF;
00307         write(fin, &packet_size, 4);
00308         write(fin, packet, packet_size);
00309         write(fin, &tmp, 4);
00310 #endif
00311 
00312         Action* a = new Action(packet);
00313         MSG_DEBUG("network.traffic","Received action %s",
00314                 ActionHandler::GetInstance()->GetActionName(a->GetType()).c_str());
00315 
00316         //Add relation between nickname and socket
00317         if( a->GetType() == Action::ACTION_NICKNAME){
00318           std::string nickname = a->PopString();
00319           std::cout<<"New nickname: " + nickname<< std::endl;
00320           (*dst_cpu)->nickname = nickname;
00321           delete a;
00322           break;
00323         }
00324 
00325         if( a->GetType() == Action::ACTION_NEW_TEAM
00326         ||  a->GetType() == Action::ACTION_DEL_TEAM)
00327         {
00328           (*dst_cpu)->ManageTeam(a);
00329           delete a;
00330         }
00331         else
00332         if(a->GetType() == Action::ACTION_CHAT_MESSAGE)
00333         {
00334           (*dst_cpu)->SendChatMessage(a);
00335           delete a;
00336         }
00337         else
00338         {
00339           ActionHandler::GetInstance()->NewAction(a, false);
00340         }
00341 
00342         // Repeat the packet to other clients:
00343         if(a->GetType() != Action::ACTION_SEND_VERSION
00344         && a->GetType() != Action::ACTION_CHANGE_STATE
00345         && a->GetType() != Action::ACTION_CHAT_MESSAGE)
00346         for(std::list<DistantComputer*>::iterator client = cpu.begin();
00347             client != cpu.end();
00348             client++)
00349         if(client != dst_cpu)
00350         {
00351           (*client)->SendDatas(packet, packet_size);
00352         }
00353         free(packet);
00354       }
00355       dst_cpu++;
00356     }
00357   }
00358   Game::GetInstance()->SetEndOfGameStatus( true );
00359 }
00360 
00361 // Send Messages
00362 void Network::SendAction(Action* a)
00363 {
00364   if (!m_is_connected) return;
00365 
00366   MSG_DEBUG("network.traffic","Send action %s",
00367         ActionHandler::GetInstance()->GetActionName(a->GetType()).c_str());
00368 
00369   int size;
00370   char* packet;
00371   a->WritePacket(packet, size);
00372 
00373   assert(*((int*)packet) != 0 );
00374   SendPacket(packet, size);
00375 
00376   free(packet);
00377 }
00378 
00379 void Network::SendPacket(char* packet, int size)
00380 {
00381 #if defined(DEBUG) && not defined(WIN32)
00382         int tmp = 0xFFFFFFFF;
00383         write(fout, &size, 4);
00384         write(fout, packet, size);
00385         write(fout, &tmp, 4);
00386 #endif
00387   for(std::list<DistantComputer*>::iterator client = cpu.begin();
00388       client != cpu.end();
00389       client++)
00390   {
00391     (*client)->SendDatas(packet, size);
00392   }
00393 }
00394 
00395 void Network::SendChatMessage(std::string txt)
00396 {
00397   if(IsServer())
00398   {
00399     ActionHandler::GetInstance()->NewAction(new Action(Action::ACTION_CHAT_MESSAGE, nickname + std::string("> ") + txt));
00400   }
00401   else
00402   {
00403     Action a(Action::ACTION_CHAT_MESSAGE, txt);
00404     network.SendAction(&a);
00405   }
00406 }
00407 
00408 //-----------------------------------------------------------------------------
00409 
00410 const bool Network::IsConnected() const { return m_is_connected; }
00411 const bool Network::IsLocal() const { return !m_is_server && !m_is_client; }
00412 const bool Network::IsServer() const { return m_is_server; }
00413 const bool Network::IsClient() const { return m_is_client; }
00414 const uint Network::GetPort() const { return ntohs(ip.port); }
00415 
00416 //-----------------------------------------------------------------------------

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