network.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "../stdafx.h"
00013 
00014 #ifdef ENABLE_NETWORK
00015 
00016 #include "../strings_func.h"
00017 #include "../command_func.h"
00018 #include "../date_func.h"
00019 #include "network_admin.h"
00020 #include "network_client.h"
00021 #include "network_server.h"
00022 #include "network_content.h"
00023 #include "network_udp.h"
00024 #include "network_gamelist.h"
00025 #include "network_base.h"
00026 #include "core/udp.h"
00027 #include "core/host.h"
00028 #include "network_gui.h"
00029 #include "../console_func.h"
00030 #include "../3rdparty/md5/md5.h"
00031 #include "../core/random_func.hpp"
00032 #include "../window_func.h"
00033 #include "../company_func.h"
00034 #include "../company_base.h"
00035 #include "../landscape_type.h"
00036 #include "../rev.h"
00037 #include "../core/pool_func.hpp"
00038 #include "../gfx_func.h"
00039 #include "../error.h"
00040 #include "table/strings.h"
00041 
00042 #ifdef DEBUG_DUMP_COMMANDS
00043 #include "../fileio_func.h"
00045 bool _ddc_fastforward = true;
00046 #endif /* DEBUG_DUMP_COMMANDS */
00047 
00049 assert_compile(NetworkClientInfoPool::MAX_SIZE == NetworkClientSocketPool::MAX_SIZE);
00050 
00052 NetworkClientInfoPool _networkclientinfo_pool("NetworkClientInfo");
00053 INSTANTIATE_POOL_METHODS(NetworkClientInfo)
00054 
00055 bool _networking;         
00056 bool _network_server;     
00057 bool _network_available;  
00058 bool _network_dedicated;  
00059 bool _is_network_server;  
00060 NetworkServerGameInfo _network_game_info; 
00061 NetworkCompanyState *_network_company_states = NULL; 
00062 ClientID _network_own_client_id;      
00063 ClientID _redirect_console_to_client; 
00064 bool _network_need_advertise;         
00065 uint32 _network_last_advertise_frame; 
00066 uint8 _network_reconnect;             
00067 StringList _network_bind_list;        
00068 StringList _network_host_list;        
00069 StringList _network_ban_list;         
00070 uint32 _frame_counter_server;         
00071 uint32 _frame_counter_max;            
00072 uint32 _frame_counter;                
00073 uint32 _last_sync_frame;              
00074 NetworkAddressList _broadcast_list;   
00075 uint32 _sync_seed_1;                  
00076 #ifdef NETWORK_SEND_DOUBLE_SEED
00077 uint32 _sync_seed_2;                  
00078 #endif
00079 uint32 _sync_frame;                   
00080 bool _network_first_time;             
00081 bool _network_udp_server;             
00082 uint16 _network_udp_broadcast;        
00083 uint8 _network_advertise_retries;     
00084 CompanyMask _network_company_passworded; 
00085 
00086 /* Check whether NETWORK_NUM_LANDSCAPES is still in sync with NUM_LANDSCAPE */
00087 assert_compile((int)NETWORK_NUM_LANDSCAPES == (int)NUM_LANDSCAPE);
00088 assert_compile((int)NETWORK_COMPANY_NAME_LENGTH == MAX_LENGTH_COMPANY_NAME_CHARS * MAX_CHAR_LENGTH);
00089 
00090 extern NetworkUDPSocketHandler *_udp_client_socket; 
00091 extern NetworkUDPSocketHandler *_udp_server_socket; 
00092 extern NetworkUDPSocketHandler *_udp_master_socket; 
00093 
00095 byte _network_clients_connected = 0;
00096 
00097 /* Some externs / forwards */
00098 extern void StateGameLoop();
00099 
00103 NetworkClientInfo::~NetworkClientInfo()
00104 {
00105   /* Delete the chat window, if you were chatting with this client. */
00106   InvalidateWindowData(WC_SEND_NETWORK_MSG, DESTTYPE_CLIENT, this->client_id);
00107 }
00108 
00114 /* static */ NetworkClientInfo *NetworkClientInfo::GetByClientID(ClientID client_id)
00115 {
00116   NetworkClientInfo *ci;
00117 
00118   FOR_ALL_CLIENT_INFOS(ci) {
00119     if (ci->client_id == client_id) return ci;
00120   }
00121 
00122   return NULL;
00123 }
00124 
00130 /* static */ ServerNetworkGameSocketHandler *ServerNetworkGameSocketHandler::GetByClientID(ClientID client_id)
00131 {
00132   NetworkClientSocket *cs;
00133 
00134   FOR_ALL_CLIENT_SOCKETS(cs) {
00135     if (cs->client_id == client_id) return cs;
00136   }
00137 
00138   return NULL;
00139 }
00140 
00141 byte NetworkSpectatorCount()
00142 {
00143   const NetworkClientInfo *ci;
00144   byte count = 0;
00145 
00146   FOR_ALL_CLIENT_INFOS(ci) {
00147     if (ci->client_playas == COMPANY_SPECTATOR) count++;
00148   }
00149 
00150   /* Don't count a dedicated server as spectator */
00151   if (_network_dedicated) count--;
00152 
00153   return count;
00154 }
00155 
00162 const char *NetworkChangeCompanyPassword(CompanyID company_id, const char *password)
00163 {
00164   if (strcmp(password, "*") == 0) password = "";
00165 
00166   if (_network_server) {
00167     NetworkServerSetCompanyPassword(company_id, password, false);
00168   } else {
00169     NetworkClientSetCompanyPassword(password);
00170   }
00171 
00172   return password;
00173 }
00174 
00182 const char *GenerateCompanyPasswordHash(const char *password, const char *password_server_id, uint32 password_game_seed)
00183 {
00184   if (StrEmpty(password)) return password;
00185 
00186   char salted_password[NETWORK_SERVER_ID_LENGTH];
00187 
00188   memset(salted_password, 0, sizeof(salted_password));
00189   snprintf(salted_password, sizeof(salted_password), "%s", password);
00190   /* Add the game seed and the server's ID as the salt. */
00191   for (uint i = 0; i < NETWORK_SERVER_ID_LENGTH - 1; i++) {
00192     salted_password[i] ^= password_server_id[i] ^ (password_game_seed >> (i % 32));
00193   }
00194 
00195   Md5 checksum;
00196   uint8 digest[16];
00197   static char hashed_password[NETWORK_SERVER_ID_LENGTH];
00198 
00199   /* Generate the MD5 hash */
00200   checksum.Append(salted_password, sizeof(salted_password) - 1);
00201   checksum.Finish(digest);
00202 
00203   for (int di = 0; di < 16; di++) sprintf(hashed_password + di * 2, "%02x", digest[di]);
00204   hashed_password[lengthof(hashed_password) - 1] = '\0';
00205 
00206   return hashed_password;
00207 }
00208 
00214 bool NetworkCompanyIsPassworded(CompanyID company_id)
00215 {
00216   return HasBit(_network_company_passworded, company_id);
00217 }
00218 
00219 /* This puts a text-message to the console, or in the future, the chat-box,
00220  *  (to keep it all a bit more general)
00221  * If 'self_send' is true, this is the client who is sending the message */
00222 void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send, const char *name, const char *str, int64 data)
00223 {
00224   StringID strid;
00225   switch (action) {
00226     case NETWORK_ACTION_SERVER_MESSAGE:
00227       /* Ignore invalid messages */
00228       strid = STR_NETWORK_SERVER_MESSAGE;
00229       colour = CC_DEFAULT;
00230       break;
00231     case NETWORK_ACTION_COMPANY_SPECTATOR:
00232       colour = CC_DEFAULT;
00233       strid = STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE;
00234       break;
00235     case NETWORK_ACTION_COMPANY_JOIN:
00236       colour = CC_DEFAULT;
00237       strid = STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN;
00238       break;
00239     case NETWORK_ACTION_COMPANY_NEW:
00240       colour = CC_DEFAULT;
00241       strid = STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW;
00242       break;
00243     case NETWORK_ACTION_JOIN:
00244       /* Show the Client ID for the server but not for the client. */
00245       strid = _network_server ? STR_NETWORK_MESSAGE_CLIENT_JOINED_ID :  STR_NETWORK_MESSAGE_CLIENT_JOINED;
00246       break;
00247     case NETWORK_ACTION_LEAVE:          strid = STR_NETWORK_MESSAGE_CLIENT_LEFT; break;
00248     case NETWORK_ACTION_NAME_CHANGE:    strid = STR_NETWORK_MESSAGE_NAME_CHANGE; break;
00249     case NETWORK_ACTION_GIVE_MONEY:     strid = self_send ? STR_NETWORK_MESSAGE_GAVE_MONEY_AWAY : STR_NETWORK_MESSAGE_GIVE_MONEY;   break;
00250     case NETWORK_ACTION_CHAT_COMPANY:   strid = self_send ? STR_NETWORK_CHAT_TO_COMPANY : STR_NETWORK_CHAT_COMPANY; break;
00251     case NETWORK_ACTION_CHAT_CLIENT:    strid = self_send ? STR_NETWORK_CHAT_TO_CLIENT  : STR_NETWORK_CHAT_CLIENT;  break;
00252     default:                            strid = STR_NETWORK_CHAT_ALL; break;
00253   }
00254 
00255   char message[1024];
00256   SetDParamStr(0, name);
00257   SetDParamStr(1, str);
00258   SetDParam(2, data);
00259 
00260   /* All of these strings start with "***". These characters are interpreted as both left-to-right and
00261    * right-to-left characters depending on the context. As the next text might be an user's name, the
00262    * user name's characters will influence the direction of the "***" instead of the language setting
00263    * of the game. Manually set the direction of the "***" by inserting a text-direction marker. */
00264   char *msg_ptr = message + Utf8Encode(message, _current_text_dir == TD_LTR ? CHAR_TD_LRM : CHAR_TD_RLM);
00265   GetString(msg_ptr, strid, lastof(message));
00266 
00267   DEBUG(desync, 1, "msg: %08x; %02x; %s", _date, _date_fract, message);
00268   IConsolePrintF(colour, "%s", message);
00269   NetworkAddChatMessage((TextColour)colour, _settings_client.gui.network_chat_timeout, "%s", message);
00270 }
00271 
00272 /* Calculate the frame-lag of a client */
00273 uint NetworkCalculateLag(const NetworkClientSocket *cs)
00274 {
00275   int lag = cs->last_frame_server - cs->last_frame;
00276   /* This client has missed his ACK packet after 1 DAY_TICKS..
00277    *  so we increase his lag for every frame that passes!
00278    * The packet can be out by a max of _net_frame_freq */
00279   if (cs->last_frame_server + DAY_TICKS + _settings_client.network.frame_freq < _frame_counter) {
00280     lag += _frame_counter - (cs->last_frame_server + DAY_TICKS + _settings_client.network.frame_freq);
00281   }
00282   return lag;
00283 }
00284 
00285 
00286 /* There was a non-recoverable error, drop back to the main menu with a nice
00287  *  error */
00288 void NetworkError(StringID error_string)
00289 {
00290   _switch_mode = SM_MENU;
00291   ShowErrorMessage(error_string, INVALID_STRING_ID, WL_CRITICAL);
00292 }
00293 
00299 StringID GetNetworkErrorMsg(NetworkErrorCode err)
00300 {
00301   /* List of possible network errors, used by
00302    * PACKET_SERVER_ERROR and PACKET_CLIENT_ERROR */
00303   static const StringID network_error_strings[] = {
00304     STR_NETWORK_ERROR_CLIENT_GENERAL,
00305     STR_NETWORK_ERROR_CLIENT_DESYNC,
00306     STR_NETWORK_ERROR_CLIENT_SAVEGAME,
00307     STR_NETWORK_ERROR_CLIENT_CONNECTION_LOST,
00308     STR_NETWORK_ERROR_CLIENT_PROTOCOL_ERROR,
00309     STR_NETWORK_ERROR_CLIENT_NEWGRF_MISMATCH,
00310     STR_NETWORK_ERROR_CLIENT_NOT_AUTHORIZED,
00311     STR_NETWORK_ERROR_CLIENT_NOT_EXPECTED,
00312     STR_NETWORK_ERROR_CLIENT_WRONG_REVISION,
00313     STR_NETWORK_ERROR_CLIENT_NAME_IN_USE,
00314     STR_NETWORK_ERROR_CLIENT_WRONG_PASSWORD,
00315     STR_NETWORK_ERROR_CLIENT_COMPANY_MISMATCH,
00316     STR_NETWORK_ERROR_CLIENT_KICKED,
00317     STR_NETWORK_ERROR_CLIENT_CHEATER,
00318     STR_NETWORK_ERROR_CLIENT_SERVER_FULL,
00319     STR_NETWORK_ERROR_CLIENT_TOO_MANY_COMMANDS
00320   };
00321 
00322   if (err >= (ptrdiff_t)lengthof(network_error_strings)) err = NETWORK_ERROR_GENERAL;
00323 
00324   return network_error_strings[err];
00325 }
00326 
00332 void NetworkHandlePauseChange(PauseMode prev_mode, PauseMode changed_mode)
00333 {
00334   if (!_networking) return;
00335 
00336   switch (changed_mode) {
00337     case PM_PAUSED_NORMAL:
00338     case PM_PAUSED_JOIN:
00339     case PM_PAUSED_GAME_SCRIPT:
00340     case PM_PAUSED_ACTIVE_CLIENTS: {
00341       bool changed = ((_pause_mode == PM_UNPAUSED) != (prev_mode == PM_UNPAUSED));
00342       bool paused = (_pause_mode != PM_UNPAUSED);
00343       if (!paused && !changed) return;
00344 
00345       StringID str;
00346       if (!changed) {
00347         int i = -1;
00348 
00349         if ((_pause_mode & PM_PAUSED_NORMAL) != PM_UNPAUSED)         SetDParam(++i, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_MANUAL);
00350         if ((_pause_mode & PM_PAUSED_JOIN) != PM_UNPAUSED)           SetDParam(++i, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_CONNECTING_CLIENTS);
00351         if ((_pause_mode & PM_PAUSED_GAME_SCRIPT) != PM_UNPAUSED)    SetDParam(++i, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_GAME_SCRIPT);
00352         if ((_pause_mode & PM_PAUSED_ACTIVE_CLIENTS) != PM_UNPAUSED) SetDParam(++i, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_NOT_ENOUGH_PLAYERS);
00353         str = STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_1 + i;
00354       } else {
00355         switch (changed_mode) {
00356           case PM_PAUSED_NORMAL:         SetDParam(0, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_MANUAL); break;
00357           case PM_PAUSED_JOIN:           SetDParam(0, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_CONNECTING_CLIENTS); break;
00358           case PM_PAUSED_GAME_SCRIPT:    SetDParam(0, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_GAME_SCRIPT); break;
00359           case PM_PAUSED_ACTIVE_CLIENTS: SetDParam(0, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_NOT_ENOUGH_PLAYERS); break;
00360           default: NOT_REACHED();
00361         }
00362         str = paused ? STR_NETWORK_SERVER_MESSAGE_GAME_PAUSED : STR_NETWORK_SERVER_MESSAGE_GAME_UNPAUSED;
00363       }
00364 
00365       char buffer[DRAW_STRING_BUFFER];
00366       GetString(buffer, str, lastof(buffer));
00367       NetworkTextMessage(NETWORK_ACTION_SERVER_MESSAGE, CC_DEFAULT, false, NULL, buffer);
00368       break;
00369     }
00370 
00371     default:
00372       return;
00373   }
00374 }
00375 
00376 
00385 static void CheckPauseHelper(bool pause, PauseMode pm)
00386 {
00387   if (pause == ((_pause_mode & pm) != PM_UNPAUSED)) return;
00388 
00389   DoCommandP(0, pm, pause ? 1 : 0, CMD_PAUSE);
00390 }
00391 
00397 static uint NetworkCountActiveClients()
00398 {
00399   const NetworkClientSocket *cs;
00400   uint count = 0;
00401 
00402   FOR_ALL_CLIENT_SOCKETS(cs) {
00403     if (cs->status != NetworkClientSocket::STATUS_ACTIVE) continue;
00404     if (!Company::IsValidID(cs->GetInfo()->client_playas)) continue;
00405     count++;
00406   }
00407 
00408   return count;
00409 }
00410 
00414 static void CheckMinActiveClients()
00415 {
00416   if ((_pause_mode & PM_PAUSED_ERROR) != PM_UNPAUSED ||
00417       !_network_dedicated ||
00418       (_settings_client.network.min_active_clients == 0 && (_pause_mode & PM_PAUSED_ACTIVE_CLIENTS) == PM_UNPAUSED)) {
00419     return;
00420   }
00421   CheckPauseHelper(NetworkCountActiveClients() < _settings_client.network.min_active_clients, PM_PAUSED_ACTIVE_CLIENTS);
00422 }
00423 
00428 static bool NetworkHasJoiningClient()
00429 {
00430   const NetworkClientSocket *cs;
00431   FOR_ALL_CLIENT_SOCKETS(cs) {
00432     if (cs->status >= NetworkClientSocket::STATUS_AUTHORIZED && cs->status < NetworkClientSocket::STATUS_ACTIVE) return true;
00433   }
00434 
00435   return false;
00436 }
00437 
00441 static void CheckPauseOnJoin()
00442 {
00443   if ((_pause_mode & PM_PAUSED_ERROR) != PM_UNPAUSED ||
00444       (!_settings_client.network.pause_on_join && (_pause_mode & PM_PAUSED_JOIN) == PM_UNPAUSED)) {
00445     return;
00446   }
00447   CheckPauseHelper(NetworkHasJoiningClient(), PM_PAUSED_JOIN);
00448 }
00449 
00458 void ParseConnectionString(const char **company, const char **port, char *connection_string)
00459 {
00460   bool ipv6 = (strchr(connection_string, ':') != strrchr(connection_string, ':'));
00461   char *p;
00462   for (p = connection_string; *p != '\0'; p++) {
00463     switch (*p) {
00464       case '[':
00465         ipv6 = true;
00466         break;
00467 
00468       case ']':
00469         ipv6 = false;
00470         break;
00471 
00472       case '#':
00473         *company = p + 1;
00474         *p = '\0';
00475         break;
00476 
00477       case ':':
00478         if (ipv6) break;
00479         *port = p + 1;
00480         *p = '\0';
00481         break;
00482     }
00483   }
00484 }
00485 
00491 /* static */ void ServerNetworkGameSocketHandler::AcceptConnection(SOCKET s, const NetworkAddress &address)
00492 {
00493   /* Register the login */
00494   _network_clients_connected++;
00495 
00496   SetWindowDirty(WC_CLIENT_LIST, 0);
00497   ServerNetworkGameSocketHandler *cs = new ServerNetworkGameSocketHandler(s);
00498   cs->client_address = address; // Save the IP of the client
00499 }
00500 
00505 static void InitializeNetworkPools(bool close_admins = true)
00506 {
00507   PoolBase::Clean(PT_NCLIENT | (close_admins ? PT_NADMIN : PT_NONE));
00508 }
00509 
00514 void NetworkClose(bool close_admins)
00515 {
00516   if (_network_server) {
00517     if (close_admins) {
00518       ServerNetworkAdminSocketHandler *as;
00519       FOR_ALL_ADMIN_SOCKETS(as) {
00520         as->CloseConnection(true);
00521       }
00522     }
00523 
00524     NetworkClientSocket *cs;
00525     FOR_ALL_CLIENT_SOCKETS(cs) {
00526       cs->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST);
00527     }
00528     ServerNetworkGameSocketHandler::CloseListeners();
00529     ServerNetworkAdminSocketHandler::CloseListeners();
00530   } else if (MyClient::my_client != NULL) {
00531     MyClient::SendQuit();
00532     MyClient::my_client->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST);
00533   }
00534 
00535   TCPConnecter::KillAll();
00536 
00537   _networking = false;
00538   _network_server = false;
00539 
00540   NetworkFreeLocalCommandQueue();
00541 
00542   free(_network_company_states);
00543   _network_company_states = NULL;
00544 
00545   InitializeNetworkPools(close_admins);
00546 }
00547 
00548 /* Inits the network (cleans sockets and stuff) */
00549 static void NetworkInitialize(bool close_admins = true)
00550 {
00551   InitializeNetworkPools(close_admins);
00552   NetworkUDPInitialize();
00553 
00554   _sync_frame = 0;
00555   _network_first_time = true;
00556 
00557   _network_reconnect = 0;
00558 }
00559 
00561 class TCPQueryConnecter : TCPConnecter {
00562 public:
00563   TCPQueryConnecter(const NetworkAddress &address) : TCPConnecter(address) {}
00564 
00565   virtual void OnFailure()
00566   {
00567     NetworkDisconnect();
00568   }
00569 
00570   virtual void OnConnect(SOCKET s)
00571   {
00572     _networking = true;
00573     new ClientNetworkGameSocketHandler(s);
00574     MyClient::SendCompanyInformationQuery();
00575   }
00576 };
00577 
00578 /* Query a server to fetch his game-info
00579  *  If game_info is true, only the gameinfo is fetched,
00580  *   else only the client_info is fetched */
00581 void NetworkTCPQueryServer(NetworkAddress address)
00582 {
00583   if (!_network_available) return;
00584 
00585   NetworkDisconnect();
00586   NetworkInitialize();
00587 
00588   new TCPQueryConnecter(address);
00589 }
00590 
00591 /* Validates an address entered as a string and adds the server to
00592  * the list. If you use this function, the games will be marked
00593  * as manually added. */
00594 void NetworkAddServer(const char *b)
00595 {
00596   if (*b != '\0') {
00597     const char *port = NULL;
00598     const char *company = NULL;
00599     char host[NETWORK_HOSTNAME_LENGTH];
00600     uint16 rport;
00601 
00602     strecpy(host, b, lastof(host));
00603 
00604     strecpy(_settings_client.network.connect_to_ip, b, lastof(_settings_client.network.connect_to_ip));
00605     rport = NETWORK_DEFAULT_PORT;
00606 
00607     ParseConnectionString(&company, &port, host);
00608     if (port != NULL) rport = atoi(port);
00609 
00610     NetworkUDPQueryServer(NetworkAddress(host, rport), true);
00611   }
00612 }
00613 
00619 void GetBindAddresses(NetworkAddressList *addresses, uint16 port)
00620 {
00621   for (char **iter = _network_bind_list.Begin(); iter != _network_bind_list.End(); iter++) {
00622     *addresses->Append() = NetworkAddress(*iter, port);
00623   }
00624 
00625   /* No address, so bind to everything. */
00626   if (addresses->Length() == 0) {
00627     *addresses->Append() = NetworkAddress("", port);
00628   }
00629 }
00630 
00631 /* Generates the list of manually added hosts from NetworkGameList and
00632  * dumps them into the array _network_host_list. This array is needed
00633  * by the function that generates the config file. */
00634 void NetworkRebuildHostList()
00635 {
00636   _network_host_list.Clear();
00637 
00638   for (NetworkGameList *item = _network_game_list; item != NULL; item = item->next) {
00639     if (item->manually) *_network_host_list.Append() = strdup(item->address.GetAddressAsString(false));
00640   }
00641 }
00642 
00644 class TCPClientConnecter : TCPConnecter {
00645 public:
00646   TCPClientConnecter(const NetworkAddress &address) : TCPConnecter(address) {}
00647 
00648   virtual void OnFailure()
00649   {
00650     NetworkError(STR_NETWORK_ERROR_NOCONNECTION);
00651   }
00652 
00653   virtual void OnConnect(SOCKET s)
00654   {
00655     _networking = true;
00656     new ClientNetworkGameSocketHandler(s);
00657     IConsoleCmdExec("exec scripts/on_client.scr 0");
00658     NetworkClient_Connected();
00659   }
00660 };
00661 
00662 
00663 /* Used by clients, to connect to a server */
00664 void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const char *join_server_password, const char *join_company_password)
00665 {
00666   if (!_network_available) return;
00667 
00668   if (address.GetPort() == 0) return;
00669 
00670   strecpy(_settings_client.network.last_host, address.GetHostname(), lastof(_settings_client.network.last_host));
00671   _settings_client.network.last_port = address.GetPort();
00672   _network_join_as = join_as;
00673   _network_join_server_password = join_server_password;
00674   _network_join_company_password = join_company_password;
00675 
00676   NetworkDisconnect();
00677   NetworkInitialize();
00678 
00679   _network_join_status = NETWORK_JOIN_STATUS_CONNECTING;
00680   ShowJoinStatusWindow();
00681 
00682   new TCPClientConnecter(address);
00683 }
00684 
00685 static void NetworkInitGameInfo()
00686 {
00687   if (StrEmpty(_settings_client.network.server_name)) {
00688     snprintf(_settings_client.network.server_name, sizeof(_settings_client.network.server_name), "Unnamed Server");
00689   }
00690 
00691   /* The server is a client too */
00692   _network_game_info.clients_on = _network_dedicated ? 0 : 1;
00693 
00694   /* There should be always space for the server. */
00695   assert(NetworkClientInfo::CanAllocateItem());
00696   NetworkClientInfo *ci = new NetworkClientInfo(CLIENT_ID_SERVER);
00697   ci->client_playas = _network_dedicated ? COMPANY_SPECTATOR : _local_company;
00698 
00699   strecpy(ci->client_name, _settings_client.network.client_name, lastof(ci->client_name));
00700 }
00701 
00702 bool NetworkServerStart()
00703 {
00704   if (!_network_available) return false;
00705 
00706   /* Call the pre-scripts */
00707   IConsoleCmdExec("exec scripts/pre_server.scr 0");
00708   if (_network_dedicated) IConsoleCmdExec("exec scripts/pre_dedicated.scr 0");
00709 
00710   NetworkDisconnect(false, false);
00711   NetworkInitialize(false);
00712   if (!ServerNetworkGameSocketHandler::Listen(_settings_client.network.server_port)) return false;
00713 
00714   /* Only listen for admins when the password isn't empty. */
00715   if (!StrEmpty(_settings_client.network.admin_password) && !ServerNetworkAdminSocketHandler::Listen(_settings_client.network.server_admin_port)) return false;
00716 
00717   /* Try to start UDP-server */
00718   _network_udp_server = _udp_server_socket->Listen();
00719 
00720   _network_company_states = CallocT<NetworkCompanyState>(MAX_COMPANIES);
00721   _network_server = true;
00722   _networking = true;
00723   _frame_counter = 0;
00724   _frame_counter_server = 0;
00725   _frame_counter_max = 0;
00726   _last_sync_frame = 0;
00727   _network_own_client_id = CLIENT_ID_SERVER;
00728 
00729   _network_clients_connected = 0;
00730   _network_company_passworded = 0;
00731 
00732   NetworkInitGameInfo();
00733 
00734   /* execute server initialization script */
00735   IConsoleCmdExec("exec scripts/on_server.scr 0");
00736   /* if the server is dedicated ... add some other script */
00737   if (_network_dedicated) IConsoleCmdExec("exec scripts/on_dedicated.scr 0");
00738 
00739   /* Try to register us to the master server */
00740   _network_last_advertise_frame = 0;
00741   _network_need_advertise = true;
00742   NetworkUDPAdvertise();
00743 
00744   /* welcome possibly still connected admins - this can only happen on a dedicated server. */
00745   if (_network_dedicated) ServerNetworkAdminSocketHandler::WelcomeAll();
00746 
00747   return true;
00748 }
00749 
00750 /* The server is rebooting...
00751  * The only difference with NetworkDisconnect, is the packets that is sent */
00752 void NetworkReboot()
00753 {
00754   if (_network_server) {
00755     NetworkClientSocket *cs;
00756     FOR_ALL_CLIENT_SOCKETS(cs) {
00757       cs->SendNewGame();
00758       cs->SendPackets();
00759     }
00760 
00761     ServerNetworkAdminSocketHandler *as;
00762     FOR_ALL_ACTIVE_ADMIN_SOCKETS(as) {
00763       as->SendNewGame();
00764       as->SendPackets();
00765     }
00766   }
00767 
00768   /* For non-dedicated servers we have to kick the admins as we are not
00769    * certain that we will end up in a new network game. */
00770   NetworkClose(!_network_dedicated);
00771 }
00772 
00778 void NetworkDisconnect(bool blocking, bool close_admins)
00779 {
00780   if (_network_server) {
00781     NetworkClientSocket *cs;
00782     FOR_ALL_CLIENT_SOCKETS(cs) {
00783       cs->SendShutdown();
00784       cs->SendPackets();
00785     }
00786 
00787     if (close_admins) {
00788       ServerNetworkAdminSocketHandler *as;
00789       FOR_ALL_ACTIVE_ADMIN_SOCKETS(as) {
00790         as->SendShutdown();
00791         as->SendPackets();
00792       }
00793     }
00794   }
00795 
00796   if (_settings_client.network.server_advertise) NetworkUDPRemoveAdvertise(blocking);
00797 
00798   DeleteWindowById(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
00799 
00800   NetworkClose(close_admins);
00801 
00802   /* Reinitialize the UDP stack, i.e. close all existing connections. */
00803   NetworkUDPInitialize();
00804 }
00805 
00810 static bool NetworkReceive()
00811 {
00812   if (_network_server) {
00813     ServerNetworkAdminSocketHandler::Receive();
00814     return ServerNetworkGameSocketHandler::Receive();
00815   } else {
00816     return ClientNetworkGameSocketHandler::Receive();
00817   }
00818 }
00819 
00820 /* This sends all buffered commands (if possible) */
00821 static void NetworkSend()
00822 {
00823   if (_network_server) {
00824     ServerNetworkAdminSocketHandler::Send();
00825     ServerNetworkGameSocketHandler::Send();
00826   } else {
00827     ClientNetworkGameSocketHandler::Send();
00828   }
00829 }
00830 
00831 /* We have to do some UDP checking */
00832 void NetworkUDPGameLoop()
00833 {
00834   _network_content_client.SendReceive();
00835   TCPConnecter::CheckCallbacks();
00836   NetworkHTTPSocketHandler::HTTPReceive();
00837 
00838   if (_network_udp_server) {
00839     _udp_server_socket->ReceivePackets();
00840     _udp_master_socket->ReceivePackets();
00841   } else {
00842     _udp_client_socket->ReceivePackets();
00843     if (_network_udp_broadcast > 0) _network_udp_broadcast--;
00844   }
00845 }
00846 
00847 /* The main loop called from ttd.c
00848  *  Here we also have to do StateGameLoop if needed! */
00849 void NetworkGameLoop()
00850 {
00851   if (!_networking) return;
00852 
00853   if (!NetworkReceive()) return;
00854 
00855   if (_network_server) {
00856     /* Log the sync state to check for in-syncedness of replays. */
00857     if (_date_fract == 0) {
00858       /* We don't want to log multiple times if paused. */
00859       static Date last_log;
00860       if (last_log != _date) {
00861         DEBUG(desync, 1, "sync: %08x; %02x; %08x; %08x", _date, _date_fract, _random.state[0], _random.state[1]);
00862         last_log = _date;
00863       }
00864     }
00865 
00866 #ifdef DEBUG_DUMP_COMMANDS
00867     /* Loading of the debug commands from -ddesync>=1 */
00868     static FILE *f = FioFOpenFile("commands.log", "rb", SAVE_DIR);
00869     static Date next_date = 0;
00870     static uint32 next_date_fract;
00871     static CommandPacket *cp = NULL;
00872     static bool check_sync_state = false;
00873     static uint32 sync_state[2];
00874     if (f == NULL && next_date == 0) {
00875       DEBUG(net, 0, "Cannot open commands.log");
00876       next_date = 1;
00877     }
00878 
00879     while (f != NULL && !feof(f)) {
00880       if (_date == next_date && _date_fract == next_date_fract) {
00881         if (cp != NULL) {
00882           NetworkSendCommand(cp->tile, cp->p1, cp->p2, cp->cmd & ~CMD_FLAGS_MASK, NULL, cp->text, cp->company);
00883           DEBUG(net, 0, "injecting: %08x; %02x; %02x; %06x; %08x; %08x; %08x; \"%s\" (%s)", _date, _date_fract, (int)_current_company, cp->tile, cp->p1, cp->p2, cp->cmd, cp->text, GetCommandName(cp->cmd));
00884           free(cp);
00885           cp = NULL;
00886         }
00887         if (check_sync_state) {
00888           if (sync_state[0] == _random.state[0] && sync_state[1] == _random.state[1]) {
00889             DEBUG(net, 0, "sync check: %08x; %02x; match", _date, _date_fract);
00890           } else {
00891             DEBUG(net, 0, "sync check: %08x; %02x; mismatch expected {%08x, %08x}, got {%08x, %08x}",
00892                   _date, _date_fract, sync_state[0], sync_state[1], _random.state[0], _random.state[1]);
00893             NOT_REACHED();
00894           }
00895           check_sync_state = false;
00896         }
00897       }
00898 
00899       if (cp != NULL || check_sync_state) break;
00900 
00901       char buff[4096];
00902       if (fgets(buff, lengthof(buff), f) == NULL) break;
00903 
00904       char *p = buff;
00905       /* Ignore the "[date time] " part of the message */
00906       if (*p == '[') {
00907         p = strchr(p, ']');
00908         if (p == NULL) break;
00909         p += 2;
00910       }
00911 
00912       if (strncmp(p, "cmd: ", 5) == 0) {
00913         cp = CallocT<CommandPacket>(1);
00914         int company;
00915         int ret = sscanf(p + 5, "%x; %x; %x; %x; %x; %x; %x; \"%[^\"]\"", &next_date, &next_date_fract, &company, &cp->tile, &cp->p1, &cp->p2, &cp->cmd, cp->text);
00916         /* There are 8 pieces of data to read, however the last is a
00917          * string that might or might not exist. Ignore it if that
00918          * string misses because in 99% of the time it's not used. */
00919         assert(ret == 8 || ret == 7);
00920         cp->company = (CompanyID)company;
00921       } else if (strncmp(p, "join: ", 6) == 0) {
00922         /* Manually insert a pause when joining; this way the client can join at the exact right time. */
00923         int ret = sscanf(p + 6, "%x; %x", &next_date, &next_date_fract);
00924         assert(ret == 2);
00925         DEBUG(net, 0, "injecting pause for join at %08x:%02x; please join when paused", next_date, next_date_fract);
00926         cp = CallocT<CommandPacket>(1);
00927         cp->company = COMPANY_SPECTATOR;
00928         cp->cmd = CMD_PAUSE;
00929         cp->p1 = PM_PAUSED_NORMAL;
00930         cp->p2 = 1;
00931         _ddc_fastforward = false;
00932       } else if (strncmp(p, "sync: ", 6) == 0) {
00933         int ret = sscanf(p + 6, "%x; %x; %x; %x", &next_date, &next_date_fract, &sync_state[0], &sync_state[1]);
00934         assert(ret == 4);
00935         check_sync_state = true;
00936       } else if (strncmp(p, "msg: ", 5) == 0 || strncmp(p, "client: ", 8) == 0 ||
00937             strncmp(p, "load: ", 6) == 0 || strncmp(p, "save: ", 6) == 0) {
00938         /* A message that is not very important to the log playback, but part of the log. */
00939       } else {
00940         /* Can't parse a line; what's wrong here? */
00941         DEBUG(net, 0, "trying to parse: %s", p);
00942         NOT_REACHED();
00943       }
00944     }
00945     if (f != NULL && feof(f)) {
00946       DEBUG(net, 0, "End of commands.log");
00947       fclose(f);
00948       f = NULL;
00949     }
00950 #endif /* DEBUG_DUMP_COMMANDS */
00951     if (_frame_counter >= _frame_counter_max) {
00952       /* Only check for active clients just before we're going to send out
00953        * the commands so we don't send multiple pause/unpause commands when
00954        * the frame_freq is more than 1 tick. Same with distributing commands. */
00955       CheckPauseOnJoin();
00956       CheckMinActiveClients();
00957       NetworkDistributeCommands();
00958     }
00959 
00960     bool send_frame = false;
00961 
00962     /* We first increase the _frame_counter */
00963     _frame_counter++;
00964     /* Update max-frame-counter */
00965     if (_frame_counter > _frame_counter_max) {
00966       _frame_counter_max = _frame_counter + _settings_client.network.frame_freq;
00967       send_frame = true;
00968     }
00969 
00970     NetworkExecuteLocalCommandQueue();
00971 
00972     /* Then we make the frame */
00973     StateGameLoop();
00974 
00975     _sync_seed_1 = _random.state[0];
00976 #ifdef NETWORK_SEND_DOUBLE_SEED
00977     _sync_seed_2 = _random.state[1];
00978 #endif
00979 
00980     NetworkServer_Tick(send_frame);
00981   } else {
00982     /* Client */
00983 
00984     /* Make sure we are at the frame were the server is (quick-frames) */
00985     if (_frame_counter_server > _frame_counter) {
00986       /* Run a number of frames; when things go bad, get out. */
00987       while (_frame_counter_server > _frame_counter) {
00988         if (!ClientNetworkGameSocketHandler::GameLoop()) return;
00989       }
00990     } else {
00991       /* Else, keep on going till _frame_counter_max */
00992       if (_frame_counter_max > _frame_counter) {
00993         /* Run one frame; if things went bad, get out. */
00994         if (!ClientNetworkGameSocketHandler::GameLoop()) return;
00995       }
00996     }
00997   }
00998 
00999   NetworkSend();
01000 }
01001 
01002 static void NetworkGenerateServerId()
01003 {
01004   Md5 checksum;
01005   uint8 digest[16];
01006   char hex_output[16 * 2 + 1];
01007   char coding_string[NETWORK_NAME_LENGTH];
01008   int di;
01009 
01010   snprintf(coding_string, sizeof(coding_string), "%d%s", (uint)Random(), "OpenTTD Server ID");
01011 
01012   /* Generate the MD5 hash */
01013   checksum.Append((const uint8*)coding_string, strlen(coding_string));
01014   checksum.Finish(digest);
01015 
01016   for (di = 0; di < 16; ++di) {
01017     sprintf(hex_output + di * 2, "%02x", digest[di]);
01018   }
01019 
01020   /* _settings_client.network.network_id is our id */
01021   snprintf(_settings_client.network.network_id, sizeof(_settings_client.network.network_id), "%s", hex_output);
01022 }
01023 
01024 void NetworkStartDebugLog(NetworkAddress address)
01025 {
01026   extern SOCKET _debug_socket;  // Comes from debug.c
01027 
01028   DEBUG(net, 0, "Redirecting DEBUG() to %s:%d", address.GetHostname(), address.GetPort());
01029 
01030   SOCKET s = address.Connect();
01031   if (s == INVALID_SOCKET) {
01032     DEBUG(net, 0, "Failed to open socket for redirection DEBUG()");
01033     return;
01034   }
01035 
01036   _debug_socket = s;
01037 
01038   DEBUG(net, 0, "DEBUG() is now redirected");
01039 }
01040 
01042 void NetworkStartUp()
01043 {
01044   DEBUG(net, 3, "[core] starting network...");
01045 
01046   /* Network is available */
01047   _network_available = NetworkCoreInitialize();
01048   _network_dedicated = false;
01049   _network_last_advertise_frame = 0;
01050   _network_need_advertise = true;
01051   _network_advertise_retries = 0;
01052 
01053   /* Generate an server id when there is none yet */
01054   if (StrEmpty(_settings_client.network.network_id)) NetworkGenerateServerId();
01055 
01056   memset(&_network_game_info, 0, sizeof(_network_game_info));
01057 
01058   NetworkInitialize();
01059   DEBUG(net, 3, "[core] network online, multiplayer available");
01060   NetworkFindBroadcastIPs(&_broadcast_list);
01061 }
01062 
01064 void NetworkShutDown()
01065 {
01066   NetworkDisconnect(true);
01067   NetworkUDPClose();
01068 
01069   DEBUG(net, 3, "[core] shutting down network");
01070 
01071   _network_available = false;
01072 
01073   NetworkCoreShutdown();
01074 }
01075 
01080 bool IsNetworkCompatibleVersion(const char *other)
01081 {
01082   return strncmp(_openttd_revision, other, NETWORK_REVISION_LENGTH - 1) == 0;
01083 }
01084 
01085 #endif /* ENABLE_NETWORK */