00001
00002
00003 #include "../stdafx.h"
00004 #include "network_data.h"
00005
00006 #ifdef ENABLE_NETWORK
00007
00008 #include "../openttd.h"
00009 #include "../debug.h"
00010 #include "../strings_func.h"
00011 #include "../map_func.h"
00012 #include "../command_func.h"
00013 #include "../variables.h"
00014 #include "../date_func.h"
00015 #include "../newgrf_config.h"
00016 #include "network_client.h"
00017 #include "network_server.h"
00018 #include "network_udp.h"
00019 #include "network_gamelist.h"
00020 #include "core/udp.h"
00021 #include "core/tcp.h"
00022 #include "core/core.h"
00023 #include "network_gui.h"
00024 #include "../console.h"
00025 #include <stdarg.h>
00026 #include "../md5.h"
00027 #include "../fileio.h"
00028 #include "../texteff.hpp"
00029 #include "../core/random_func.hpp"
00030 #include "../window_func.h"
00031 #include "../string_func.h"
00032 #include "../player_func.h"
00033 #include "../settings_type.h"
00034 #ifdef DEBUG_DUMP_COMMANDS
00035 #include "../core/alloc_func.hpp"
00036 #endif
00037
00038 #include "table/strings.h"
00039
00040 bool _network_reload_cfg;
00041 bool _network_server;
00042 bool _network_available;
00043 bool _network_dedicated;
00044 bool _network_advertise;
00045
00046
00047 assert_compile((int)NETWORK_NUM_LANDSCAPES == (int)NUM_LANDSCAPE);
00048 assert_compile((int)NETWORK_COMPANY_NAME_LENGTH == MAX_LENGTH_COMPANY_NAME + 1);
00049
00050
00051 CommandPacket *_local_command_queue;
00052
00053 extern NetworkUDPSocketHandler *_udp_client_socket;
00054 extern NetworkUDPSocketHandler *_udp_server_socket;
00055 extern NetworkUDPSocketHandler *_udp_master_socket;
00056
00057
00058
00059 NetworkTCPSocketHandler _clients[MAX_CLIENTS];
00060
00061
00062
00063
00064 static SOCKET _listensocket;
00065
00066
00067 static byte _network_clients_connected = 0;
00068
00069 static uint16 _network_client_index = NETWORK_SERVER_INDEX + 1;
00070
00071
00072 extern void StateGameLoop();
00073
00074
00075 NetworkClientInfo *NetworkFindClientInfoFromIndex(uint16 client_index)
00076 {
00077 NetworkClientInfo *ci;
00078
00079 for (ci = _network_client_info; ci != endof(_network_client_info); ci++) {
00080 if (ci->client_index == client_index) return ci;
00081 }
00082
00083 return NULL;
00084 }
00085
00089 NetworkClientInfo *NetworkFindClientInfoFromIP(const char *ip)
00090 {
00091 NetworkClientInfo *ci;
00092 uint32 ip_number = inet_addr(ip);
00093
00094 for (ci = _network_client_info; ci != endof(_network_client_info); ci++) {
00095 if (ci->client_ip == ip_number) return ci;
00096 }
00097
00098 return NULL;
00099 }
00100
00101
00102 NetworkTCPSocketHandler *NetworkFindClientStateFromIndex(uint16 client_index)
00103 {
00104 NetworkTCPSocketHandler *cs;
00105
00106 for (cs = _clients; cs != endof(_clients); cs++) {
00107 if (cs->index == client_index) return cs;
00108 }
00109
00110 return NULL;
00111 }
00112
00113
00114
00115 void NetworkGetClientName(char *client_name, size_t size, const NetworkTCPSocketHandler *cs)
00116 {
00117 const NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs);
00118
00119 if (ci->client_name[0] == '\0') {
00120 snprintf(client_name, size, "Client #%4d", cs->index);
00121 } else {
00122 ttd_strlcpy(client_name, ci->client_name, size);
00123 }
00124 }
00125
00126 byte NetworkSpectatorCount()
00127 {
00128 NetworkTCPSocketHandler *cs;
00129 byte count = 0;
00130
00131 FOR_ALL_CLIENTS(cs) {
00132 if (DEREF_CLIENT_INFO(cs)->client_playas == PLAYER_SPECTATOR) count++;
00133 }
00134
00135 return count;
00136 }
00137
00138
00139
00140
00141 void CDECL NetworkTextMessage(NetworkAction action, uint16 color, bool self_send, const char *name, const char *str, ...)
00142 {
00143 char buf[1024];
00144 va_list va;
00145 const int duration = 10;
00146 char message[1024];
00147 char temp[1024];
00148
00149 va_start(va, str);
00150 vsnprintf(buf, lengthof(buf), str, va);
00151 va_end(va);
00152
00153 switch (action) {
00154 case NETWORK_ACTION_SERVER_MESSAGE:
00155 color = 1;
00156 snprintf(message, sizeof(message), "*** %s", buf);
00157 break;
00158 case NETWORK_ACTION_JOIN:
00159 color = 1;
00160 GetString(temp, STR_NETWORK_CLIENT_JOINED, lastof(temp));
00161 snprintf(message, sizeof(message), "*** %s %s", name, temp);
00162 break;
00163 case NETWORK_ACTION_LEAVE:
00164 color = 1;
00165 GetString(temp, STR_NETWORK_ERR_LEFT, lastof(temp));
00166 snprintf(message, sizeof(message), "*** %s %s (%s)", name, temp, buf);
00167 break;
00168 case NETWORK_ACTION_GIVE_MONEY:
00169 if (self_send) {
00170 SetDParamStr(0, name);
00171 SetDParam(1, atoi(buf));
00172 GetString(temp, STR_NETWORK_GAVE_MONEY_AWAY, lastof(temp));
00173 snprintf(message, sizeof(message), "*** %s", temp);
00174 } else {
00175 SetDParam(0, atoi(buf));
00176 GetString(temp, STR_NETWORK_GIVE_MONEY, lastof(temp));
00177 snprintf(message, sizeof(message), "*** %s %s", name, temp);
00178 }
00179 break;
00180 case NETWORK_ACTION_NAME_CHANGE:
00181 GetString(temp, STR_NETWORK_NAME_CHANGE, lastof(temp));
00182 snprintf(message, sizeof(message), "*** %s %s %s", name, temp, buf);
00183 break;
00184 case NETWORK_ACTION_CHAT_COMPANY:
00185 SetDParamStr(0, name);
00186 SetDParamStr(1, buf);
00187 GetString(temp, self_send ? STR_NETWORK_CHAT_TO_COMPANY : STR_NETWORK_CHAT_COMPANY, lastof(temp));
00188 ttd_strlcpy(message, temp, sizeof(message));
00189 break;
00190 case NETWORK_ACTION_CHAT_CLIENT:
00191 SetDParamStr(0, name);
00192 SetDParamStr(1, buf);
00193 GetString(temp, self_send ? STR_NETWORK_CHAT_TO_CLIENT : STR_NETWORK_CHAT_CLIENT, lastof(temp));
00194 ttd_strlcpy(message, temp, sizeof(message));
00195 break;
00196 default:
00197 SetDParamStr(0, name);
00198 SetDParamStr(1, buf);
00199 GetString(temp, STR_NETWORK_CHAT_ALL, lastof(temp));
00200 ttd_strlcpy(message, temp, sizeof(message));
00201 break;
00202 }
00203
00204 DebugDumpCommands("ddc:cmsg:%d;%d;%s\n", _date, _date_fract, message);
00205 IConsolePrintF(color, "%s", message);
00206 AddChatMessage(color, duration, "%s", message);
00207 }
00208
00209
00210 uint NetworkCalculateLag(const NetworkTCPSocketHandler *cs)
00211 {
00212 int lag = cs->last_frame_server - cs->last_frame;
00213
00214
00215
00216 if (cs->last_frame_server + DAY_TICKS + _network_frame_freq < _frame_counter)
00217 lag += _frame_counter - (cs->last_frame_server + DAY_TICKS + _network_frame_freq);
00218
00219 return lag;
00220 }
00221
00222
00223
00224
00225 static void NetworkError(StringID error_string)
00226 {
00227 _switch_mode = SM_MENU;
00228 _switch_mode_errorstr = error_string;
00229 }
00230
00231 static void ClientStartError(const char *error)
00232 {
00233 DEBUG(net, 0, "[client] could not start network: %s",error);
00234 NetworkError(STR_NETWORK_ERR_CLIENT_START);
00235 }
00236
00237 static void ServerStartError(const char *error)
00238 {
00239 DEBUG(net, 0, "[server] could not start network: %s",error);
00240 NetworkError(STR_NETWORK_ERR_SERVER_START);
00241 }
00242
00243 static void NetworkClientError(NetworkRecvStatus res, NetworkTCPSocketHandler* cs)
00244 {
00245
00246
00247 NetworkErrorCode errorno;
00248
00249
00250 if (res == NETWORK_RECV_STATUS_CLOSE_QUERY) {
00251 cs->has_quit = true;
00252 NetworkCloseClient(cs);
00253 _networking = false;
00254
00255 DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
00256 return;
00257 }
00258
00259 switch (res) {
00260 case NETWORK_RECV_STATUS_DESYNC: errorno = NETWORK_ERROR_DESYNC; break;
00261 case NETWORK_RECV_STATUS_SAVEGAME: errorno = NETWORK_ERROR_SAVEGAME_FAILED; break;
00262 case NETWORK_RECV_STATUS_NEWGRF_MISMATCH: errorno = NETWORK_ERROR_NEWGRF_MISMATCH; break;
00263 default: errorno = NETWORK_ERROR_GENERAL; break;
00264 }
00265
00266 if (res != NETWORK_RECV_STATUS_SERVER_ERROR && res != NETWORK_RECV_STATUS_SERVER_FULL &&
00267 res != NETWORK_RECV_STATUS_SERVER_BANNED) {
00268 SEND_COMMAND(PACKET_CLIENT_ERROR)(errorno);
00269
00270
00271 DEREF_CLIENT(0)->Send_Packets();
00272 }
00273
00274 _switch_mode = SM_MENU;
00275 NetworkCloseClient(cs);
00276 _networking = false;
00277 }
00278
00283 char* GetNetworkErrorMsg(char* buf, NetworkErrorCode err, const char* last)
00284 {
00285
00286
00287 static const StringID network_error_strings[] = {
00288 STR_NETWORK_ERR_CLIENT_GENERAL,
00289 STR_NETWORK_ERR_CLIENT_DESYNC,
00290 STR_NETWORK_ERR_CLIENT_SAVEGAME,
00291 STR_NETWORK_ERR_CLIENT_CONNECTION_LOST,
00292 STR_NETWORK_ERR_CLIENT_PROTOCOL_ERROR,
00293 STR_NETWORK_ERR_CLIENT_NEWGRF_MISMATCH,
00294 STR_NETWORK_ERR_CLIENT_NOT_AUTHORIZED,
00295 STR_NETWORK_ERR_CLIENT_NOT_EXPECTED,
00296 STR_NETWORK_ERR_CLIENT_WRONG_REVISION,
00297 STR_NETWORK_ERR_CLIENT_NAME_IN_USE,
00298 STR_NETWORK_ERR_CLIENT_WRONG_PASSWORD,
00299 STR_NETWORK_ERR_CLIENT_PLAYER_MISMATCH,
00300 STR_NETWORK_ERR_CLIENT_KICKED,
00301 STR_NETWORK_ERR_CLIENT_CHEATER,
00302 STR_NETWORK_ERR_CLIENT_SERVER_FULL
00303 };
00304
00305 if (err >= (ptrdiff_t)lengthof(network_error_strings)) err = NETWORK_ERROR_GENERAL;
00306
00307 return GetString(buf, network_error_strings[err], last);
00308 }
00309
00310
00311 static uint NetworkCountPlayers()
00312 {
00313 NetworkTCPSocketHandler *cs;
00314 uint count = 0;
00315
00316 FOR_ALL_CLIENTS(cs) {
00317 const NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs);
00318 if (IsValidPlayer(ci->client_playas)) count++;
00319 }
00320
00321 return count;
00322 }
00323
00324 static bool _min_players_paused = false;
00325
00326
00327 void CheckMinPlayers()
00328 {
00329 if (!_network_dedicated) return;
00330
00331 if (NetworkCountPlayers() < _network_min_players) {
00332 if (_min_players_paused) return;
00333
00334 _min_players_paused = true;
00335 DoCommandP(0, 1, 0, NULL, CMD_PAUSE);
00336 NetworkServer_HandleChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game paused (not enough players)", NETWORK_SERVER_INDEX);
00337 } else {
00338 if (!_min_players_paused) return;
00339
00340 _min_players_paused = false;
00341 DoCommandP(0, 0, 0, NULL, CMD_PAUSE);
00342 NetworkServer_HandleChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game unpaused (enough players)", NETWORK_SERVER_INDEX);
00343 }
00344 }
00345
00346
00347 static void NetworkFindIPs()
00348 {
00349 #if !defined(PSP)
00350 int i;
00351
00352 #if defined(BEOS_NET_SERVER)
00353
00354 int _netstat(int fd, char **output, int verbose);
00355
00356 int seek_past_header(char **pos, const char *header) {
00357 char *new_pos = strstr(*pos, header);
00358 if (new_pos == 0) {
00359 return B_ERROR;
00360 }
00361 *pos += strlen(header) + new_pos - *pos + 1;
00362 return B_OK;
00363 }
00364
00365 int output_length;
00366 char *output_pointer = NULL;
00367 char **output;
00368 int sock = socket(AF_INET, SOCK_DGRAM, 0);
00369 i = 0;
00370
00371
00372 _broadcast_list[0] = 0;
00373
00374 if (sock < 0) {
00375 DEBUG(net, 0, "[core] error creating socket");
00376 return;
00377 }
00378
00379 output_length = _netstat(sock, &output_pointer, 1);
00380 if (output_length < 0) {
00381 DEBUG(net, 0, "[core] error running _netstat");
00382 return;
00383 }
00384
00385 output = &output_pointer;
00386 if (seek_past_header(output, "IP Interfaces:") == B_OK) {
00387 for (;;) {
00388 uint32 n, fields, read;
00389 uint8 i1, i2, i3, i4, j1, j2, j3, j4;
00390 struct in_addr inaddr;
00391 uint32 ip;
00392 uint32 netmask;
00393
00394 fields = sscanf(*output, "%u: %hhu.%hhu.%hhu.%hhu, netmask %hhu.%hhu.%hhu.%hhu%n",
00395 &n, &i1,&i2,&i3,&i4, &j1,&j2,&j3,&j4, &read);
00396 read += 1;
00397 if (fields != 9) {
00398 break;
00399 }
00400
00401 ip = (uint32)i1 << 24 | (uint32)i2 << 16 | (uint32)i3 << 8 | (uint32)i4;
00402 netmask = (uint32)j1 << 24 | (uint32)j2 << 16 | (uint32)j3 << 8 | (uint32)j4;
00403
00404 if (ip != INADDR_LOOPBACK && ip != INADDR_ANY) {
00405 inaddr.s_addr = htonl(ip | ~netmask);
00406 _broadcast_list[i] = inaddr.s_addr;
00407 i++;
00408 if (i == MAX_INTERFACES) break;
00409 }
00410 if (read < 0) {
00411 break;
00412 }
00413 *output += read;
00414 }
00415
00416
00417
00418 closesocket(sock);
00419 }
00420 #elif defined(HAVE_GETIFADDRS)
00421 struct ifaddrs *ifap, *ifa;
00422
00423
00424 _broadcast_list[0] = 0;
00425
00426 if (getifaddrs(&ifap) != 0)
00427 return;
00428
00429 i = 0;
00430 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
00431 if (!(ifa->ifa_flags & IFF_BROADCAST)) continue;
00432 if (ifa->ifa_broadaddr == NULL) continue;
00433 if (ifa->ifa_broadaddr->sa_family != AF_INET) continue;
00434 _broadcast_list[i] = ((struct sockaddr_in*)ifa->ifa_broadaddr)->sin_addr.s_addr;
00435 i++;
00436 if (i == MAX_INTERFACES) break;
00437 }
00438 freeifaddrs(ifap);
00439
00440 #else
00441 SOCKET sock;
00442 #ifdef WIN32
00443 DWORD len = 0;
00444 INTERFACE_INFO ifo[MAX_INTERFACES];
00445 uint j;
00446 #else
00447 char buf[4 * 1024];
00448 struct ifconf ifconf;
00449 const char* buf_end;
00450 const char* p;
00451 #endif
00452
00453
00454 _broadcast_list[0] = 0;
00455
00456 sock = socket(AF_INET, SOCK_DGRAM, 0);
00457 if (sock == INVALID_SOCKET) return;
00458
00459 #ifdef WIN32
00460 memset(&ifo[0], 0, sizeof(ifo));
00461 if ((WSAIoctl(sock, SIO_GET_INTERFACE_LIST, NULL, 0, &ifo[0], sizeof(ifo), &len, NULL, NULL)) != 0) {
00462 closesocket(sock);
00463 return;
00464 }
00465
00466 i = 0;
00467 for (j = 0; j < len / sizeof(*ifo); j++) {
00468 if (ifo[j].iiFlags & IFF_LOOPBACK) continue;
00469 if (!(ifo[j].iiFlags & IFF_BROADCAST)) continue;
00470
00471
00472
00473 _broadcast_list[i++] =
00474 ifo[j].iiAddress.AddressIn.sin_addr.s_addr |
00475 ~ifo[j].iiNetmask.AddressIn.sin_addr.s_addr;
00476 if (i == MAX_INTERFACES) break;
00477 }
00478 #else
00479 ifconf.ifc_len = sizeof(buf);
00480 ifconf.ifc_buf = buf;
00481 if (ioctl(sock, SIOCGIFCONF, &ifconf) == -1) {
00482 closesocket(sock);
00483 return;
00484 }
00485
00486 i = 0;
00487 buf_end = buf + ifconf.ifc_len;
00488 for (p = buf; p < buf_end;) {
00489 const struct ifreq* req = (const struct ifreq*)p;
00490
00491 if (req->ifr_addr.sa_family == AF_INET) {
00492 struct ifreq r;
00493
00494 strncpy(r.ifr_name, req->ifr_name, lengthof(r.ifr_name));
00495 if (ioctl(sock, SIOCGIFFLAGS, &r) != -1 &&
00496 r.ifr_flags & IFF_BROADCAST &&
00497 ioctl(sock, SIOCGIFBRDADDR, &r) != -1) {
00498 _broadcast_list[i++] =
00499 ((struct sockaddr_in*)&r.ifr_broadaddr)->sin_addr.s_addr;
00500 if (i == MAX_INTERFACES) break;
00501 }
00502 }
00503
00504 p += sizeof(struct ifreq);
00505 #if defined(AF_LINK) && !defined(SUNOS)
00506 p += req->ifr_addr.sa_len - sizeof(struct sockaddr);
00507 #endif
00508 }
00509 #endif
00510
00511 closesocket(sock);
00512 #endif
00513
00514 _broadcast_list[i] = 0;
00515
00516 DEBUG(net, 3, "Detected broadcast addresses:");
00517
00518 for (i = 0; _broadcast_list[i] != 0; i++) {
00519 DEBUG(net, 3, "%d) %s", i, inet_ntoa(*(struct in_addr *)&_broadcast_list[i]));
00520 }
00521 #endif
00522 }
00523
00524
00525 unsigned long NetworkResolveHost(const char *hostname)
00526 {
00527 in_addr_t ip;
00528
00529
00530 ip = inet_addr(hostname);
00531
00532
00533 if (ip == INADDR_NONE) {
00534 struct in_addr addr;
00535 #if !defined(PSP)
00536 struct hostent *he = gethostbyname(hostname);
00537 if (he == NULL) {
00538 DEBUG(net, 0, "Cannot resolve '%s'", hostname);
00539 return ip;
00540 }
00541 addr = *(struct in_addr *)he->h_addr_list[0];
00542 #else
00543 int rid = -1;
00544 char buf[1024];
00545
00546
00547 if (sceNetResolverCreate(&rid, buf, sizeof(buf)) < 0) {
00548 DEBUG(net, 0, "[NET] Error connecting resolver");
00549 return ip;
00550 }
00551
00552
00553 if (sceNetResolverStartNtoA(rid, hostname, &addr, 2, 3) < 0) {
00554 DEBUG(net, 0, "[NET] Cannot resolve %s", hostname);
00555 sceNetResolverDelete(rid);
00556 return ip;
00557 }
00558 sceNetResolverDelete(rid);
00559 #endif
00560
00561 DEBUG(net, 1, "[NET] Resolved %s to %s", hostname, inet_ntoa(addr));
00562 ip = addr.s_addr;
00563 }
00564 return ip;
00565 }
00566
00573 void ParseConnectionString(const char **player, const char **port, char *connection_string)
00574 {
00575 char *p;
00576 for (p = connection_string; *p != '\0'; p++) {
00577 switch (*p) {
00578 case '#':
00579 *player = p + 1;
00580 *p = '\0';
00581 break;
00582 case ':':
00583 *port = p + 1;
00584 *p = '\0';
00585 break;
00586 }
00587 }
00588 }
00589
00590
00591
00592 static NetworkTCPSocketHandler *NetworkAllocClient(SOCKET s)
00593 {
00594 NetworkTCPSocketHandler *cs;
00595 byte client_no = 0;
00596
00597 if (_network_server) {
00598
00599 if (_network_clients_connected >= MAX_CLIENTS) return NULL;
00600 if (_network_game_info.clients_on >= _network_game_info.clients_max) return NULL;
00601
00602
00603 client_no = _network_clients_connected++;
00604 }
00605
00606 cs = DEREF_CLIENT(client_no);
00607 cs->Initialize();
00608 cs->sock = s;
00609 cs->last_frame = _frame_counter;
00610 cs->last_frame_server = _frame_counter;
00611
00612 if (_network_server) {
00613 NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs);
00614 memset(ci, 0, sizeof(*ci));
00615
00616 cs->index = _network_client_index++;
00617 ci->client_index = cs->index;
00618 ci->client_playas = PLAYER_INACTIVE_CLIENT;
00619 ci->join_date = _date;
00620
00621 InvalidateWindow(WC_CLIENT_LIST, 0);
00622 }
00623
00624 return cs;
00625 }
00626
00627
00628 void NetworkCloseClient(NetworkTCPSocketHandler *cs)
00629 {
00630 NetworkClientInfo *ci;
00631
00632 if (cs->sock == INVALID_SOCKET) {
00633 cs->has_quit = true;
00634 return;
00635 }
00636
00637 DEBUG(net, 1, "Closed client connection %d", cs->index);
00638
00639 if (!cs->has_quit && _network_server && cs->status > STATUS_INACTIVE) {
00640
00641 NetworkErrorCode errorno = NETWORK_ERROR_CONNECTION_LOST;
00642 char str[100];
00643 char client_name[NETWORK_CLIENT_NAME_LENGTH];
00644 NetworkTCPSocketHandler *new_cs;
00645
00646 NetworkGetClientName(client_name, sizeof(client_name), cs);
00647
00648 GetNetworkErrorMsg(str, errorno, lastof(str));
00649
00650 NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, client_name, "%s", str);
00651
00652
00653 FOR_ALL_CLIENTS(new_cs) {
00654 if (new_cs->status > STATUS_AUTH && cs != new_cs) {
00655 SEND_COMMAND(PACKET_SERVER_ERROR_QUIT)(new_cs, cs->index, errorno);
00656 }
00657 }
00658 }
00659
00660
00661 if (cs->status == STATUS_PRE_ACTIVE && _network_pause_on_join) {
00662 DoCommandP(0, 0, 0, NULL, CMD_PAUSE);
00663 NetworkServer_HandleChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game unpaused", NETWORK_SERVER_INDEX);
00664 }
00665
00666 cs->Destroy();
00667
00668
00669 ci = DEREF_CLIENT_INFO(cs);
00670
00671 if (_network_server) {
00672
00673 if (cs->status >= STATUS_AUTH) _network_game_info.clients_on--;
00674 _network_clients_connected--;
00675
00676 while ((cs + 1) != DEREF_CLIENT(MAX_CLIENTS) && (cs + 1)->sock != INVALID_SOCKET) {
00677 *cs = *(cs + 1);
00678 *ci = *(ci + 1);
00679 cs++;
00680 ci++;
00681 }
00682
00683 InvalidateWindow(WC_CLIENT_LIST, 0);
00684 }
00685
00686
00687 cs->sock = INVALID_SOCKET;
00688 cs->status = STATUS_INACTIVE;
00689 cs->index = NETWORK_EMPTY_INDEX;
00690 ci->client_index = NETWORK_EMPTY_INDEX;
00691
00692 CheckMinPlayers();
00693 }
00694
00695
00696 static bool NetworkConnect(const char *hostname, int port)
00697 {
00698 SOCKET s;
00699 struct sockaddr_in sin;
00700
00701 DEBUG(net, 1, "Connecting to %s %d", hostname, port);
00702
00703 s = socket(AF_INET, SOCK_STREAM, 0);
00704 if (s == INVALID_SOCKET) {
00705 ClientStartError("socket() failed");
00706 return false;
00707 }
00708
00709 if (!SetNoDelay(s)) DEBUG(net, 1, "Setting TCP_NODELAY failed");
00710
00711 sin.sin_family = AF_INET;
00712 sin.sin_addr.s_addr = NetworkResolveHost(hostname);
00713 sin.sin_port = htons(port);
00714 _network_last_host_ip = sin.sin_addr.s_addr;
00715
00716
00717 if (connect(s, (struct sockaddr*) &sin, sizeof(sin)) != 0) return false;
00718
00719 if (!SetNonBlocking(s)) DEBUG(net, 0, "Setting non-blocking mode failed");
00720
00721
00722 NetworkAllocClient(s);
00723
00724 _network_join_status = NETWORK_JOIN_STATUS_CONNECTING;
00725 ShowJoinStatusWindow();
00726
00727 return true;
00728 }
00729
00730
00731 static void NetworkAcceptClients()
00732 {
00733 struct sockaddr_in sin;
00734 NetworkTCPSocketHandler *cs;
00735 uint i;
00736 bool banned;
00737
00738
00739 assert(_listensocket != INVALID_SOCKET);
00740
00741 for (;;) {
00742 socklen_t sin_len = sizeof(sin);
00743 SOCKET s = accept(_listensocket, (struct sockaddr*)&sin, &sin_len);
00744 if (s == INVALID_SOCKET) return;
00745
00746 SetNonBlocking(s);
00747
00748 DEBUG(net, 1, "Client connected from %s on frame %d", inet_ntoa(sin.sin_addr), _frame_counter);
00749
00750 SetNoDelay(s);
00751
00752
00753 banned = false;
00754 for (i = 0; i < lengthof(_network_ban_list); i++) {
00755 if (_network_ban_list[i] == NULL) continue;
00756
00757 if (sin.sin_addr.s_addr == inet_addr(_network_ban_list[i])) {
00758 Packet p(PACKET_SERVER_BANNED);
00759 p.PrepareToSend();
00760
00761 DEBUG(net, 1, "Banned ip tried to join (%s), refused", _network_ban_list[i]);
00762
00763 send(s, (const char*)p.buffer, p.size, 0);
00764 closesocket(s);
00765
00766 banned = true;
00767 break;
00768 }
00769 }
00770
00771 if (banned) continue;
00772
00773 cs = NetworkAllocClient(s);
00774 if (cs == NULL) {
00775
00776
00777 Packet p(PACKET_SERVER_FULL);
00778 p.PrepareToSend();
00779
00780 send(s, (const char*)p.buffer, p.size, 0);
00781 closesocket(s);
00782
00783 continue;
00784 }
00785
00786
00787
00788
00789 cs->status = STATUS_INACTIVE;
00790
00791 DEREF_CLIENT_INFO(cs)->client_ip = sin.sin_addr.s_addr;
00792 }
00793 }
00794
00795
00796 static bool NetworkListen()
00797 {
00798 SOCKET ls;
00799 struct sockaddr_in sin;
00800
00801 DEBUG(net, 1, "Listening on %s:%d", _network_server_bind_ip_host, _network_server_port);
00802
00803 ls = socket(AF_INET, SOCK_STREAM, 0);
00804 if (ls == INVALID_SOCKET) {
00805 ServerStartError("socket() on listen socket failed");
00806 return false;
00807 }
00808
00809 {
00810 int reuse = 1;
00811
00812 if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) == -1) {
00813 ServerStartError("setsockopt() on listen socket failed");
00814 return false;
00815 }
00816 }
00817
00818 if (!SetNonBlocking(ls)) DEBUG(net, 0, "Setting non-blocking mode failed");
00819
00820 sin.sin_family = AF_INET;
00821 sin.sin_addr.s_addr = _network_server_bind_ip;
00822 sin.sin_port = htons(_network_server_port);
00823
00824 if (bind(ls, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
00825 ServerStartError("bind() failed");
00826 return false;
00827 }
00828
00829 if (listen(ls, 1) != 0) {
00830 ServerStartError("listen() failed");
00831 return false;
00832 }
00833
00834 _listensocket = ls;
00835
00836 return true;
00837 }
00838
00839
00840 static void NetworkClose()
00841 {
00842 NetworkTCPSocketHandler *cs;
00843
00844 FOR_ALL_CLIENTS(cs) {
00845 if (!_network_server) {
00846 SEND_COMMAND(PACKET_CLIENT_QUIT)("leaving");
00847 cs->Send_Packets();
00848 }
00849 NetworkCloseClient(cs);
00850 }
00851
00852 if (_network_server) {
00853
00854 closesocket(_listensocket);
00855 _listensocket = INVALID_SOCKET;
00856 DEBUG(net, 1, "Closed listener");
00857 NetworkUDPCloseAll();
00858 }
00859 }
00860
00861
00862 static void NetworkInitialize()
00863 {
00864 NetworkTCPSocketHandler *cs;
00865
00866 _local_command_queue = NULL;
00867
00868
00869 for (cs = _clients; cs != &_clients[MAX_CLIENTS]; cs++) {
00870 cs->Initialize();
00871 }
00872
00873
00874 memset(&_network_client_info, 0, sizeof(_network_client_info));
00875 memset(&_network_player_info, 0, sizeof(_network_player_info));
00876
00877 _sync_frame = 0;
00878 _network_first_time = true;
00879
00880 _network_reconnect = 0;
00881
00882 NetworkUDPInitialize();
00883 }
00884
00885
00886
00887
00888 void NetworkTCPQueryServer(const char* host, unsigned short port)
00889 {
00890 if (!_network_available) return;
00891
00892 NetworkDisconnect();
00893
00894 NetworkInitialize();
00895
00896 _network_server = false;
00897
00898
00899 _networking = NetworkConnect(host, port);
00900
00901
00902 if (_networking) {
00903 SEND_COMMAND(PACKET_CLIENT_COMPANY_INFO)();
00904 } else {
00905 NetworkDisconnect();
00906 }
00907 }
00908
00909
00910
00911
00912 void NetworkAddServer(const char *b)
00913 {
00914 if (*b != '\0') {
00915 const char *port = NULL;
00916 const char *player = NULL;
00917 char host[NETWORK_HOSTNAME_LENGTH];
00918 uint16 rport;
00919
00920 ttd_strlcpy(host, b, lengthof(host));
00921
00922 ttd_strlcpy(_network_default_ip, b, lengthof(_network_default_ip));
00923 rport = NETWORK_DEFAULT_PORT;
00924
00925 ParseConnectionString(&player, &port, host);
00926 if (port != NULL) rport = atoi(port);
00927
00928 NetworkUDPQueryServer(host, rport, true);
00929 }
00930 }
00931
00932
00933
00934
00935 void NetworkRebuildHostList()
00936 {
00937 uint i = 0;
00938 const NetworkGameList *item = _network_game_list;
00939 while (item != NULL && i != lengthof(_network_host_list)) {
00940 if (item->manually) {
00941 free(_network_host_list[i]);
00942 _network_host_list[i++] = str_fmt("%s:%i", item->info.hostname, item->port);
00943 }
00944 item = item->next;
00945 }
00946
00947 for (; i < lengthof(_network_host_list); i++) {
00948 free(_network_host_list[i]);
00949 _network_host_list[i] = NULL;
00950 }
00951 }
00952
00953
00954 bool NetworkClientConnectGame(const char *host, uint16 port)
00955 {
00956 if (!_network_available) return false;
00957
00958 if (port == 0) return false;
00959
00960 ttd_strlcpy(_network_last_host, host, sizeof(_network_last_host));
00961 _network_last_port = port;
00962
00963 NetworkDisconnect();
00964 NetworkUDPCloseAll();
00965 NetworkInitialize();
00966
00967
00968 _networking = NetworkConnect(host, port);
00969
00970
00971 if (_networking) {
00972 IConsoleCmdExec("exec scripts/on_client.scr 0");
00973 NetworkClient_Connected();
00974 } else {
00975
00976 NetworkError(STR_NETWORK_ERR_NOCONNECTION);
00977 }
00978
00979 return _networking;
00980 }
00981
00982 static void NetworkInitGameInfo()
00983 {
00984 NetworkClientInfo *ci;
00985
00986 ttd_strlcpy(_network_game_info.server_name, _network_server_name, sizeof(_network_game_info.server_name));
00987 ttd_strlcpy(_network_game_info.server_password, _network_server_password, sizeof(_network_server_password));
00988 ttd_strlcpy(_network_game_info.rcon_password, _network_rcon_password, sizeof(_network_rcon_password));
00989 if (_network_game_info.server_name[0] == '\0')
00990 snprintf(_network_game_info.server_name, sizeof(_network_game_info.server_name), "Unnamed Server");
00991
00992 ttd_strlcpy(_network_game_info.server_revision, _openttd_revision, sizeof(_network_game_info.server_revision));
00993
00994
00995 if (_network_dedicated) {
00996 _network_game_info.clients_on = 0;
00997 _network_game_info.companies_on = 0;
00998 _network_game_info.dedicated = true;
00999 } else {
01000 _network_game_info.clients_on = 1;
01001 _network_game_info.companies_on = 1;
01002 _network_game_info.dedicated = false;
01003 }
01004
01005 _network_game_info.spectators_on = 0;
01006
01007 _network_game_info.game_date = _date;
01008 _network_game_info.start_date = ConvertYMDToDate(_patches.starting_year, 0, 1);
01009 _network_game_info.map_width = MapSizeX();
01010 _network_game_info.map_height = MapSizeY();
01011 _network_game_info.map_set = _opt.landscape;
01012
01013 _network_game_info.use_password = (_network_server_password[0] != '\0');
01014
01015
01016
01017 ci = &_network_client_info[MAX_CLIENT_INFO - 1];
01018 memset(ci, 0, sizeof(*ci));
01019
01020 ci->client_index = NETWORK_SERVER_INDEX;
01021 ci->client_playas = _network_dedicated ? PLAYER_SPECTATOR : _local_player;
01022
01023 ttd_strlcpy(ci->client_name, _network_player_name, sizeof(ci->client_name));
01024 ttd_strlcpy(ci->unique_id, _network_unique_id, sizeof(ci->unique_id));
01025 }
01026
01027 bool NetworkServerStart()
01028 {
01029 if (!_network_available) return false;
01030
01031
01032 IConsoleCmdExec("exec scripts/pre_server.scr 0");
01033 if (_network_dedicated) IConsoleCmdExec("exec scripts/pre_dedicated.scr 0");
01034
01035 NetworkInitialize();
01036 if (!NetworkListen()) return false;
01037
01038
01039 _network_udp_server = true;
01040 _network_udp_server = _udp_server_socket->Listen(_network_server_bind_ip, _network_server_port, false);
01041
01042 _network_server = true;
01043 _networking = true;
01044 _frame_counter = 0;
01045 _frame_counter_server = 0;
01046 _frame_counter_max = 0;
01047 _last_sync_frame = 0;
01048 _network_own_client_index = NETWORK_SERVER_INDEX;
01049
01050
01051 if (!_network_dedicated) _network_playas = PLAYER_FIRST;
01052
01053 _network_clients_connected = 0;
01054
01055 NetworkInitGameInfo();
01056
01057
01058 IConsoleCmdExec("exec scripts/on_server.scr 0");
01059
01060 if (_network_dedicated) IConsoleCmdExec("exec scripts/on_dedicated.scr 0");
01061
01062 _min_players_paused = false;
01063 CheckMinPlayers();
01064
01065
01066 _network_last_advertise_frame = 0;
01067 _network_need_advertise = true;
01068 NetworkUDPAdvertise();
01069 return true;
01070 }
01071
01072
01073
01074 void NetworkReboot()
01075 {
01076 if (_network_server) {
01077 NetworkTCPSocketHandler *cs;
01078 FOR_ALL_CLIENTS(cs) {
01079 SEND_COMMAND(PACKET_SERVER_NEWGAME)(cs);
01080 cs->Send_Packets();
01081 }
01082 }
01083
01084 NetworkClose();
01085
01086
01087 while (_local_command_queue != NULL) {
01088 CommandPacket *p = _local_command_queue;
01089 _local_command_queue = _local_command_queue->next;
01090 free(p);
01091 }
01092
01093 _networking = false;
01094 _network_server = false;
01095 }
01096
01097
01098 void NetworkDisconnect()
01099 {
01100 if (_network_server) {
01101 NetworkTCPSocketHandler *cs;
01102 FOR_ALL_CLIENTS(cs) {
01103 SEND_COMMAND(PACKET_SERVER_SHUTDOWN)(cs);
01104 cs->Send_Packets();
01105 }
01106 }
01107
01108 if (_network_advertise) NetworkUDPRemoveAdvertise();
01109
01110 DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
01111
01112 NetworkClose();
01113
01114
01115 while (_local_command_queue != NULL) {
01116 CommandPacket *p = _local_command_queue;
01117 _local_command_queue = _local_command_queue->next;
01118 free(p);
01119 }
01120
01121 _networking = false;
01122 _network_server = false;
01123 }
01124
01125
01126 static bool NetworkReceive()
01127 {
01128 NetworkTCPSocketHandler *cs;
01129 int n;
01130 fd_set read_fd, write_fd;
01131 struct timeval tv;
01132
01133 FD_ZERO(&read_fd);
01134 FD_ZERO(&write_fd);
01135
01136 FOR_ALL_CLIENTS(cs) {
01137 FD_SET(cs->sock, &read_fd);
01138 FD_SET(cs->sock, &write_fd);
01139 }
01140
01141
01142 if (_network_server) FD_SET(_listensocket, &read_fd);
01143
01144 tv.tv_sec = tv.tv_usec = 0;
01145 #if !defined(__MORPHOS__) && !defined(__AMIGA__)
01146 n = select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv);
01147 #else
01148 n = WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL);
01149 #endif
01150 if (n == -1 && !_network_server) NetworkError(STR_NETWORK_ERR_LOSTCONNECTION);
01151
01152
01153 if (_network_server && FD_ISSET(_listensocket, &read_fd)) NetworkAcceptClients();
01154
01155
01156 FOR_ALL_CLIENTS(cs) {
01157 cs->writable = !!FD_ISSET(cs->sock, &write_fd);
01158 if (FD_ISSET(cs->sock, &read_fd)) {
01159 if (_network_server) {
01160 NetworkServer_ReadPackets(cs);
01161 } else {
01162 NetworkRecvStatus res;
01163
01164
01165 if (cs->has_quit) return false;
01166
01167 res = NetworkClient_ReadPackets(cs);
01168 if (res != NETWORK_RECV_STATUS_OKAY) {
01169
01170
01171 NetworkClientError(res, cs);
01172 return false;
01173 }
01174 }
01175 }
01176 }
01177 return true;
01178 }
01179
01180
01181 static void NetworkSend()
01182 {
01183 NetworkTCPSocketHandler *cs;
01184 FOR_ALL_CLIENTS(cs) {
01185 if (cs->writable) {
01186 cs->Send_Packets();
01187
01188 if (cs->status == STATUS_MAP) {
01189
01190 SEND_COMMAND(PACKET_SERVER_MAP)(cs);
01191 }
01192 }
01193 }
01194 }
01195
01196
01197 static void NetworkHandleLocalQueue()
01198 {
01199 CommandPacket *cp, **cp_prev;
01200
01201 cp_prev = &_local_command_queue;
01202
01203 while ( (cp = *cp_prev) != NULL) {
01204
01205
01206
01207 if (_frame_counter < cp->frame) break;
01208
01209 if (_frame_counter > cp->frame) {
01210
01211
01212 error("[net] Trying to execute a packet in the past!");
01213 }
01214
01215
01216 NetworkExecuteCommand(cp);
01217
01218 *cp_prev = cp->next;
01219 free(cp);
01220 }
01221
01222
01223
01224
01225 for (cp = _local_command_queue; cp; cp = cp->next) {
01226 assert(_frame_counter < cp->frame);
01227 }
01228
01229 }
01230
01231 static bool NetworkDoClientLoop()
01232 {
01233 _frame_counter++;
01234
01235 NetworkHandleLocalQueue();
01236
01237 StateGameLoop();
01238
01239
01240 if (_sync_frame != 0) {
01241 if (_sync_frame == _frame_counter) {
01242 #ifdef NETWORK_SEND_DOUBLE_SEED
01243 if (_sync_seed_1 != _random.state[0] || _sync_seed_2 != _random.state[1]) {
01244 #else
01245 if (_sync_seed_1 != _random.state[0]) {
01246 #endif
01247 NetworkError(STR_NETWORK_ERR_DESYNC);
01248 DebugDumpCommands("ddc:serr:%d;%d\n", _date, _date_fract);
01249 DEBUG(net, 0, "Sync error detected!");
01250 NetworkClientError(NETWORK_RECV_STATUS_DESYNC, DEREF_CLIENT(0));
01251 return false;
01252 }
01253
01254
01255
01256
01257 if (_network_first_time) {
01258 _network_first_time = false;
01259 SEND_COMMAND(PACKET_CLIENT_ACK)();
01260 }
01261
01262 _sync_frame = 0;
01263 } else if (_sync_frame < _frame_counter) {
01264 DEBUG(net, 1, "Missed frame for sync-test (%d / %d)", _sync_frame, _frame_counter);
01265 _sync_frame = 0;
01266 }
01267 }
01268
01269 return true;
01270 }
01271
01272
01273 void NetworkUDPGameLoop()
01274 {
01275 if (_network_udp_server) {
01276 _udp_server_socket->ReceivePackets();
01277 _udp_master_socket->ReceivePackets();
01278 } else {
01279 _udp_client_socket->ReceivePackets();
01280 if (_network_udp_broadcast > 0) _network_udp_broadcast--;
01281 NetworkGameListRequery();
01282 }
01283 }
01284
01285
01286
01287 void NetworkGameLoop()
01288 {
01289 if (!_networking) return;
01290
01291 if (!NetworkReceive()) return;
01292
01293 if (_network_server) {
01294 #ifdef DEBUG_DUMP_COMMANDS
01295 static FILE *f = FioFOpenFile("commands.log", "rb", SAVE_DIR);
01296 static Date next_date = 0;
01297 static uint32 next_date_fract;
01298 static CommandPacket *cp = NULL;
01299 if (f == NULL && next_date == 0) {
01300 printf("Cannot open commands.log\n");
01301 next_date = 1;
01302 }
01303
01304 while (f != NULL && !feof(f)) {
01305 if (cp != NULL && _date == next_date && _date_fract == next_date_fract) {
01306 _current_player = cp->player;
01307 _cmd_text = cp->text;
01308 DoCommandP(cp->tile, cp->p1, cp->p2, NULL, cp->cmd);
01309 free(cp);
01310 cp = NULL;
01311 }
01312
01313 if (cp != NULL) break;
01314
01315 char buff[4096];
01316 if (fgets(buff, lengthof(buff), f) == NULL) break;
01317 if (strncmp(buff, "ddc:cmd:", 8) != 0) continue;
01318 cp = MallocT<CommandPacket>(1);
01319 int player;
01320 sscanf(&buff[8], "%d;%d;%d;%d;%d;%d;%d;%s", &next_date, &next_date_fract, &player, &cp->tile, &cp->p1, &cp->p2, &cp->cmd, cp->text);
01321 cp->player = (Owner)player;
01322 }
01323 #endif
01324
01325 bool send_frame = false;
01326
01327
01328 _frame_counter++;
01329
01330 if (_frame_counter > _frame_counter_max) {
01331 _frame_counter_max = _frame_counter + _network_frame_freq;
01332 send_frame = true;
01333 }
01334
01335 NetworkHandleLocalQueue();
01336
01337
01338 StateGameLoop();
01339
01340 _sync_seed_1 = _random.state[0];
01341 #ifdef NETWORK_SEND_DOUBLE_SEED
01342 _sync_seed_2 = _random.state[1];
01343 #endif
01344
01345 NetworkServer_Tick(send_frame);
01346 } else {
01347
01348
01349
01350 if (_frame_counter_server > _frame_counter) {
01351 while (_frame_counter_server > _frame_counter) {
01352 if (!NetworkDoClientLoop()) break;
01353 }
01354 } else {
01355
01356 if (_frame_counter_max > _frame_counter) NetworkDoClientLoop();
01357 }
01358 }
01359
01360 NetworkSend();
01361 }
01362
01363 static void NetworkGenerateUniqueId()
01364 {
01365 Md5 checksum;
01366 uint8 digest[16];
01367 char hex_output[16*2 + 1];
01368 char coding_string[NETWORK_NAME_LENGTH];
01369 int di;
01370
01371 snprintf(coding_string, sizeof(coding_string), "%d%s", (uint)Random(), "OpenTTD Unique ID");
01372
01373
01374 checksum.Append((const uint8*)coding_string, strlen(coding_string));
01375 checksum.Finish(digest);
01376
01377 for (di = 0; di < 16; ++di)
01378 sprintf(hex_output + di * 2, "%02x", digest[di]);
01379
01380
01381 snprintf(_network_unique_id, sizeof(_network_unique_id), "%s", hex_output);
01382 }
01383
01384 void NetworkStartDebugLog(const char *hostname, uint16 port)
01385 {
01386 extern SOCKET _debug_socket;
01387 SOCKET s;
01388 struct sockaddr_in sin;
01389
01390 DEBUG(net, 0, "Redirecting DEBUG() to %s:%d", hostname, port);
01391
01392 s = socket(AF_INET, SOCK_STREAM, 0);
01393 if (s == INVALID_SOCKET) {
01394 DEBUG(net, 0, "Failed to open socket for redirection DEBUG()");
01395 return;
01396 }
01397
01398 if (!SetNoDelay(s)) DEBUG(net, 1, "Setting TCP_NODELAY failed");
01399
01400 sin.sin_family = AF_INET;
01401 sin.sin_addr.s_addr = NetworkResolveHost(hostname);
01402 sin.sin_port = htons(port);
01403
01404 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) != 0) {
01405 DEBUG(net, 0, "Failed to redirection DEBUG() to %s:%d", hostname, port);
01406 return;
01407 }
01408
01409 if (!SetNonBlocking(s)) DEBUG(net, 0, "Setting non-blocking mode failed");
01410 _debug_socket = s;
01411
01412 DEBUG(net, 0, "DEBUG() is now redirected");
01413 }
01414
01416 void NetworkStartUp()
01417 {
01418 DEBUG(net, 3, "[core] starting network...");
01419
01420
01421 _network_available = NetworkCoreInitialize();;
01422 _network_dedicated = false;
01423 _network_last_advertise_frame = 0;
01424 _network_need_advertise = true;
01425 _network_advertise_retries = 0;
01426
01427
01428 _network_server_bind_ip = inet_addr(_network_server_bind_ip_host);
01429
01430 snprintf(_network_server_bind_ip_host, sizeof(_network_server_bind_ip_host), "%s", inet_ntoa(*(struct in_addr *)&_network_server_bind_ip));
01431
01432
01433 if (_network_unique_id[0] == '\0') NetworkGenerateUniqueId();
01434
01435 {
01436 byte cl_max = _network_game_info.clients_max;
01437 byte cp_max = _network_game_info.companies_max;
01438 byte sp_max = _network_game_info.spectators_max;
01439 byte s_lang = _network_game_info.server_lang;
01440
01441 memset(&_network_game_info, 0, sizeof(_network_game_info));
01442 _network_game_info.clients_max = cl_max;
01443 _network_game_info.companies_max = cp_max;
01444 _network_game_info.spectators_max = sp_max;
01445 _network_game_info.server_lang = s_lang;
01446 }
01447
01448
01449 NetworkInitialize();
01450 DEBUG(net, 3, "[core] network online, multiplayer available");
01451 NetworkFindIPs();
01452 }
01453
01455 void NetworkShutDown()
01456 {
01457 NetworkDisconnect();
01458 NetworkUDPShutdown();
01459
01460 DEBUG(net, 3, "[core] shutting down network");
01461
01462 _network_available = false;
01463
01464 NetworkCoreShutdown();
01465 }
01466
01471 bool IsNetworkCompatibleVersion(const char *other)
01472 {
01473 extern const char _openttd_revision[];
01474 return strncmp(_openttd_revision, other, NETWORK_REVISION_LENGTH - 1) == 0;
01475 }
01476
01477 #endif
01478
01479
01480 PlayerID _network_playas;