network.cpp

Go to the documentation of this file.
00001 /* $Id: network.cpp 19624 2010-04-13 21:40:24Z rubidium $ */
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_client.h"
00020 #include "network_server.h"
00021 #include "network_content.h"
00022 #include "network_udp.h"
00023 #include "network_gamelist.h"
00024 #include "network_base.h"
00025 #include "core/udp.h"
00026 #include "core/host.h"
00027 #include "network_gui.h"
00028 #include "../console_func.h"
00029 #include "../3rdparty/md5/md5.h"
00030 #include "../core/random_func.hpp"
00031 #include "../window_func.h"
00032 #include "../company_func.h"
00033 #include "../company_base.h"
00034 #include "../landscape_type.h"
00035 #include "../rev.h"
00036 #include "../core/pool_func.hpp"
00037 #include "../gfx_func.h"
00038 #include "table/strings.h"
00039 
00040 #ifdef DEBUG_DUMP_COMMANDS
00041 #include "../fileio_func.h"
00043 bool _ddc_fastforward = true;
00044 #endif /* DEBUG_DUMP_COMMANDS */
00045 
00046 DECLARE_POSTFIX_INCREMENT(ClientID);
00047 
00048 assert_compile(NetworkClientInfoPool::MAX_SIZE == NetworkClientSocketPool::MAX_SIZE);
00049 
00050 NetworkClientInfoPool _networkclientinfo_pool("NetworkClientInfo");
00051 INSTANTIATE_POOL_METHODS(NetworkClientInfo)
00052 
00053 bool _networking;         
00054 bool _network_server;     
00055 bool _network_available;  
00056 bool _network_dedicated;  
00057 bool _is_network_server;  
00058 NetworkServerGameInfo _network_game_info;
00059 NetworkCompanyState *_network_company_states = NULL;
00060 ClientID _network_own_client_id;
00061 ClientID _redirect_console_to_client;
00062 bool _network_need_advertise;
00063 uint32 _network_last_advertise_frame;
00064 uint8 _network_reconnect;
00065 StringList _network_bind_list;
00066 StringList _network_host_list;
00067 StringList _network_ban_list;
00068 uint32 _frame_counter_server; // The frame_counter of the server, if in network-mode
00069 uint32 _frame_counter_max; // To where we may go with our clients
00070 uint32 _frame_counter;
00071 uint32 _last_sync_frame; // Used in the server to store the last time a sync packet was sent to clients.
00072 NetworkAddressList _broadcast_list;
00073 uint32 _sync_seed_1;
00074 #ifdef NETWORK_SEND_DOUBLE_SEED
00075 uint32 _sync_seed_2;
00076 #endif
00077 uint32 _sync_frame;
00078 bool _network_first_time;
00079 bool _network_udp_server;
00080 uint16 _network_udp_broadcast;
00081 uint8 _network_advertise_retries;
00082 CompanyMask _network_company_passworded; 
00083 
00084 /* Check whether NETWORK_NUM_LANDSCAPES is still in sync with NUM_LANDSCAPE */
00085 assert_compile((int)NETWORK_NUM_LANDSCAPES == (int)NUM_LANDSCAPE);
00086 assert_compile((int)NETWORK_COMPANY_NAME_LENGTH == MAX_LENGTH_COMPANY_NAME_BYTES);
00087 
00088 extern NetworkUDPSocketHandler *_udp_client_socket; 
00089 extern NetworkUDPSocketHandler *_udp_server_socket; 
00090 extern NetworkUDPSocketHandler *_udp_master_socket; 
00091 
00092 /* The listen socket for the server */
00093 static SocketList _listensockets;
00094 
00095 /* The amount of clients connected */
00096 static byte _network_clients_connected = 0;
00097 /* The identifier counter for new clients (is never decreased) */
00098 static ClientID _network_client_id = CLIENT_ID_FIRST;
00099 
00100 /* Some externs / forwards */
00101 extern void StateGameLoop();
00102 
00106 NetworkClientInfo::~NetworkClientInfo()
00107 {
00108   /* Delete the chat window, if you were chatting with this client. */
00109   InvalidateWindowData(WC_SEND_NETWORK_MSG, DESTTYPE_CLIENT, this->client_id);
00110 }
00111 
00117 NetworkClientInfo *NetworkFindClientInfoFromIndex(ClientIndex index)
00118 {
00119   return NetworkClientInfo::GetIfValid(index);
00120 }
00121 
00127 NetworkClientInfo *NetworkFindClientInfoFromClientID(ClientID client_id)
00128 {
00129   NetworkClientInfo *ci;
00130 
00131   FOR_ALL_CLIENT_INFOS(ci) {
00132     if (ci->client_id == client_id) return ci;
00133   }
00134 
00135   return NULL;
00136 }
00137 
00143 NetworkClientInfo *NetworkFindClientInfoFromIP(const char *ip)
00144 {
00145   NetworkClientInfo *ci;
00146   NetworkAddress address(ip);
00147 
00148   if (address.GetAddressLength() == 0) return NULL;
00149 
00150   FOR_ALL_CLIENT_INFOS(ci) {
00151     if (ci->client_address == address) return ci;
00152   }
00153 
00154   return NULL;
00155 }
00156 
00162 NetworkClientSocket *NetworkFindClientStateFromClientID(ClientID client_id)
00163 {
00164   NetworkClientSocket *cs;
00165 
00166   FOR_ALL_CLIENT_SOCKETS(cs) {
00167     if (cs->client_id == client_id) return cs;
00168   }
00169 
00170   return NULL;
00171 }
00172 
00173 /* NetworkGetClientName is a server-safe function to get the name of the client
00174  *  if the user did not send it yet, Client #<no> is used. */
00175 void NetworkGetClientName(char *client_name, size_t size, const NetworkClientSocket *cs)
00176 {
00177   const NetworkClientInfo *ci = cs->GetInfo();
00178 
00179   if (StrEmpty(ci->client_name)) {
00180     snprintf(client_name, size, "Client #%4d", cs->client_id);
00181   } else {
00182     ttd_strlcpy(client_name, ci->client_name, size);
00183   }
00184 }
00185 
00186 byte NetworkSpectatorCount()
00187 {
00188   const NetworkClientInfo *ci;
00189   byte count = 0;
00190 
00191   FOR_ALL_CLIENT_INFOS(ci) {
00192     if (ci->client_playas == COMPANY_SPECTATOR) count++;
00193   }
00194 
00195   /* Don't count a dedicated server as spectator */
00196   if (_network_dedicated) count--;
00197 
00198   return count;
00199 }
00200 
00206 bool NetworkCompanyIsPassworded(CompanyID company_id)
00207 {
00208   return HasBit(_network_company_passworded, company_id);
00209 }
00210 
00211 /* This puts a text-message to the console, or in the future, the chat-box,
00212  *  (to keep it all a bit more general)
00213  * If 'self_send' is true, this is the client who is sending the message */
00214 void NetworkTextMessage(NetworkAction action, ConsoleColour colour, bool self_send, const char *name, const char *str, int64 data)
00215 {
00216   const int duration = 10; // Game days the messages stay visible
00217 
00218   StringID strid;
00219   switch (action) {
00220     case NETWORK_ACTION_SERVER_MESSAGE:
00221       /* Ignore invalid messages */
00222       strid = STR_NETWORK_SERVER_MESSAGE;
00223       colour = CC_DEFAULT;
00224       break;
00225     case NETWORK_ACTION_COMPANY_SPECTATOR:
00226       colour = CC_DEFAULT;
00227       strid = STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE;
00228       break;
00229     case NETWORK_ACTION_COMPANY_JOIN:
00230       colour = CC_DEFAULT;
00231       strid = STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN;
00232       break;
00233     case NETWORK_ACTION_COMPANY_NEW:
00234       colour = CC_DEFAULT;
00235       strid = STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW;
00236       break;
00237     case NETWORK_ACTION_JOIN:
00238       /* Show the Client ID for the server but not for the client. */
00239       strid = _network_server ? STR_NETWORK_MESSAGE_CLIENT_JOINED_ID :  STR_NETWORK_MESSAGE_CLIENT_JOINED;
00240       break;
00241     case NETWORK_ACTION_LEAVE:          strid = STR_NETWORK_MESSAGE_CLIENT_LEFT; break;
00242     case NETWORK_ACTION_NAME_CHANGE:    strid = STR_NETWORK_MESSAGE_NAME_CHANGE; break;
00243     case NETWORK_ACTION_GIVE_MONEY:     strid = self_send ? STR_NETWORK_MESSAGE_GAVE_MONEY_AWAY : STR_NETWORK_MESSAGE_GIVE_MONEY;   break;
00244     case NETWORK_ACTION_CHAT_COMPANY:   strid = self_send ? STR_NETWORK_CHAT_TO_COMPANY : STR_NETWORK_CHAT_COMPANY; break;
00245     case NETWORK_ACTION_CHAT_CLIENT:    strid = self_send ? STR_NETWORK_CHAT_TO_CLIENT  : STR_NETWORK_CHAT_CLIENT;  break;
00246     default:                            strid = STR_NETWORK_CHAT_ALL; break;
00247   }
00248 
00249   char message[1024];
00250   SetDParamStr(0, name);
00251   SetDParamStr(1, str);
00252   SetDParam(2, data);
00253   GetString(message, strid, lastof(message));
00254 
00255   DEBUG(desync, 1, "msg: %08x; %02x; %s", _date, _date_fract, message);
00256   IConsolePrintF(colour, "%s", message);
00257   NetworkAddChatMessage((TextColour)colour, duration, "%s", message);
00258 }
00259 
00260 /* Calculate the frame-lag of a client */
00261 uint NetworkCalculateLag(const NetworkClientSocket *cs)
00262 {
00263   int lag = cs->last_frame_server - cs->last_frame;
00264   /* This client has missed his ACK packet after 1 DAY_TICKS..
00265    *  so we increase his lag for every frame that passes!
00266    * The packet can be out by a max of _net_frame_freq */
00267   if (cs->last_frame_server + DAY_TICKS + _settings_client.network.frame_freq < _frame_counter)
00268     lag += _frame_counter - (cs->last_frame_server + DAY_TICKS + _settings_client.network.frame_freq);
00269 
00270   return lag;
00271 }
00272 
00273 
00274 /* There was a non-recoverable error, drop back to the main menu with a nice
00275  *  error */
00276 static void NetworkError(StringID error_string)
00277 {
00278   _switch_mode = SM_MENU;
00279   extern StringID _switch_mode_errorstr;
00280   _switch_mode_errorstr = error_string;
00281 }
00282 
00283 static void ServerStartError(const char *error)
00284 {
00285   DEBUG(net, 0, "[server] could not start network: %s",error);
00286   NetworkError(STR_NETWORK_ERROR_SERVER_START);
00287 }
00288 
00289 static void NetworkClientError(NetworkRecvStatus res, NetworkClientSocket *cs)
00290 {
00291   /* First, send a CLIENT_ERROR to the server, so he knows we are
00292    *  disconnection (and why!) */
00293   NetworkErrorCode errorno;
00294 
00295   /* We just want to close the connection.. */
00296   if (res == NETWORK_RECV_STATUS_CLOSE_QUERY) {
00297     cs->NetworkSocketHandler::CloseConnection();
00298     NetworkCloseClient(cs, res);
00299     _networking = false;
00300 
00301     DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
00302     return;
00303   }
00304 
00305   switch (res) {
00306     case NETWORK_RECV_STATUS_DESYNC:          errorno = NETWORK_ERROR_DESYNC; break;
00307     case NETWORK_RECV_STATUS_SAVEGAME:        errorno = NETWORK_ERROR_SAVEGAME_FAILED; break;
00308     case NETWORK_RECV_STATUS_NEWGRF_MISMATCH: errorno = NETWORK_ERROR_NEWGRF_MISMATCH; break;
00309     default:                                  errorno = NETWORK_ERROR_GENERAL; break;
00310   }
00311 
00312   /* This means we fucked up and the server closed the connection */
00313   if (res != NETWORK_RECV_STATUS_SERVER_ERROR && res != NETWORK_RECV_STATUS_SERVER_FULL &&
00314       res != NETWORK_RECV_STATUS_SERVER_BANNED) {
00315     SEND_COMMAND(PACKET_CLIENT_ERROR)(errorno);
00316   }
00317 
00318   _switch_mode = SM_MENU;
00319   NetworkCloseClient(cs, res);
00320   _networking = false;
00321 }
00322 
00328 StringID GetNetworkErrorMsg(NetworkErrorCode err)
00329 {
00330   /* List of possible network errors, used by
00331    * PACKET_SERVER_ERROR and PACKET_CLIENT_ERROR */
00332   static const StringID network_error_strings[] = {
00333     STR_NETWORK_ERROR_CLIENT_GENERAL,
00334     STR_NETWORK_ERROR_CLIENT_DESYNC,
00335     STR_NETWORK_ERROR_CLIENT_SAVEGAME,
00336     STR_NETWORK_ERROR_CLIENT_CONNECTION_LOST,
00337     STR_NETWORK_ERROR_CLIENT_PROTOCOL_ERROR,
00338     STR_NETWORK_ERROR_CLIENT_NEWGRF_MISMATCH,
00339     STR_NETWORK_ERROR_CLIENT_NOT_AUTHORIZED,
00340     STR_NETWORK_ERROR_CLIENT_NOT_EXPECTED,
00341     STR_NETWORK_ERROR_CLIENT_WRONG_REVISION,
00342     STR_NETWORK_ERROR_CLIENT_NAME_IN_USE,
00343     STR_NETWORK_ERROR_CLIENT_WRONG_PASSWORD,
00344     STR_NETWORK_ERROR_CLIENT_COMPANY_MISMATCH,
00345     STR_NETWORK_ERROR_CLIENT_KICKED,
00346     STR_NETWORK_ERROR_CLIENT_CHEATER,
00347     STR_NETWORK_ERROR_CLIENT_SERVER_FULL
00348   };
00349 
00350   if (err >= (ptrdiff_t)lengthof(network_error_strings)) err = NETWORK_ERROR_GENERAL;
00351 
00352   return network_error_strings[err];
00353 }
00354 
00360 void NetworkHandlePauseChange(PauseMode prev_mode, PauseMode changed_mode)
00361 {
00362   if (!_networking) return;
00363 
00364   switch (changed_mode) {
00365     case PM_PAUSED_NORMAL:
00366     case PM_PAUSED_JOIN:
00367     case PM_PAUSED_ACTIVE_CLIENTS: {
00368       bool changed = ((_pause_mode == PM_UNPAUSED) != (prev_mode == PM_UNPAUSED));
00369       bool paused = (_pause_mode != PM_UNPAUSED);
00370       if (!paused && !changed) return;
00371 
00372       StringID str;
00373       if (!changed) {
00374         int i = -1;
00375         if ((_pause_mode & PM_PAUSED_NORMAL) != PM_UNPAUSED)         SetDParam(++i, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_MANUAL);
00376         if ((_pause_mode & PM_PAUSED_JOIN) != PM_UNPAUSED)           SetDParam(++i, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_CONNECTING_CLIENTS);
00377         if ((_pause_mode & PM_PAUSED_ACTIVE_CLIENTS) != PM_UNPAUSED) SetDParam(++i, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_NOT_ENOUGH_PLAYERS);
00378         str = STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_1 + i;
00379       } else {
00380         switch (changed_mode) {
00381           case PM_PAUSED_NORMAL:         SetDParam(0, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_MANUAL); break;
00382           case PM_PAUSED_JOIN:           SetDParam(0, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_CONNECTING_CLIENTS); break;
00383           case PM_PAUSED_ACTIVE_CLIENTS: SetDParam(0, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_NOT_ENOUGH_PLAYERS); break;
00384           default: NOT_REACHED();
00385         }
00386         str = paused ? STR_NETWORK_SERVER_MESSAGE_GAME_PAUSED : STR_NETWORK_SERVER_MESSAGE_GAME_UNPAUSED;
00387       }
00388 
00389       char buffer[DRAW_STRING_BUFFER];
00390       GetString(buffer, str, lastof(buffer));
00391       NetworkTextMessage(NETWORK_ACTION_SERVER_MESSAGE, CC_DEFAULT, false, NULL, buffer);
00392     } break;
00393 
00394     default:
00395       return;
00396   }
00397 }
00398 
00399 
00408 static void CheckPauseHelper(bool pause, PauseMode pm)
00409 {
00410   if (pause == ((_pause_mode & pm) != PM_UNPAUSED)) return;
00411 
00412   DoCommandP(0, pm, pause ? 1 : 0, CMD_PAUSE);
00413 }
00414 
00420 static uint NetworkCountActiveClients()
00421 {
00422   const NetworkClientSocket *cs;
00423   uint count = 0;
00424 
00425   FOR_ALL_CLIENT_SOCKETS(cs) {
00426     if (cs->status != STATUS_ACTIVE) continue;
00427     if (!Company::IsValidID(cs->GetInfo()->client_playas)) continue;
00428     count++;
00429   }
00430 
00431   return count;
00432 }
00433 
00437 static void CheckMinActiveClients()
00438 {
00439   if ((_pause_mode & PM_PAUSED_ERROR) != PM_UNPAUSED ||
00440       !_network_dedicated ||
00441       (_settings_client.network.min_active_clients == 0 && (_pause_mode & PM_PAUSED_ACTIVE_CLIENTS) == PM_UNPAUSED)) {
00442     return;
00443   }
00444   CheckPauseHelper(NetworkCountActiveClients() < _settings_client.network.min_active_clients, PM_PAUSED_ACTIVE_CLIENTS);
00445 }
00446 
00451 static bool NetworkHasJoiningClient()
00452 {
00453   const NetworkClientSocket *cs;
00454   FOR_ALL_CLIENT_SOCKETS(cs) {
00455     if (cs->status >= STATUS_AUTHORIZED && cs->status < STATUS_ACTIVE) return true;
00456   }
00457 
00458   return false;
00459 }
00460 
00464 static void CheckPauseOnJoin()
00465 {
00466   if ((_pause_mode & PM_PAUSED_ERROR) != PM_UNPAUSED ||
00467       (!_settings_client.network.pause_on_join && (_pause_mode & PM_PAUSED_JOIN) == PM_UNPAUSED)) {
00468     return;
00469   }
00470   CheckPauseHelper(NetworkHasJoiningClient(), PM_PAUSED_JOIN);
00471 }
00472 
00479 void ParseConnectionString(const char **company, const char **port, char *connection_string)
00480 {
00481   bool ipv6 = (strchr(connection_string, ':') != strrchr(connection_string, ':'));
00482   char *p;
00483   for (p = connection_string; *p != '\0'; p++) {
00484     switch (*p) {
00485       case '[':
00486         ipv6 = true;
00487         break;
00488 
00489       case ']':
00490         ipv6 = false;
00491         break;
00492 
00493       case '#':
00494         *company = p + 1;
00495         *p = '\0';
00496         break;
00497 
00498       case ':':
00499         if (ipv6) break;
00500         *port = p + 1;
00501         *p = '\0';
00502         break;
00503     }
00504   }
00505 }
00506 
00507 /* Creates a new client from a socket
00508  *   Used both by the server and the client */
00509 static NetworkClientSocket *NetworkAllocClient(SOCKET s)
00510 {
00511   if (_network_server) {
00512     /* Can we handle a new client? */
00513     if (_network_clients_connected >= MAX_CLIENTS) return NULL;
00514     if (_network_game_info.clients_on >= _settings_client.network.max_clients) return NULL;
00515 
00516     /* Register the login */
00517     _network_clients_connected++;
00518   }
00519 
00520   NetworkClientSocket *cs = new NetworkClientSocket(INVALID_CLIENT_ID);
00521   cs->sock = s;
00522   cs->last_frame = _frame_counter;
00523   cs->last_frame_server = _frame_counter;
00524 
00525   if (_network_server) {
00526     cs->client_id = _network_client_id++;
00527     NetworkClientInfo *ci = new NetworkClientInfo(cs->client_id);
00528     cs->SetInfo(ci);
00529     ci->client_playas = COMPANY_INACTIVE_CLIENT;
00530     ci->join_date = _date;
00531 
00532     SetWindowDirty(WC_CLIENT_LIST, 0);
00533   }
00534 
00535   return cs;
00536 }
00537 
00538 /* Close a connection */
00539 NetworkRecvStatus NetworkCloseClient(NetworkClientSocket *cs, NetworkRecvStatus status)
00540 {
00541   assert(status != NETWORK_RECV_STATUS_OKAY);
00542   /*
00543    * Sending a message just before leaving the game calls cs->Send_Packets.
00544    * This might invoke this function, which means that when we close the
00545    * connection after cs->Send_Packets we will close an already closed
00546    * connection. This handles that case gracefully without having to make
00547    * that code any more complex or more aware of the validity of the socket.
00548    */
00549   if (cs->sock == INVALID_SOCKET) return status;
00550 
00551   if (status != NETWORK_RECV_STATUS_CONN_LOST && !cs->HasClientQuit() && _network_server && cs->status >= STATUS_AUTHORIZED) {
00552     /* We did not receive a leave message from this client... */
00553     char client_name[NETWORK_CLIENT_NAME_LENGTH];
00554     NetworkClientSocket *new_cs;
00555 
00556     NetworkGetClientName(client_name, sizeof(client_name), cs);
00557 
00558     NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, NULL, STR_NETWORK_ERROR_CLIENT_CONNECTION_LOST);
00559 
00560     /* Inform other clients of this... strange leaving ;) */
00561     FOR_ALL_CLIENT_SOCKETS(new_cs) {
00562       if (new_cs->status > STATUS_AUTHORIZED && cs != new_cs) {
00563         SEND_COMMAND(PACKET_SERVER_ERROR_QUIT)(new_cs, cs->client_id, NETWORK_ERROR_CONNECTION_LOST);
00564       }
00565     }
00566   }
00567 
00568   DEBUG(net, 1, "Closed client connection %d", cs->client_id);
00569 
00570   if (_network_server) {
00571     /* We just lost one client :( */
00572     if (cs->status >= STATUS_AUTHORIZED) _network_game_info.clients_on--;
00573     _network_clients_connected--;
00574 
00575     SetWindowDirty(WC_CLIENT_LIST, 0);
00576   }
00577 
00578   cs->Send_Packets(true);
00579 
00580   delete cs->GetInfo();
00581   delete cs;
00582 
00583   return status;
00584 }
00585 
00586 /* For the server, to accept new clients */
00587 static void NetworkAcceptClients(SOCKET ls)
00588 {
00589   for (;;) {
00590     struct sockaddr_storage sin;
00591     memset(&sin, 0, sizeof(sin));
00592     socklen_t sin_len = sizeof(sin);
00593     SOCKET s = accept(ls, (struct sockaddr*)&sin, &sin_len);
00594     if (s == INVALID_SOCKET) return;
00595 
00596     SetNonBlocking(s); // XXX error handling?
00597 
00598     NetworkAddress address(sin, sin_len);
00599     DEBUG(net, 1, "Client connected from %s on frame %d", address.GetHostname(), _frame_counter);
00600 
00601     SetNoDelay(s); // XXX error handling?
00602 
00603     /* Check if the client is banned */
00604     bool banned = false;
00605     for (char **iter = _network_ban_list.Begin(); iter != _network_ban_list.End(); iter++) {
00606       banned = address.IsInNetmask(*iter);
00607       if (banned) {
00608         Packet p(PACKET_SERVER_BANNED);
00609         p.PrepareToSend();
00610 
00611         DEBUG(net, 1, "Banned ip tried to join (%s), refused", *iter);
00612 
00613         send(s, (const char*)p.buffer, p.size, 0);
00614         closesocket(s);
00615         break;
00616       }
00617     }
00618     /* If this client is banned, continue with next client */
00619     if (banned) continue;
00620 
00621     NetworkClientSocket *cs = NetworkAllocClient(s);
00622     if (cs == NULL) {
00623       /* no more clients allowed?
00624        * Send to the client that we are full! */
00625       Packet p(PACKET_SERVER_FULL);
00626       p.PrepareToSend();
00627 
00628       send(s, (const char*)p.buffer, p.size, 0);
00629       closesocket(s);
00630 
00631       continue;
00632     }
00633 
00634     /* a new client has connected. We set him at inactive for now
00635      *  maybe he is only requesting server-info. Till he has sent a PACKET_CLIENT_MAP_OK
00636      *  the client stays inactive */
00637     cs->status = STATUS_INACTIVE;
00638 
00639     cs->GetInfo()->client_address = address; // Save the IP of the client
00640   }
00641 }
00642 
00643 /* Set up the listen socket for the server */
00644 static bool NetworkListen()
00645 {
00646   assert(_listensockets.Length() == 0);
00647 
00648   NetworkAddressList addresses;
00649   GetBindAddresses(&addresses, _settings_client.network.server_port);
00650 
00651   for (NetworkAddress *address = addresses.Begin(); address != addresses.End(); address++) {
00652     address->Listen(SOCK_STREAM, &_listensockets);
00653   }
00654 
00655   if (_listensockets.Length() == 0) {
00656     ServerStartError("Could not create listening socket");
00657     return false;
00658   }
00659 
00660   return true;
00661 }
00662 
00664 static void InitializeNetworkPools()
00665 {
00666   _networkclientsocket_pool.CleanPool();
00667   _networkclientinfo_pool.CleanPool();
00668 }
00669 
00670 /* Close all current connections */
00671 static void NetworkClose()
00672 {
00673   NetworkClientSocket *cs;
00674 
00675   FOR_ALL_CLIENT_SOCKETS(cs) {
00676     if (!_network_server) {
00677       SEND_COMMAND(PACKET_CLIENT_QUIT)();
00678       cs->Send_Packets();
00679     }
00680     NetworkCloseClient(cs, NETWORK_RECV_STATUS_CONN_LOST);
00681   }
00682 
00683   if (_network_server) {
00684     /* We are a server, also close the listensocket */
00685     for (SocketList::iterator s = _listensockets.Begin(); s != _listensockets.End(); s++) {
00686       closesocket(s->second);
00687     }
00688     _listensockets.Clear();
00689     DEBUG(net, 1, "[tcp] closed listeners");
00690   }
00691 
00692   TCPConnecter::KillAll();
00693 
00694   _networking = false;
00695   _network_server = false;
00696 
00697   NetworkFreeLocalCommandQueue();
00698 
00699   free(_network_company_states);
00700   _network_company_states = NULL;
00701 
00702   InitializeNetworkPools();
00703 }
00704 
00705 /* Inits the network (cleans sockets and stuff) */
00706 static void NetworkInitialize()
00707 {
00708   InitializeNetworkPools();
00709   NetworkUDPInitialize();
00710 
00711   _sync_frame = 0;
00712   _network_first_time = true;
00713 
00714   _network_reconnect = 0;
00715 }
00716 
00718 class TCPQueryConnecter : TCPConnecter {
00719 public:
00720   TCPQueryConnecter(const NetworkAddress &address) : TCPConnecter(address) {}
00721 
00722   virtual void OnFailure()
00723   {
00724     NetworkDisconnect();
00725   }
00726 
00727   virtual void OnConnect(SOCKET s)
00728   {
00729     _networking = true;
00730     NetworkAllocClient(s);
00731     SEND_COMMAND(PACKET_CLIENT_COMPANY_INFO)();
00732   }
00733 };
00734 
00735 /* Query a server to fetch his game-info
00736  *  If game_info is true, only the gameinfo is fetched,
00737  *   else only the client_info is fetched */
00738 void NetworkTCPQueryServer(NetworkAddress address)
00739 {
00740   if (!_network_available) return;
00741 
00742   NetworkDisconnect();
00743   NetworkInitialize();
00744 
00745   new TCPQueryConnecter(address);
00746 }
00747 
00748 /* Validates an address entered as a string and adds the server to
00749  * the list. If you use this function, the games will be marked
00750  * as manually added. */
00751 void NetworkAddServer(const char *b)
00752 {
00753   if (*b != '\0') {
00754     const char *port = NULL;
00755     const char *company = NULL;
00756     char host[NETWORK_HOSTNAME_LENGTH];
00757     uint16 rport;
00758 
00759     strecpy(host, b, lastof(host));
00760 
00761     strecpy(_settings_client.network.connect_to_ip, b, lastof(_settings_client.network.connect_to_ip));
00762     rport = NETWORK_DEFAULT_PORT;
00763 
00764     ParseConnectionString(&company, &port, host);
00765     if (port != NULL) rport = atoi(port);
00766 
00767     NetworkUDPQueryServer(NetworkAddress(host, rport), true);
00768   }
00769 }
00770 
00776 void GetBindAddresses(NetworkAddressList *addresses, uint16 port)
00777 {
00778   for (char **iter = _network_bind_list.Begin(); iter != _network_bind_list.End(); iter++) {
00779     *addresses->Append() = NetworkAddress(*iter, port);
00780   }
00781 
00782   /* No address, so bind to everything. */
00783   if (addresses->Length() == 0) {
00784     *addresses->Append() = NetworkAddress("", port);
00785   }
00786 }
00787 
00788 /* Generates the list of manually added hosts from NetworkGameList and
00789  * dumps them into the array _network_host_list. This array is needed
00790  * by the function that generates the config file. */
00791 void NetworkRebuildHostList()
00792 {
00793   _network_host_list.Clear();
00794 
00795   for (NetworkGameList *item = _network_game_list; item != NULL; item = item->next) {
00796     if (item->manually) *_network_host_list.Append() = strdup(item->address.GetAddressAsString(false));
00797   }
00798 }
00799 
00801 class TCPClientConnecter : TCPConnecter {
00802 public:
00803   TCPClientConnecter(const NetworkAddress &address) : TCPConnecter(address) {}
00804 
00805   virtual void OnFailure()
00806   {
00807     NetworkError(STR_NETWORK_ERROR_NOCONNECTION);
00808   }
00809 
00810   virtual void OnConnect(SOCKET s)
00811   {
00812     _networking = true;
00813     NetworkAllocClient(s);
00814     IConsoleCmdExec("exec scripts/on_client.scr 0");
00815     NetworkClient_Connected();
00816   }
00817 };
00818 
00819 
00820 /* Used by clients, to connect to a server */
00821 void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const char *join_server_password, const char *join_company_password)
00822 {
00823   if (!_network_available) return;
00824 
00825   if (address.GetPort() == 0) return;
00826 
00827   strecpy(_settings_client.network.last_host, address.GetHostname(), lastof(_settings_client.network.last_host));
00828   _settings_client.network.last_port = address.GetPort();
00829   _network_join_as = join_as;
00830   _network_join_server_password = join_server_password;
00831   _network_join_company_password = join_company_password;
00832 
00833   NetworkDisconnect();
00834   NetworkInitialize();
00835 
00836   _network_join_status = NETWORK_JOIN_STATUS_CONNECTING;
00837   ShowJoinStatusWindow();
00838 
00839   new TCPClientConnecter(address);
00840 }
00841 
00842 static void NetworkInitGameInfo()
00843 {
00844   if (StrEmpty(_settings_client.network.server_name)) {
00845     snprintf(_settings_client.network.server_name, sizeof(_settings_client.network.server_name), "Unnamed Server");
00846   }
00847 
00848   /* The server is a client too */
00849   _network_game_info.clients_on = _network_dedicated ? 0 : 1;
00850 
00851   NetworkClientInfo *ci = new NetworkClientInfo(CLIENT_ID_SERVER);
00852   ci->client_playas = _network_dedicated ? COMPANY_SPECTATOR : _local_company;
00853   /* Give the server a valid IP; banning it is pointless anyways */
00854   sockaddr_in sock;
00855   memset(&sock, 0, sizeof(sock));
00856   sock.sin_family = AF_INET;
00857   ci->client_address = NetworkAddress((sockaddr*)&sock, sizeof(sock));
00858 
00859   strecpy(ci->client_name, _settings_client.network.client_name, lastof(ci->client_name));
00860 }
00861 
00862 bool NetworkServerStart()
00863 {
00864   if (!_network_available) return false;
00865 
00866   /* Call the pre-scripts */
00867   IConsoleCmdExec("exec scripts/pre_server.scr 0");
00868   if (_network_dedicated) IConsoleCmdExec("exec scripts/pre_dedicated.scr 0");
00869 
00870   NetworkDisconnect();
00871   NetworkInitialize();
00872   if (!NetworkListen()) return false;
00873 
00874   /* Try to start UDP-server */
00875   _network_udp_server = _udp_server_socket->Listen();
00876 
00877   _network_company_states = CallocT<NetworkCompanyState>(MAX_COMPANIES);
00878   _network_server = true;
00879   _networking = true;
00880   _frame_counter = 0;
00881   _frame_counter_server = 0;
00882   _frame_counter_max = 0;
00883   _last_sync_frame = 0;
00884   _network_own_client_id = CLIENT_ID_SERVER;
00885 
00886   _network_clients_connected = 0;
00887   _network_company_passworded = 0;
00888 
00889   NetworkInitGameInfo();
00890 
00891   /* execute server initialization script */
00892   IConsoleCmdExec("exec scripts/on_server.scr 0");
00893   /* if the server is dedicated ... add some other script */
00894   if (_network_dedicated) IConsoleCmdExec("exec scripts/on_dedicated.scr 0");
00895 
00896   /* Try to register us to the master server */
00897   _network_last_advertise_frame = 0;
00898   _network_need_advertise = true;
00899   NetworkUDPAdvertise();
00900   return true;
00901 }
00902 
00903 /* The server is rebooting...
00904  * The only difference with NetworkDisconnect, is the packets that is sent */
00905 void NetworkReboot()
00906 {
00907   if (_network_server) {
00908     NetworkClientSocket *cs;
00909     FOR_ALL_CLIENT_SOCKETS(cs) {
00910       SEND_COMMAND(PACKET_SERVER_NEWGAME)(cs);
00911       cs->Send_Packets();
00912     }
00913   }
00914 
00915   NetworkClose();
00916 }
00917 
00922 void NetworkDisconnect(bool blocking)
00923 {
00924   if (_network_server) {
00925     NetworkClientSocket *cs;
00926     FOR_ALL_CLIENT_SOCKETS(cs) {
00927       SEND_COMMAND(PACKET_SERVER_SHUTDOWN)(cs);
00928       cs->Send_Packets();
00929     }
00930   }
00931 
00932   if (_settings_client.network.server_advertise) NetworkUDPRemoveAdvertise(blocking);
00933 
00934   DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
00935 
00936   NetworkClose();
00937 }
00938 
00939 /* Receives something from the network */
00940 static bool NetworkReceive()
00941 {
00942   NetworkClientSocket *cs;
00943   fd_set read_fd, write_fd;
00944   struct timeval tv;
00945 
00946   FD_ZERO(&read_fd);
00947   FD_ZERO(&write_fd);
00948 
00949   FOR_ALL_CLIENT_SOCKETS(cs) {
00950     FD_SET(cs->sock, &read_fd);
00951     FD_SET(cs->sock, &write_fd);
00952   }
00953 
00954   /* take care of listener port */
00955   for (SocketList::iterator s = _listensockets.Begin(); s != _listensockets.End(); s++) {
00956     FD_SET(s->second, &read_fd);
00957   }
00958 
00959   tv.tv_sec = tv.tv_usec = 0; // don't block at all.
00960 #if !defined(__MORPHOS__) && !defined(__AMIGA__)
00961   int n = select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv);
00962 #else
00963   int n = WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL);
00964 #endif
00965   if (n == -1 && !_network_server) NetworkError(STR_NETWORK_ERROR_LOSTCONNECTION);
00966 
00967   /* accept clients.. */
00968   for (SocketList::iterator s = _listensockets.Begin(); s != _listensockets.End(); s++) {
00969     if (FD_ISSET(s->second, &read_fd)) NetworkAcceptClients(s->second);
00970   }
00971 
00972   /* read stuff from clients */
00973   FOR_ALL_CLIENT_SOCKETS(cs) {
00974     cs->writable = !!FD_ISSET(cs->sock, &write_fd);
00975     if (FD_ISSET(cs->sock, &read_fd)) {
00976       if (_network_server) {
00977         NetworkServer_ReadPackets(cs);
00978       } else {
00979         NetworkRecvStatus res;
00980 
00981         /* The client already was quiting! */
00982         if (cs->HasClientQuit()) return false;
00983 
00984         res = NetworkClient_ReadPackets(cs);
00985         if (res != NETWORK_RECV_STATUS_OKAY) {
00986           /* The client made an error of which we can not recover
00987            *   close the client and drop back to main menu */
00988           NetworkClientError(res, cs);
00989           return false;
00990         }
00991       }
00992     }
00993   }
00994   return true;
00995 }
00996 
00997 /* This sends all buffered commands (if possible) */
00998 static void NetworkSend()
00999 {
01000   NetworkClientSocket *cs;
01001   FOR_ALL_CLIENT_SOCKETS(cs) {
01002     if (cs->writable) {
01003       cs->Send_Packets();
01004 
01005       if (cs->status == STATUS_MAP) {
01006         /* This client is in the middle of a map-send, call the function for that */
01007         SEND_COMMAND(PACKET_SERVER_MAP)(cs);
01008       }
01009     }
01010   }
01011 }
01012 
01013 static bool NetworkDoClientLoop()
01014 {
01015   _frame_counter++;
01016 
01017   NetworkExecuteLocalCommandQueue();
01018 
01019   StateGameLoop();
01020 
01021   /* Check if we are in sync! */
01022   if (_sync_frame != 0) {
01023     if (_sync_frame == _frame_counter) {
01024 #ifdef NETWORK_SEND_DOUBLE_SEED
01025       if (_sync_seed_1 != _random.state[0] || _sync_seed_2 != _random.state[1]) {
01026 #else
01027       if (_sync_seed_1 != _random.state[0]) {
01028 #endif
01029         NetworkError(STR_NETWORK_ERROR_DESYNC);
01030         DEBUG(desync, 1, "sync_err: %08x; %02x", _date, _date_fract);
01031         DEBUG(net, 0, "Sync error detected!");
01032         NetworkClientError(NETWORK_RECV_STATUS_DESYNC, NetworkClientSocket::Get(0));
01033         return false;
01034       }
01035 
01036       /* If this is the first time we have a sync-frame, we
01037        *   need to let the server know that we are ready and at the same
01038        *   frame as he is.. so we can start playing! */
01039       if (_network_first_time) {
01040         _network_first_time = false;
01041         SEND_COMMAND(PACKET_CLIENT_ACK)();
01042       }
01043 
01044       _sync_frame = 0;
01045     } else if (_sync_frame < _frame_counter) {
01046       DEBUG(net, 1, "Missed frame for sync-test (%d / %d)", _sync_frame, _frame_counter);
01047       _sync_frame = 0;
01048     }
01049   }
01050 
01051   return true;
01052 }
01053 
01054 /* We have to do some UDP checking */
01055 void NetworkUDPGameLoop()
01056 {
01057   _network_content_client.SendReceive();
01058   TCPConnecter::CheckCallbacks();
01059   NetworkHTTPSocketHandler::HTTPReceive();
01060 
01061   if (_network_udp_server) {
01062     _udp_server_socket->ReceivePackets();
01063     _udp_master_socket->ReceivePackets();
01064   } else {
01065     _udp_client_socket->ReceivePackets();
01066     if (_network_udp_broadcast > 0) _network_udp_broadcast--;
01067     NetworkGameListRequery();
01068   }
01069 }
01070 
01071 /* The main loop called from ttd.c
01072  *  Here we also have to do StateGameLoop if needed! */
01073 void NetworkGameLoop()
01074 {
01075   if (!_networking) return;
01076 
01077   if (!NetworkReceive()) return;
01078 
01079   if (_network_server) {
01080     /* Log the sync state to check for in-syncedness of replays. */
01081     if (_date_fract == 0) {
01082       /* We don't want to log multiple times if paused. */
01083       static Date last_log;
01084       if (last_log != _date) {
01085         DEBUG(desync, 1, "sync: %08x; %02x; %08x; %08x", _date, _date_fract, _random.state[0], _random.state[1]);
01086         last_log = _date;
01087       }
01088     }
01089 
01090 #ifdef DEBUG_DUMP_COMMANDS
01091     /* Loading of the debug commands from -ddesync>=1 */
01092     static FILE *f = FioFOpenFile("commands.log", "rb", SAVE_DIR);
01093     static Date next_date = 0;
01094     static uint32 next_date_fract;
01095     static CommandPacket *cp = NULL;
01096     static bool check_sync_state = false;
01097     static uint32 sync_state[2];
01098     if (f == NULL && next_date == 0) {
01099       DEBUG(net, 0, "Cannot open commands.log");
01100       next_date = 1;
01101     }
01102 
01103     while (f != NULL && !feof(f)) {
01104       if (_date == next_date && _date_fract == next_date_fract) {
01105         if (cp != NULL) {
01106           NetworkSend_Command(cp->tile, cp->p1, cp->p2, cp->cmd & ~CMD_FLAGS_MASK, NULL, cp->text, cp->company);
01107           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));
01108           free(cp);
01109           cp = NULL;
01110         }
01111         if (check_sync_state) {
01112           if (sync_state[0] == _random.state[0] && sync_state[1] == _random.state[1]) {
01113             DEBUG(net, 0, "sync check: %08x; %02x; match", _date, _date_fract);
01114           } else {
01115             DEBUG(net, 0, "sync check: %08x; %02x; mismatch expected {%08x, %08x}, got {%08x, %08x}",
01116                   _date, _date_fract, sync_state[0], sync_state[1], _random.state[0], _random.state[1]);
01117             NOT_REACHED();
01118           }
01119           check_sync_state = false;
01120         }
01121       }
01122 
01123       if (cp != NULL || check_sync_state) break;
01124 
01125       char buff[4096];
01126       if (fgets(buff, lengthof(buff), f) == NULL) break;
01127 
01128       char *p = buff;
01129       /* Ignore the "[date time] " part of the message */
01130       if (*p == '[') {
01131         p = strchr(p, ']');
01132         if (p == NULL) break;
01133         p += 2;
01134       }
01135 
01136       if (strncmp(p, "cmd: ", 5) == 0) {
01137         cp = CallocT<CommandPacket>(1);
01138         int company;
01139         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);
01140         /* There are 8 pieces of data to read, however the last is a
01141          * string that might or might not exist. Ignore it if that
01142          * string misses because in 99% of the time it's not used. */
01143         assert(ret == 8 || ret == 7);
01144         cp->company = (CompanyID)company;
01145       } else if (strncmp(p, "join: ", 6) == 0) {
01146         /* Manually insert a pause when joining; this way the client can join at the exact right time. */
01147         int ret = sscanf(p + 6, "%x; %x", &next_date, &next_date_fract);
01148         assert(ret == 2);
01149         DEBUG(net, 0, "injecting pause for join at %08x:%02x; please join when paused", next_date, next_date_fract);
01150         cp = CallocT<CommandPacket>(1);
01151         cp->company = COMPANY_SPECTATOR;
01152         cp->cmd = CMD_PAUSE;
01153         cp->p1 = PM_PAUSED_NORMAL;
01154         cp->p2 = 1;
01155         _ddc_fastforward = false;
01156       } else if (strncmp(p, "sync: ", 6) == 0) {
01157         int ret = sscanf(p + 6, "%x; %x; %x; %x", &next_date, &next_date_fract, &sync_state[0], &sync_state[1]);
01158         assert(ret == 4);
01159         check_sync_state = true;
01160       } else if (strncmp(p, "msg: ", 5) == 0 || strncmp(p, "client: ", 8) == 0 ||
01161             strncmp(p, "load: ", 6) == 0 || strncmp(p, "save: ", 6) == 0) {
01162         /* A message that is not very important to the log playback, but part of the log. */
01163       } else {
01164         /* Can't parse a line; what's wrong here? */
01165         DEBUG(net, 0, "trying to parse: %s", p);
01166         NOT_REACHED();
01167       }
01168     }
01169     if (f != NULL && feof(f)) {
01170       DEBUG(net, 0, "End of commands.log");
01171       fclose(f);
01172       f = NULL;
01173     }
01174 #endif /* DEBUG_DUMP_COMMANDS */
01175     if (_frame_counter >= _frame_counter_max) {
01176       /* Only check for active clients just before we're going to send out
01177        * the commands so we don't send multiple pause/unpause commands when
01178        * the frame_freq is more than 1 tick. */
01179       CheckPauseOnJoin();
01180       CheckMinActiveClients();
01181     }
01182 
01183     bool send_frame = false;
01184 
01185     /* We first increase the _frame_counter */
01186     _frame_counter++;
01187     /* Update max-frame-counter */
01188     if (_frame_counter > _frame_counter_max) {
01189       _frame_counter_max = _frame_counter + _settings_client.network.frame_freq;
01190       send_frame = true;
01191     }
01192 
01193     NetworkExecuteLocalCommandQueue();
01194 
01195     /* Then we make the frame */
01196     StateGameLoop();
01197 
01198     _sync_seed_1 = _random.state[0];
01199 #ifdef NETWORK_SEND_DOUBLE_SEED
01200     _sync_seed_2 = _random.state[1];
01201 #endif
01202 
01203     NetworkServer_Tick(send_frame);
01204   } else {
01205     /* Client */
01206 
01207     /* Make sure we are at the frame were the server is (quick-frames) */
01208     if (_frame_counter_server > _frame_counter) {
01209       while (_frame_counter_server > _frame_counter) {
01210         if (!NetworkDoClientLoop()) break;
01211       }
01212     } else {
01213       /* Else, keep on going till _frame_counter_max */
01214       if (_frame_counter_max > _frame_counter) NetworkDoClientLoop();
01215     }
01216   }
01217 
01218   NetworkSend();
01219 }
01220 
01221 static void NetworkGenerateServerId()
01222 {
01223   Md5 checksum;
01224   uint8 digest[16];
01225   char hex_output[16 * 2 + 1];
01226   char coding_string[NETWORK_NAME_LENGTH];
01227   int di;
01228 
01229   snprintf(coding_string, sizeof(coding_string), "%d%s", (uint)Random(), "OpenTTD Server ID");
01230 
01231   /* Generate the MD5 hash */
01232   checksum.Append((const uint8*)coding_string, strlen(coding_string));
01233   checksum.Finish(digest);
01234 
01235   for (di = 0; di < 16; ++di) {
01236     sprintf(hex_output + di * 2, "%02x", digest[di]);
01237   }
01238 
01239   /* _settings_client.network.network_id is our id */
01240   snprintf(_settings_client.network.network_id, sizeof(_settings_client.network.network_id), "%s", hex_output);
01241 }
01242 
01243 void NetworkStartDebugLog(NetworkAddress address)
01244 {
01245   extern SOCKET _debug_socket;  // Comes from debug.c
01246 
01247   DEBUG(net, 0, "Redirecting DEBUG() to %s:%d", address.GetHostname(), address.GetPort());
01248 
01249   SOCKET s = address.Connect();
01250   if (s == INVALID_SOCKET) {
01251     DEBUG(net, 0, "Failed to open socket for redirection DEBUG()");
01252     return;
01253   }
01254 
01255   _debug_socket = s;
01256 
01257   DEBUG(net, 0, "DEBUG() is now redirected");
01258 }
01259 
01261 void NetworkStartUp()
01262 {
01263   DEBUG(net, 3, "[core] starting network...");
01264 
01265   /* Network is available */
01266   _network_available = NetworkCoreInitialize();
01267   _network_dedicated = false;
01268   _network_last_advertise_frame = 0;
01269   _network_need_advertise = true;
01270   _network_advertise_retries = 0;
01271 
01272   /* Generate an server id when there is none yet */
01273   if (StrEmpty(_settings_client.network.network_id)) NetworkGenerateServerId();
01274 
01275   memset(&_network_game_info, 0, sizeof(_network_game_info));
01276 
01277   NetworkInitialize();
01278   DEBUG(net, 3, "[core] network online, multiplayer available");
01279   NetworkFindBroadcastIPs(&_broadcast_list);
01280 }
01281 
01283 void NetworkShutDown()
01284 {
01285   NetworkDisconnect(true);
01286   NetworkUDPClose();
01287 
01288   DEBUG(net, 3, "[core] shutting down network");
01289 
01290   _network_available = false;
01291 
01292   NetworkCoreShutdown();
01293 }
01294 
01299 bool IsNetworkCompatibleVersion(const char *other)
01300 {
01301   return strncmp(_openttd_revision, other, NETWORK_REVISION_LENGTH - 1) == 0;
01302 }
01303 
01304 #endif /* ENABLE_NETWORK */

Generated on Sat Apr 17 23:24:49 2010 for OpenTTD by  doxygen 1.6.1