tron@2186: /* $Id$ */ tron@2186: richk@10724: /** @file network.cpp Base functions for networking support. */ richk@10724: rubidium@5720: #include "../stdafx.h" richk@10991: #include "../player_type.h" truelight@0: truelight@543: #ifdef ENABLE_NETWORK truelight@0: rubidium@5720: #include "../openttd.h" rubidium@5720: #include "../debug.h" rubidium@6872: #include "../strings_func.h" rubidium@6872: #include "../map_func.h" rubidium@6872: #include "../command_func.h" rubidium@5720: #include "../variables.h" rubidium@6872: #include "../date_func.h" rubidium@5720: #include "../newgrf_config.h" richk@10991: #include "network_internal.h" truelight@543: #include "network_client.h" truelight@543: #include "network_server.h" truelight@543: #include "network_udp.h" truelight@543: #include "network_gamelist.h" rubidium@5720: #include "core/udp.h" rubidium@5720: #include "core/tcp.h" rubidium@5774: #include "core/core.h" Darkvater@5683: #include "network_gui.h" richk@10731: #include "../console_func.h" truelight@543: #include /* va_list */ rubidium@5720: #include "../md5.h" richk@6720: #include "../fileio.h" rubidium@6872: #include "../texteff.hpp" rubidium@6872: #include "../core/random_func.hpp" rubidium@6872: #include "../window_func.h" rubidium@6872: #include "../string_func.h" rubidium@6872: #include "../player_func.h" rubidium@6872: #include "../settings_type.h" richk@10724: #include "../landscape_type.h" richk@10724: #include "../rev.h" rubidium@6872: #ifdef DEBUG_DUMP_COMMANDS rubidium@6872: #include "../core/alloc_func.hpp" rubidium@6872: #endif /* DEBUG_DUMP_COMMANDS */ rubidium@6872: #include "table/strings.h" rubidium@6872: rubidium@6872: bool _network_reload_cfg; rubidium@6872: bool _network_server; ///< network-server is active rubidium@6872: bool _network_available; ///< is network mode available? rubidium@6872: bool _network_dedicated; ///< are we a dedicated server? richk@10991: bool _is_network_server; ///< Does this client wants to be a network-server? richk@10991: NetworkServerGameInfo _network_game_info; richk@10991: NetworkPlayerInfo _network_player_info[MAX_PLAYERS]; richk@10991: NetworkClientInfo _network_client_info[MAX_CLIENT_INFO]; richk@10991: uint16 _network_own_client_index; richk@10991: uint16 _redirect_console_to_client; richk@10991: bool _network_need_advertise; richk@10991: uint32 _network_last_advertise_frame; richk@10991: uint8 _network_reconnect; richk@10991: char *_network_host_list[10]; richk@10991: char *_network_ban_list[25]; richk@10991: uint32 _frame_counter_server; // The frame_counter of the server, if in network-mode richk@10991: uint32 _frame_counter_max; // To where we may go with our clients richk@10991: uint32 _last_sync_frame; // Used in the server to store the last time a sync packet was sent to clients. richk@10991: uint32 _broadcast_list[MAX_INTERFACES + 1]; richk@10991: uint32 _network_server_bind_ip; richk@10991: uint32 _sync_seed_1, _sync_seed_2; richk@10991: uint32 _sync_frame; richk@10991: bool _network_first_time; richk@10991: uint32 _network_last_host_ip; richk@10991: bool _network_udp_server; richk@10991: uint16 _network_udp_broadcast; richk@10991: uint8 _network_advertise_retries; truelight@0: rubidium@6061: /* Check whether NETWORK_NUM_LANDSCAPES is still in sync with NUM_LANDSCAPE */ rubidium@6061: assert_compile((int)NETWORK_NUM_LANDSCAPES == (int)NUM_LANDSCAPE); rubidium@6061: rubidium@5838: // global variables (declared in network_data.h) rubidium@5838: CommandPacket *_local_command_queue; rubidium@5838: rubidium@5870: extern NetworkUDPSocketHandler *_udp_client_socket; ///< udp client socket rubidium@5870: extern NetworkUDPSocketHandler *_udp_server_socket; ///< udp server socket rubidium@5870: extern NetworkUDPSocketHandler *_udp_master_socket; ///< udp master socket rubidium@5838: rubidium@5838: // Here we keep track of the clients rubidium@5838: // (and the client uses [0] for his own communication) rubidium@5875: NetworkTCPSocketHandler _clients[MAX_CLIENTS]; rubidium@5838: rubidium@5838: rubidium@5838: truelight@543: // The listen socket for the server truelight@543: static SOCKET _listensocket; truelight@0: truelight@543: // The amount of clients connected truelight@543: static byte _network_clients_connected = 0; truelight@543: // The index counter for new clients (is never decreased) truelight@543: static uint16 _network_client_index = NETWORK_SERVER_INDEX + 1; truelight@0: truelight@716: /* Some externs / forwards */ rubidium@6573: extern void StateGameLoop(); truelight@716: truelight@543: // Function that looks up the CI for a given client-index truelight@543: NetworkClientInfo *NetworkFindClientInfoFromIndex(uint16 client_index) truelight@543: { truelight@543: NetworkClientInfo *ci; truelight@0: Darkvater@4883: for (ci = _network_client_info; ci != endof(_network_client_info); ci++) { tron@4077: if (ci->client_index == client_index) return ci; tron@4077: } truelight@543: truelight@543: return NULL; truelight@543: } truelight@543: Darkvater@2859: /** Return the CI for a given IP Darkvater@2859: * @param ip IP of the client we are looking for. This must be in string-format Darkvater@2859: * @return return a pointer to the corresponding NetworkClientInfo struct or NULL on failure */ Darkvater@2859: NetworkClientInfo *NetworkFindClientInfoFromIP(const char *ip) Darkvater@2859: { Darkvater@2859: NetworkClientInfo *ci; Darkvater@2859: uint32 ip_number = inet_addr(ip); Darkvater@2859: Darkvater@4883: for (ci = _network_client_info; ci != endof(_network_client_info); ci++) { Darkvater@2859: if (ci->client_ip == ip_number) return ci; Darkvater@2859: } Darkvater@2859: Darkvater@2859: return NULL; Darkvater@2859: } Darkvater@2859: truelight@543: // Function that looks up the CS for a given client-index rubidium@5875: NetworkTCPSocketHandler *NetworkFindClientStateFromIndex(uint16 client_index) truelight@543: { rubidium@5875: NetworkTCPSocketHandler *cs; truelight@543: Darkvater@5820: for (cs = _clients; cs != endof(_clients); cs++) { tron@4077: if (cs->index == client_index) return cs; tron@4077: } truelight@543: truelight@543: return NULL; truelight@543: } truelight@543: truelight@543: // NetworkGetClientName is a server-safe function to get the name of the client truelight@543: // if the user did not send it yet, Client # is used. rubidium@5875: void NetworkGetClientName(char *client_name, size_t size, const NetworkTCPSocketHandler *cs) truelight@543: { Darkvater@3623: const NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs); tron@4077: tron@4077: if (ci->client_name[0] == '\0') { Darkvater@3623: snprintf(client_name, size, "Client #%4d", cs->index); tron@4077: } else { Darkvater@3623: ttd_strlcpy(client_name, ci->client_name, size); tron@4077: } truelight@543: } truelight@543: rubidium@6573: byte NetworkSpectatorCount() Darkvater@2944: { rubidium@5875: NetworkTCPSocketHandler *cs; Darkvater@2944: byte count = 0; Darkvater@2944: Darkvater@2944: FOR_ALL_CLIENTS(cs) { Darkvater@4848: if (DEREF_CLIENT_INFO(cs)->client_playas == PLAYER_SPECTATOR) count++; Darkvater@2944: } Darkvater@2944: Darkvater@2944: return count; Darkvater@2944: } Darkvater@2944: truelight@543: // This puts a text-message to the console, or in the future, the chat-box, truelight@543: // (to keep it all a bit more general) truelight@722: // If 'self_send' is true, this is the client who is sending the message richk@10731: void CDECL NetworkTextMessage(NetworkAction action, ConsoleColour color, bool self_send, const char *name, const char *str, ...) truelight@543: { truelight@543: char buf[1024]; truelight@543: va_list va; truelight@543: const int duration = 10; // Game days the messages stay visible truelight@722: char message[1024]; truelight@722: char temp[1024]; truelight@543: truelight@543: va_start(va, str); tron@2373: vsnprintf(buf, lengthof(buf), str, va); truelight@543: va_end(va); truelight@543: truelight@543: switch (action) { Darkvater@4944: case NETWORK_ACTION_SERVER_MESSAGE: richk@10731: color = CC_DEFAULT; Darkvater@4944: snprintf(message, sizeof(message), "*** %s", buf); Darkvater@4944: break; truelight@722: case NETWORK_ACTION_JOIN: richk@10731: color = CC_DEFAULT; Darkvater@4912: GetString(temp, STR_NETWORK_CLIENT_JOINED, lastof(temp)); truelight@722: snprintf(message, sizeof(message), "*** %s %s", name, temp); truelight@722: break; truelight@722: case NETWORK_ACTION_LEAVE: richk@10731: color = CC_DEFAULT; Darkvater@4912: GetString(temp, STR_NETWORK_ERR_LEFT, lastof(temp)); truelight@722: snprintf(message, sizeof(message), "*** %s %s (%s)", name, temp, buf); truelight@543: break; truelight@543: case NETWORK_ACTION_GIVE_MONEY: truelight@722: if (self_send) { ludde@2055: SetDParamStr(0, name); truelight@722: SetDParam(1, atoi(buf)); Darkvater@4912: GetString(temp, STR_NETWORK_GAVE_MONEY_AWAY, lastof(temp)); truelight@722: snprintf(message, sizeof(message), "*** %s", temp); truelight@722: } else { truelight@722: SetDParam(0, atoi(buf)); Darkvater@4912: GetString(temp, STR_NETWORK_GIVE_MONEY, lastof(temp)); truelight@722: snprintf(message, sizeof(message), "*** %s %s", name, temp); truelight@722: } truelight@543: break; truelight@543: case NETWORK_ACTION_NAME_CHANGE: Darkvater@4912: GetString(temp, STR_NETWORK_NAME_CHANGE, lastof(temp)); truelight@722: snprintf(message, sizeof(message), "*** %s %s %s", name, temp, buf); truelight@543: break; Darkvater@4943: case NETWORK_ACTION_CHAT_COMPANY: Darkvater@4943: SetDParamStr(0, name); Darkvater@4943: SetDParamStr(1, buf); Darkvater@4943: GetString(temp, self_send ? STR_NETWORK_CHAT_TO_COMPANY : STR_NETWORK_CHAT_COMPANY, lastof(temp)); Darkvater@4943: ttd_strlcpy(message, temp, sizeof(message)); Darkvater@4943: break; Darkvater@4943: case NETWORK_ACTION_CHAT_CLIENT: Darkvater@4943: SetDParamStr(0, name); Darkvater@4943: SetDParamStr(1, buf); Darkvater@4943: GetString(temp, self_send ? STR_NETWORK_CHAT_TO_CLIENT : STR_NETWORK_CHAT_CLIENT, lastof(temp)); Darkvater@4943: ttd_strlcpy(message, temp, sizeof(message)); Darkvater@4943: break; truelight@543: default: ludde@2055: SetDParamStr(0, name); Darkvater@4943: SetDParamStr(1, buf); Darkvater@4912: GetString(temp, STR_NETWORK_CHAT_ALL, lastof(temp)); Darkvater@4943: ttd_strlcpy(message, temp, sizeof(message)); truelight@543: break; truelight@543: } truelight@722: rubidium@6872: DebugDumpCommands("ddc:cmsg:%d;%d;%s\n", _date, _date_fract, message); truelight@732: IConsolePrintF(color, "%s", message); rubidium@6868: AddChatMessage(color, duration, "%s", message); truelight@543: } truelight@543: truelight@543: // Calculate the frame-lag of a client rubidium@5875: uint NetworkCalculateLag(const NetworkTCPSocketHandler *cs) truelight@543: { truelight@543: int lag = cs->last_frame_server - cs->last_frame; truelight@543: // This client has missed his ACK packet after 1 DAY_TICKS.. truelight@543: // so we increase his lag for every frame that passes! truelight@543: // The packet can be out by a max of _net_frame_freq richk@10991: if (cs->last_frame_server + DAY_TICKS + _settings_client.network.frame_freq < _frame_counter) richk@10991: lag += _frame_counter - (cs->last_frame_server + DAY_TICKS + _settings_client.network.frame_freq); truelight@543: truelight@543: return lag; truelight@543: } truelight@0: truelight@0: truelight@543: // There was a non-recoverable error, drop back to the main menu with a nice truelight@543: // error tron@1095: static void NetworkError(StringID error_string) truelight@193: { truelight@543: _switch_mode = SM_MENU; richk@10242: extern StringID _switch_mode_errorstr; truelight@543: _switch_mode_errorstr = error_string; dominik@105: } dominik@105: tron@1010: static void ClientStartError(const char *error) tron@1010: { Darkvater@5568: DEBUG(net, 0, "[client] could not start network: %s",error); truelight@543: NetworkError(STR_NETWORK_ERR_CLIENT_START); truelight@0: } truelight@0: tron@1010: static void ServerStartError(const char *error) tron@1010: { Darkvater@5568: DEBUG(net, 0, "[server] could not start network: %s",error); truelight@543: NetworkError(STR_NETWORK_ERR_SERVER_START); signde@208: } signde@208: rubidium@5875: static void NetworkClientError(NetworkRecvStatus res, NetworkTCPSocketHandler* cs) tron@4077: { truelight@543: // First, send a CLIENT_ERROR to the server, so he knows we are truelight@543: // disconnection (and why!) truelight@543: NetworkErrorCode errorno; truelight@543: truelight@543: // We just want to close the connection.. truelight@543: if (res == NETWORK_RECV_STATUS_CLOSE_QUERY) { Darkvater@4880: cs->has_quit = true; truelight@716: NetworkCloseClient(cs); truelight@543: _networking = false; truelight@543: truelight@543: DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0); truelight@543: return; truelight@543: } truelight@543: tron@2952: switch (res) { rubidium@6123: case NETWORK_RECV_STATUS_DESYNC: errorno = NETWORK_ERROR_DESYNC; break; rubidium@6123: case NETWORK_RECV_STATUS_SAVEGAME: errorno = NETWORK_ERROR_SAVEGAME_FAILED; break; rubidium@6123: case NETWORK_RECV_STATUS_NEWGRF_MISMATCH: errorno = NETWORK_ERROR_NEWGRF_MISMATCH; break; rubidium@6123: default: errorno = NETWORK_ERROR_GENERAL; break; truelight@543: } truelight@543: // This means we fucked up and the server closed the connection truelight@841: if (res != NETWORK_RECV_STATUS_SERVER_ERROR && res != NETWORK_RECV_STATUS_SERVER_FULL && truelight@841: res != NETWORK_RECV_STATUS_SERVER_BANNED) { truelight@543: SEND_COMMAND(PACKET_CLIENT_ERROR)(errorno); truelight@543: truelight@543: // Dequeue all commands before closing the socket rubidium@6153: DEREF_CLIENT(0)->Send_Packets(); truelight@543: } truelight@543: truelight@543: _switch_mode = SM_MENU; truelight@716: NetworkCloseClient(cs); truelight@543: _networking = false; truelight@543: } truelight@543: Darkvater@3437: /** Retrieve a string representation of an internal error number Darkvater@3437: * @param buf buffer where the error message will be stored tron@4077: * @param err NetworkErrorCode Darkvater@3437: * @return returns a pointer to the error message (buf) */ Darkvater@4912: char* GetNetworkErrorMsg(char* buf, NetworkErrorCode err, const char* last) Darkvater@3437: { Darkvater@3437: /* List of possible network errors, used by Darkvater@3437: * PACKET_SERVER_ERROR and PACKET_CLIENT_ERROR */ Darkvater@3437: static const StringID network_error_strings[] = { Darkvater@3437: STR_NETWORK_ERR_CLIENT_GENERAL, Darkvater@3437: STR_NETWORK_ERR_CLIENT_DESYNC, Darkvater@3437: STR_NETWORK_ERR_CLIENT_SAVEGAME, Darkvater@3437: STR_NETWORK_ERR_CLIENT_CONNECTION_LOST, Darkvater@3437: STR_NETWORK_ERR_CLIENT_PROTOCOL_ERROR, rubidium@6123: STR_NETWORK_ERR_CLIENT_NEWGRF_MISMATCH, Darkvater@3437: STR_NETWORK_ERR_CLIENT_NOT_AUTHORIZED, Darkvater@3437: STR_NETWORK_ERR_CLIENT_NOT_EXPECTED, Darkvater@3437: STR_NETWORK_ERR_CLIENT_WRONG_REVISION, Darkvater@3437: STR_NETWORK_ERR_CLIENT_NAME_IN_USE, Darkvater@3437: STR_NETWORK_ERR_CLIENT_WRONG_PASSWORD, Darkvater@3437: STR_NETWORK_ERR_CLIENT_PLAYER_MISMATCH, Darkvater@3437: STR_NETWORK_ERR_CLIENT_KICKED, Darkvater@3437: STR_NETWORK_ERR_CLIENT_CHEATER, tron@4077: STR_NETWORK_ERR_CLIENT_SERVER_FULL Darkvater@3437: }; Darkvater@3437: rubidium@5838: if (err >= (ptrdiff_t)lengthof(network_error_strings)) err = NETWORK_ERROR_GENERAL; Darkvater@3437: Darkvater@4912: return GetString(buf, network_error_strings[err], last); Darkvater@3437: } Darkvater@3437: peter1138@4716: /* Count the number of active clients connected */ rubidium@6573: static uint NetworkCountPlayers() peter1138@4716: { rubidium@5875: NetworkTCPSocketHandler *cs; peter1138@4716: uint count = 0; peter1138@4716: peter1138@4716: FOR_ALL_CLIENTS(cs) { Darkvater@4880: const NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs); Darkvater@4878: if (IsValidPlayer(ci->client_playas)) count++; peter1138@4716: } peter1138@4716: peter1138@4716: return count; peter1138@4716: } peter1138@4716: peter1138@4716: static bool _min_players_paused = false; peter1138@4716: peter1138@4716: /* Check if the minimum number of players has been reached and pause or unpause the game as appropriate */ rubidium@6573: void CheckMinPlayers() peter1138@4716: { peter1138@4716: if (!_network_dedicated) return; peter1138@4716: richk@10991: if (NetworkCountPlayers() < _settings_client.network.min_players) { peter1138@4716: if (_min_players_paused) return; peter1138@4716: peter1138@4716: _min_players_paused = true; peter1138@4716: DoCommandP(0, 1, 0, NULL, CMD_PAUSE); richk@10991: NetworkServerSendChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game paused (not enough players)", NETWORK_SERVER_INDEX); peter1138@4716: } else { peter1138@4716: if (!_min_players_paused) return; peter1138@4716: peter1138@4716: _min_players_paused = false; peter1138@4716: DoCommandP(0, 0, 0, NULL, CMD_PAUSE); richk@10991: NetworkServerSendChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game unpaused (enough players)", NETWORK_SERVER_INDEX); peter1138@4716: } peter1138@4716: } peter1138@4716: truelight@543: // Find all IP-aliases for this host rubidium@6573: static void NetworkFindIPs() truelight@0: { truelight@6227: #if !defined(PSP) tron@4034: int i; truelight@0: truelight@543: #if defined(BEOS_NET_SERVER) /* doesn't have neither getifaddrs or net/if.h */ truelight@543: /* Based on Andrew Bachmann's netstat+.c. Big thanks to him! */ truelight@543: int _netstat(int fd, char **output, int verbose); truelight@543: truelight@543: int seek_past_header(char **pos, const char *header) { truelight@543: char *new_pos = strstr(*pos, header); truelight@543: if (new_pos == 0) { truelight@543: return B_ERROR; truelight@543: } truelight@543: *pos += strlen(header) + new_pos - *pos + 1; truelight@543: return B_OK; truelight@543: } truelight@543: truelight@543: int output_length; truelight@543: char *output_pointer = NULL; truelight@543: char **output; truelight@543: int sock = socket(AF_INET, SOCK_DGRAM, 0); truelight@543: i = 0; truelight@543: truelight@543: // If something fails, make sure the list is empty tron@4034: _broadcast_list[0] = 0; truelight@543: truelight@543: if (sock < 0) { Darkvater@5568: DEBUG(net, 0, "[core] error creating socket"); truelight@543: return; truelight@543: } truelight@543: truelight@543: output_length = _netstat(sock, &output_pointer, 1); truelight@543: if (output_length < 0) { Darkvater@5568: DEBUG(net, 0, "[core] error running _netstat"); truelight@543: return; truelight@543: } truelight@543: truelight@543: output = &output_pointer; truelight@543: if (seek_past_header(output, "IP Interfaces:") == B_OK) { truelight@543: for (;;) { truelight@543: uint32 n, fields, read; truelight@543: uint8 i1, i2, i3, i4, j1, j2, j3, j4; truelight@543: struct in_addr inaddr; tron@4034: uint32 ip; tron@4034: uint32 netmask; tron@4034: truelight@543: fields = sscanf(*output, "%u: %hhu.%hhu.%hhu.%hhu, netmask %hhu.%hhu.%hhu.%hhu%n", richk@10242: &n, &i1, &i2, &i3, &i4, &j1, &j2, &j3, &j4, &read); truelight@543: read += 1; truelight@543: if (fields != 9) { truelight@543: break; truelight@543: } tron@4034: tron@4034: ip = (uint32)i1 << 24 | (uint32)i2 << 16 | (uint32)i3 << 8 | (uint32)i4; tron@4034: netmask = (uint32)j1 << 24 | (uint32)j2 << 16 | (uint32)j3 << 8 | (uint32)j4; tron@4034: tron@4034: if (ip != INADDR_LOOPBACK && ip != INADDR_ANY) { tron@4034: inaddr.s_addr = htonl(ip | ~netmask); tron@4034: _broadcast_list[i] = inaddr.s_addr; truelight@543: i++; rubidium@6877: if (i == MAX_INTERFACES) break; truelight@543: } truelight@543: if (read < 0) { truelight@543: break; truelight@543: } truelight@543: *output += read; truelight@543: } truelight@543: /* XXX - Using either one of these crashes openttd heavily? - wber */ truelight@543: /*free(output_pointer);*/ truelight@543: /*free(output);*/ truelight@543: closesocket(sock); truelight@543: } truelight@543: #elif defined(HAVE_GETIFADDRS) truelight@543: struct ifaddrs *ifap, *ifa; truelight@543: truelight@543: // If something fails, make sure the list is empty tron@4034: _broadcast_list[0] = 0; truelight@543: truelight@543: if (getifaddrs(&ifap) != 0) truelight@0: return; truelight@0: truelight@543: i = 0; truelight@543: for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { tron@4034: if (!(ifa->ifa_flags & IFF_BROADCAST)) continue; tron@4034: if (ifa->ifa_broadaddr == NULL) continue; tron@4034: if (ifa->ifa_broadaddr->sa_family != AF_INET) continue; tron@4034: _broadcast_list[i] = ((struct sockaddr_in*)ifa->ifa_broadaddr)->sin_addr.s_addr; truelight@543: i++; rubidium@6877: if (i == MAX_INTERFACES) break; truelight@543: } truelight@543: freeifaddrs(ifap); truelight@0: truelight@543: #else /* not HAVE_GETIFADDRS */ truelight@543: SOCKET sock; tron@4033: #ifdef WIN32 tron@4033: DWORD len = 0; tron@4033: INTERFACE_INFO ifo[MAX_INTERFACES]; tron@4034: uint j; tron@4033: #else tron@4033: char buf[4 * 1024]; // Arbitrary buffer size tron@4033: struct ifconf ifconf; tron@4033: const char* buf_end; tron@4033: const char* p; truelight@543: #endif truelight@543: truelight@543: // If something fails, make sure the list is empty tron@4034: _broadcast_list[0] = 0; truelight@543: tron@1466: sock = socket(AF_INET, SOCK_DGRAM, 0); tron@1466: if (sock == INVALID_SOCKET) return; truelight@543: truelight@543: #ifdef WIN32 truelight@543: memset(&ifo[0], 0, sizeof(ifo)); truelight@543: if ((WSAIoctl(sock, SIO_GET_INTERFACE_LIST, NULL, 0, &ifo[0], sizeof(ifo), &len, NULL, NULL)) != 0) { truelight@543: closesocket(sock); truelight@543: return; truelight@543: } truelight@543: tron@4036: i = 0; tron@4034: for (j = 0; j < len / sizeof(*ifo); j++) { tron@4036: if (ifo[j].iiFlags & IFF_LOOPBACK) continue; tron@4034: if (!(ifo[j].iiFlags & IFF_BROADCAST)) continue; tron@4036: /* iiBroadcast is unusable, because it always seems to be set to tron@4036: * 255.255.255.255. tron@4036: */ tron@4036: _broadcast_list[i++] = tron@4036: ifo[j].iiAddress.AddressIn.sin_addr.s_addr | tron@4036: ~ifo[j].iiNetmask.AddressIn.sin_addr.s_addr; rubidium@6877: if (i == MAX_INTERFACES) break; tron@4033: } truelight@543: #else tron@4033: ifconf.ifc_len = sizeof(buf); tron@4033: ifconf.ifc_buf = buf; tron@4033: if (ioctl(sock, SIOCGIFCONF, &ifconf) == -1) { tron@4033: closesocket(sock); tron@4033: return; tron@4033: } tron@4033: tron@4033: i = 0; tron@4033: buf_end = buf + ifconf.ifc_len; tron@4033: for (p = buf; p < buf_end;) { tron@4033: const struct ifreq* req = (const struct ifreq*)p; tron@4033: tron@4033: if (req->ifr_addr.sa_family == AF_INET) { tron@4033: struct ifreq r; tron@4033: tron@4033: strncpy(r.ifr_name, req->ifr_name, lengthof(r.ifr_name)); tron@4037: if (ioctl(sock, SIOCGIFFLAGS, &r) != -1 && tron@4037: r.ifr_flags & IFF_BROADCAST && tron@4037: ioctl(sock, SIOCGIFBRDADDR, &r) != -1) { tron@4034: _broadcast_list[i++] = tron@4034: ((struct sockaddr_in*)&r.ifr_broadaddr)->sin_addr.s_addr; rubidium@6877: if (i == MAX_INTERFACES) break; tron@4033: } truelight@0: } truelight@0: tron@4033: p += sizeof(struct ifreq); celestar@6545: #if defined(AF_LINK) && !defined(SUNOS) tron@4033: p += req->ifr_addr.sa_len - sizeof(struct sockaddr); truelight@543: #endif truelight@0: } tron@4033: #endif truelight@0: truelight@543: closesocket(sock); truelight@543: #endif /* not HAVE_GETIFADDRS */ truelight@543: tron@4034: _broadcast_list[i] = 0; truelight@543: Darkvater@5568: DEBUG(net, 3, "Detected broadcast addresses:"); truelight@543: // Now display to the debug all the detected ips tron@4034: for (i = 0; _broadcast_list[i] != 0; i++) { Darkvater@5568: DEBUG(net, 3, "%d) %s", i, inet_ntoa(*(struct in_addr *)&_broadcast_list[i]));//inet_ntoa(inaddr)); truelight@543: } truelight@6227: #endif /* PSP */ truelight@543: } truelight@543: truelight@543: // Resolve a hostname to a inet_addr truelight@543: unsigned long NetworkResolveHost(const char *hostname) truelight@543: { truelight@543: in_addr_t ip; truelight@543: truelight@543: // First try: is it an ip address? truelight@543: ip = inet_addr(hostname); truelight@543: truelight@543: // If not try to resolve the name truelight@543: if (ip == INADDR_NONE) { truelight@6227: struct in_addr addr; truelight@6227: #if !defined(PSP) truelight@543: struct hostent *he = gethostbyname(hostname); truelight@543: if (he == NULL) { Darkvater@5568: DEBUG(net, 0, "Cannot resolve '%s'", hostname); truelight@6227: return ip; truelight@543: } truelight@6227: addr = *(struct in_addr *)he->h_addr_list[0]; truelight@6227: #else truelight@6227: int rid = -1; truelight@6227: char buf[1024]; truelight@6227: truelight@6227: /* Create a resolver */ truelight@6227: if (sceNetResolverCreate(&rid, buf, sizeof(buf)) < 0) { truelight@6227: DEBUG(net, 0, "[NET] Error connecting resolver"); truelight@6227: return ip; truelight@6227: } truelight@6227: truelight@6227: /* Try to resolve the name */ truelight@6227: if (sceNetResolverStartNtoA(rid, hostname, &addr, 2, 3) < 0) { truelight@6227: DEBUG(net, 0, "[NET] Cannot resolve %s", hostname); truelight@6227: sceNetResolverDelete(rid); truelight@6227: return ip; truelight@6227: } truelight@6227: sceNetResolverDelete(rid); truelight@6227: #endif /* PSP */ truelight@6227: truelight@6227: DEBUG(net, 1, "[NET] Resolved %s to %s", hostname, inet_ntoa(addr)); truelight@6227: ip = addr.s_addr; truelight@543: } truelight@543: return ip; truelight@543: } truelight@543: Darkvater@5955: /** Converts a string to ip/port/player Darkvater@5955: * Format: IP#player:port Darkvater@5955: * Darkvater@5955: * connection_string will be re-terminated to seperate out the hostname, and player and port will Darkvater@5955: * be set to the player and port strings given by the user, inside the memory area originally Darkvater@5955: * occupied by connection_string. */ tron@1329: void ParseConnectionString(const char **player, const char **port, char *connection_string) truelight@543: { tron@1329: char *p; truelight@543: for (p = connection_string; *p != '\0'; p++) { Darkvater@5955: switch (*p) { Darkvater@5955: case '#': Darkvater@5955: *player = p + 1; Darkvater@5955: *p = '\0'; Darkvater@5955: break; Darkvater@5955: case ':': Darkvater@5955: *port = p + 1; Darkvater@5955: *p = '\0'; Darkvater@5955: break; truelight@0: } truelight@0: } truelight@0: } truelight@0: truelight@543: // Creates a new client from a socket truelight@543: // Used both by the server and the client rubidium@5875: static NetworkTCPSocketHandler *NetworkAllocClient(SOCKET s) truelight@0: { rubidium@5875: NetworkTCPSocketHandler *cs; Darkvater@4880: byte client_no = 0; truelight@0: truelight@543: if (_network_server) { truelight@543: // Can we handle a new client? Darkvater@4880: if (_network_clients_connected >= MAX_CLIENTS) return NULL; richk@10991: if (_network_game_info.clients_on >= _settings_client.network.max_clients) return NULL; signde@206: truelight@543: // Register the login truelight@543: client_no = _network_clients_connected++; truelight@543: } truelight@543: Darkvater@4880: cs = DEREF_CLIENT(client_no); rubidium@5875: cs->Initialize(); rubidium@5875: cs->sock = s; truelight@1602: cs->last_frame = _frame_counter; truelight@1602: cs->last_frame_server = _frame_counter; truelight@1602: truelight@543: if (_network_server) { Darkvater@4880: NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs); truelight@543: memset(ci, 0, sizeof(*ci)); truelight@543: truelight@543: cs->index = _network_client_index++; truelight@543: ci->client_index = cs->index; Darkvater@4878: ci->client_playas = PLAYER_INACTIVE_CLIENT; truelight@543: ci->join_date = _date; truelight@543: truelight@543: InvalidateWindow(WC_CLIENT_LIST, 0); truelight@543: } truelight@543: truelight@543: return cs; signde@206: } signde@206: truelight@543: // Close a connection rubidium@5875: void NetworkCloseClient(NetworkTCPSocketHandler *cs) truelight@0: { truelight@543: NetworkClientInfo *ci; truelight@543: // Socket is already dead rubidium@5875: if (cs->sock == INVALID_SOCKET) { Darkvater@4880: cs->has_quit = true; truelight@903: return; truelight@903: } truelight@260: Darkvater@5568: DEBUG(net, 1, "Closed client connection %d", cs->index); truelight@0: Darkvater@4880: if (!cs->has_quit && _network_server && cs->status > STATUS_INACTIVE) { truelight@543: // We did not receive a leave message from this client... truelight@543: NetworkErrorCode errorno = NETWORK_ERROR_CONNECTION_LOST; truelight@722: char str[100]; Darkvater@3623: char client_name[NETWORK_CLIENT_NAME_LENGTH]; rubidium@5875: NetworkTCPSocketHandler *new_cs; truelight@0: truelight@543: NetworkGetClientName(client_name, sizeof(client_name), cs); truelight@0: Darkvater@4912: GetNetworkErrorMsg(str, errorno, lastof(str)); truelight@0: richk@10731: NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, "%s", str); truelight@543: truelight@543: // Inform other clients of this... strange leaving ;) truelight@543: FOR_ALL_CLIENTS(new_cs) { truelight@543: if (new_cs->status > STATUS_AUTH && cs != new_cs) { truelight@543: SEND_COMMAND(PACKET_SERVER_ERROR_QUIT)(new_cs, cs->index, errorno); truelight@0: } truelight@0: } truelight@1604: } truelight@1602: truelight@1604: /* When the client was PRE_ACTIVE, the server was in pause mode, so unpause */ richk@10991: if (cs->status == STATUS_PRE_ACTIVE && _settings_client.network.pause_on_join) { truelight@1604: DoCommandP(0, 0, 0, NULL, CMD_PAUSE); richk@10991: NetworkServerSendChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game unpaused", NETWORK_SERVER_INDEX); truelight@0: } truelight@0: rubidium@6153: cs->Destroy(); truelight@0: truelight@543: // Close the gap in the client-list truelight@543: ci = DEREF_CLIENT_INFO(cs); truelight@0: truelight@543: if (_network_server) { truelight@543: // We just lost one client :( rubidium@6673: if (cs->status >= STATUS_AUTH) _network_game_info.clients_on--; truelight@543: _network_clients_connected--; truelight@543: rubidium@5875: while ((cs + 1) != DEREF_CLIENT(MAX_CLIENTS) && (cs + 1)->sock != INVALID_SOCKET) { truelight@543: *cs = *(cs + 1); truelight@543: *ci = *(ci + 1); truelight@543: cs++; truelight@543: ci++; truelight@0: } truelight@0: truelight@543: InvalidateWindow(WC_CLIENT_LIST, 0); truelight@0: } truelight@0: truelight@543: // Reset the status of the last socket rubidium@5875: cs->sock = INVALID_SOCKET; truelight@543: cs->status = STATUS_INACTIVE; truelight@543: cs->index = NETWORK_EMPTY_INDEX; truelight@543: ci->client_index = NETWORK_EMPTY_INDEX; peter1138@4716: peter1138@4716: CheckMinPlayers(); truelight@0: } truelight@0: truelight@543: // A client wants to connect to a server truelight@716: static bool NetworkConnect(const char *hostname, int port) truelight@543: { truelight@543: SOCKET s; truelight@543: struct sockaddr_in sin; truelight@0: Darkvater@5568: DEBUG(net, 1, "Connecting to %s %d", hostname, port); truelight@0: truelight@543: s = socket(AF_INET, SOCK_STREAM, 0); truelight@543: if (s == INVALID_SOCKET) { truelight@543: ClientStartError("socket() failed"); truelight@543: return false; truelight@0: } truelight@0: Darkvater@5568: if (!SetNoDelay(s)) DEBUG(net, 1, "Setting TCP_NODELAY failed"); truelight@543: truelight@543: sin.sin_family = AF_INET; truelight@543: sin.sin_addr.s_addr = NetworkResolveHost(hostname); truelight@543: sin.sin_port = htons(port); truelight@543: _network_last_host_ip = sin.sin_addr.s_addr; truelight@543: Darkvater@4880: /* We failed to connect for which reason what so ever */ Darkvater@4880: if (connect(s, (struct sockaddr*) &sin, sizeof(sin)) != 0) return false; truelight@543: Darkvater@5568: if (!SetNonBlocking(s)) DEBUG(net, 0, "Setting non-blocking mode failed"); // XXX should this be an error? truelight@543: truelight@543: // in client mode, only the first client field is used. it's pointing to the server. truelight@716: NetworkAllocClient(s); truelight@543: Darkvater@5683: _network_join_status = NETWORK_JOIN_STATUS_CONNECTING; truelight@543: ShowJoinStatusWindow(); truelight@543: truelight@0: return true; truelight@0: } truelight@0: truelight@543: // For the server, to accept new clients rubidium@6573: static void NetworkAcceptClients() truelight@0: { truelight@0: struct sockaddr_in sin; rubidium@5875: NetworkTCPSocketHandler *cs; tron@959: uint i; truelight@841: bool banned; truelight@0: truelight@543: // Should never ever happen.. is it possible?? truelight@0: assert(_listensocket != INVALID_SOCKET); truelight@0: truelight@543: for (;;) { Darkvater@4880: socklen_t sin_len = sizeof(sin); Darkvater@4880: SOCKET s = accept(_listensocket, (struct sockaddr*)&sin, &sin_len); truelight@0: if (s == INVALID_SOCKET) return; truelight@0: tron@1332: SetNonBlocking(s); // XXX error handling? truelight@0: Darkvater@5568: DEBUG(net, 1, "Client connected from %s on frame %d", inet_ntoa(sin.sin_addr), _frame_counter); truelight@0: tron@1332: SetNoDelay(s); // XXX error handling? truelight@0: truelight@841: /* Check if the client is banned */ truelight@841: banned = false; truelight@841: for (i = 0; i < lengthof(_network_ban_list); i++) { Darkvater@4880: if (_network_ban_list[i] == NULL) continue; truelight@841: truelight@841: if (sin.sin_addr.s_addr == inet_addr(_network_ban_list[i])) { rubidium@6149: Packet p(PACKET_SERVER_BANNED); rubidium@6149: p.PrepareToSend(); truelight@841: Darkvater@5568: DEBUG(net, 1, "Banned ip tried to join (%s), refused", _network_ban_list[i]); truelight@841: rubidium@6149: send(s, (const char*)p.buffer, p.size, 0); truelight@841: closesocket(s); truelight@841: truelight@841: banned = true; truelight@841: break; truelight@841: } truelight@841: } truelight@841: /* If this client is banned, continue with next client */ Darkvater@4880: if (banned) continue; truelight@841: truelight@716: cs = NetworkAllocClient(s); truelight@0: if (cs == NULL) { truelight@0: // no more clients allowed? truelight@543: // Send to the client that we are full! rubidium@6149: Packet p(PACKET_SERVER_FULL); rubidium@6149: p.PrepareToSend(); truelight@543: rubidium@6149: send(s, (const char*)p.buffer, p.size, 0); truelight@0: closesocket(s); truelight@543: truelight@0: continue; truelight@0: } truelight@0: truelight@543: // a new client has connected. We set him at inactive for now truelight@543: // maybe he is only requesting server-info. Till he has sent a PACKET_CLIENT_MAP_OK truelight@543: // the client stays inactive truelight@543: cs->status = STATUS_INACTIVE; truelight@543: Darkvater@4880: DEREF_CLIENT_INFO(cs)->client_ip = sin.sin_addr.s_addr; // Save the IP of the client truelight@0: } truelight@0: } truelight@0: truelight@543: // Set up the listen socket for the server rubidium@6573: static bool NetworkListen() darkvater@211: { dominik@105: SOCKET ls; dominik@105: struct sockaddr_in sin; dominik@105: richk@10991: DEBUG(net, 1, "Listening on %s:%d", _settings_client.network.server_bind_ip, _settings_client.network.server_port); dominik@105: dominik@105: ls = socket(AF_INET, SOCK_STREAM, 0); truelight@543: if (ls == INVALID_SOCKET) { truelight@543: ServerStartError("socket() on listen socket failed"); truelight@543: return false; dominik@105: } dominik@105: truelight@543: { // reuse the socket truelight@543: int reuse = 1; truelight@543: // The (const char*) cast is needed for windows!! truelight@543: if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) == -1) { truelight@543: ServerStartError("setsockopt() on listen socket failed"); truelight@543: return false; truelight@543: } truelight@543: } truelight@543: Darkvater@5568: if (!SetNonBlocking(ls)) DEBUG(net, 0, "Setting non-blocking mode failed"); // XXX should this be an error? dominik@105: dominik@105: sin.sin_family = AF_INET; truelight@629: sin.sin_addr.s_addr = _network_server_bind_ip; richk@10991: sin.sin_port = htons(_settings_client.network.server_port); dominik@105: truelight@543: if (bind(ls, (struct sockaddr*)&sin, sizeof(sin)) != 0) { truelight@543: ServerStartError("bind() failed"); truelight@543: return false; truelight@543: } dominik@105: truelight@543: if (listen(ls, 1) != 0) { truelight@543: ServerStartError("listen() failed"); truelight@543: return false; truelight@543: } dominik@105: dominik@105: _listensocket = ls; truelight@543: truelight@543: return true; dominik@105: } dominik@105: truelight@543: // Close all current connections rubidium@6573: static void NetworkClose() truelight@543: { rubidium@5875: NetworkTCPSocketHandler *cs; truelight@543: truelight@543: FOR_ALL_CLIENTS(cs) { truelight@543: if (!_network_server) { truelight@543: SEND_COMMAND(PACKET_CLIENT_QUIT)("leaving"); rubidium@6153: cs->Send_Packets(); truelight@543: } truelight@716: NetworkCloseClient(cs); truelight@543: } truelight@543: truelight@543: if (_network_server) { richk@10991: /* We are a server, also close the listensocket */ truelight@543: closesocket(_listensocket); truelight@543: _listensocket = INVALID_SOCKET; Darkvater@5568: DEBUG(net, 1, "Closed listener"); truelight@543: } richk@10991: NetworkUDPCloseAll(); richk@10991: richk@10991: /* Free all queued commands */ richk@10991: while (_local_command_queue != NULL) { richk@10991: CommandPacket *p = _local_command_queue; richk@10991: _local_command_queue = _local_command_queue->next; richk@10991: free(p); richk@10991: } richk@10991: richk@10991: _networking = false; richk@10991: _network_server = false; truelight@543: } truelight@543: truelight@543: // Inits the network (cleans sockets and stuff) rubidium@6573: static void NetworkInitialize() truelight@543: { rubidium@5875: NetworkTCPSocketHandler *cs; truelight@543: truelight@543: _local_command_queue = NULL; truelight@543: truelight@543: // Clean all client-sockets truelight@543: for (cs = _clients; cs != &_clients[MAX_CLIENTS]; cs++) { rubidium@5875: cs->Initialize(); truelight@543: } truelight@543: truelight@543: // Clean the client_info memory Darkvater@2879: memset(&_network_client_info, 0, sizeof(_network_client_info)); Darkvater@2879: memset(&_network_player_info, 0, sizeof(_network_player_info)); truelight@543: truelight@543: _sync_frame = 0; truelight@543: _network_first_time = true; truelight@543: truelight@543: _network_reconnect = 0; truelight@543: truelight@543: NetworkUDPInitialize(); truelight@543: } truelight@543: truelight@543: // Query a server to fetch his game-info truelight@543: // If game_info is true, only the gameinfo is fetched, truelight@543: // else only the client_info is fetched rubidium@6167: void NetworkTCPQueryServer(const char* host, unsigned short port) truelight@543: { rubidium@6167: if (!_network_available) return; truelight@543: truelight@543: NetworkDisconnect(); truelight@543: truelight@543: NetworkInitialize(); truelight@543: truelight@543: _network_server = false; truelight@543: truelight@543: // Try to connect truelight@543: _networking = NetworkConnect(host, port); truelight@543: truelight@543: // We are connected truelight@543: if (_networking) { truelight@543: SEND_COMMAND(PACKET_CLIENT_COMPANY_INFO)(); Darkvater@4880: } else { // No networking, close everything down again Darkvater@4880: NetworkDisconnect(); truelight@543: } truelight@543: } truelight@543: dominik@738: /* Validates an address entered as a string and adds the server to Darkvater@2881: * the list. If you use this function, the games will be marked dominik@738: * as manually added. */ tron@1329: void NetworkAddServer(const char *b) dominik@710: { dominik@710: if (*b != '\0') { tron@1329: const char *port = NULL; tron@1329: const char *player = NULL; tron@1329: char host[NETWORK_HOSTNAME_LENGTH]; dominik@710: uint16 rport; dominik@710: truelight@716: ttd_strlcpy(host, b, lengthof(host)); truelight@716: richk@10991: ttd_strlcpy(_settings_client.network.connect_to_ip, b, lengthof(_settings_client.network.connect_to_ip)); dominik@710: rport = NETWORK_DEFAULT_PORT; dominik@710: truelight@716: ParseConnectionString(&player, &port, host); dominik@710: if (port != NULL) rport = atoi(port); dominik@710: rubidium@6167: NetworkUDPQueryServer(host, rport, true); dominik@738: } dominik@738: } dominik@738: truelight@746: /* Generates the list of manually added hosts from NetworkGameList and dominik@738: * dumps them into the array _network_host_list. This array is needed dominik@738: * by the function that generates the config file. */ rubidium@6573: void NetworkRebuildHostList() dominik@738: { tron@959: uint i = 0; Darkvater@4880: const NetworkGameList *item = _network_game_list; truelight@841: while (item != NULL && i != lengthof(_network_host_list)) { Darkvater@2915: if (item->manually) { Darkvater@2915: free(_network_host_list[i]); dominik@895: _network_host_list[i++] = str_fmt("%s:%i", item->info.hostname, item->port); Darkvater@2915: } dominik@738: item = item->next; dominik@738: } dominik@738: truelight@841: for (; i < lengthof(_network_host_list); i++) { Darkvater@2915: free(_network_host_list[i]); Darkvater@2915: _network_host_list[i] = NULL; dominik@710: } dominik@710: } dominik@710: truelight@543: // Used by clients, to connect to a server Darkvater@4880: bool NetworkClientConnectGame(const char *host, uint16 port) truelight@543: { truelight@543: if (!_network_available) return false; truelight@543: truelight@543: if (port == 0) return false; truelight@543: richk@10991: ttd_strlcpy(_settings_client.network.last_host, host, sizeof(_settings_client.network.last_host)); richk@10991: _settings_client.network.last_port = port; truelight@543: truelight@543: NetworkDisconnect(); truelight@543: NetworkInitialize(); truelight@543: truelight@543: // Try to connect truelight@543: _networking = NetworkConnect(host, port); truelight@543: truelight@543: // We are connected truelight@543: if (_networking) { truelight@543: IConsoleCmdExec("exec scripts/on_client.scr 0"); truelight@543: NetworkClient_Connected(); truelight@543: } else { truelight@543: // Connecting failed truelight@543: NetworkError(STR_NETWORK_ERR_NOCONNECTION); truelight@543: } truelight@543: truelight@543: return _networking; truelight@543: } truelight@543: rubidium@6573: static void NetworkInitGameInfo() truelight@543: { truelight@543: NetworkClientInfo *ci; truelight@543: richk@10991: if (StrEmpty(_settings_client.network.server_name)) { richk@10991: snprintf(_settings_client.network.server_name, sizeof(_settings_client.network.server_name), "Unnamed Server"); truelight@543: } Darkvater@2879: richk@10991: /* The server is a client too */ richk@10991: _network_game_info.clients_on = _network_dedicated ? 0 : 1; richk@10991: _network_game_info.start_date = ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1); truelight@543: truelight@543: // We use _network_client_info[MAX_CLIENT_INFO - 1] to store the server-data in it truelight@543: // The index is NETWORK_SERVER_INDEX ( = 1) truelight@543: ci = &_network_client_info[MAX_CLIENT_INFO - 1]; truelight@543: memset(ci, 0, sizeof(*ci)); truelight@543: truelight@543: ci->client_index = NETWORK_SERVER_INDEX; Darkvater@4878: ci->client_playas = _network_dedicated ? PLAYER_SPECTATOR : _local_player; Darkvater@4878: richk@10991: ttd_strlcpy(ci->client_name, _settings_client.network.player_name, sizeof(ci->client_name)); richk@10991: ttd_strlcpy(ci->unique_id, _settings_client.network.network_id, sizeof(ci->unique_id)); truelight@543: } truelight@543: rubidium@6573: bool NetworkServerStart() truelight@543: { truelight@543: if (!_network_available) return false; truelight@543: truelight@629: /* Call the pre-scripts */ truelight@629: IConsoleCmdExec("exec scripts/pre_server.scr 0"); truelight@629: if (_network_dedicated) IConsoleCmdExec("exec scripts/pre_dedicated.scr 0"); truelight@629: truelight@543: NetworkInitialize(); Darkvater@4880: if (!NetworkListen()) return false; truelight@543: truelight@543: // Try to start UDP-server truelight@543: _network_udp_server = true; richk@10991: _network_udp_server = _udp_server_socket->Listen(_network_server_bind_ip, _settings_client.network.server_port, false); truelight@543: truelight@543: _network_server = true; truelight@543: _networking = true; truelight@543: _frame_counter = 0; truelight@543: _frame_counter_server = 0; truelight@543: _frame_counter_max = 0; ludde@2079: _last_sync_frame = 0; truelight@543: _network_own_client_index = NETWORK_SERVER_INDEX; truelight@543: Darkvater@4878: /* Non-dedicated server will always be player #1 */ rubidium@5838: if (!_network_dedicated) _network_playas = PLAYER_FIRST; truelight@683: truelight@543: _network_clients_connected = 0; truelight@543: truelight@543: NetworkInitGameInfo(); truelight@543: truelight@543: // execute server initialization script truelight@543: IConsoleCmdExec("exec scripts/on_server.scr 0"); truelight@543: // if the server is dedicated ... add some other script truelight@543: if (_network_dedicated) IConsoleCmdExec("exec scripts/on_dedicated.scr 0"); truelight@668: peter1138@4716: _min_players_paused = false; peter1138@4716: CheckMinPlayers(); peter1138@4716: truelight@668: /* Try to register us to the master server */ peter1138@2861: _network_last_advertise_frame = 0; peter1138@2861: _network_need_advertise = true; truelight@668: NetworkUDPAdvertise(); truelight@543: return true; truelight@543: } truelight@543: truelight@543: // The server is rebooting... truelight@543: // The only difference with NetworkDisconnect, is the packets that is sent rubidium@6573: void NetworkReboot() truelight@543: { truelight@543: if (_network_server) { rubidium@5875: NetworkTCPSocketHandler *cs; truelight@543: FOR_ALL_CLIENTS(cs) { truelight@543: SEND_COMMAND(PACKET_SERVER_NEWGAME)(cs); rubidium@6153: cs->Send_Packets(); truelight@543: } truelight@543: } truelight@543: truelight@543: NetworkClose(); truelight@543: } truelight@543: truelight@543: // We want to disconnect from the host/clients rubidium@6573: void NetworkDisconnect() truelight@543: { truelight@543: if (_network_server) { rubidium@5875: NetworkTCPSocketHandler *cs; truelight@543: FOR_ALL_CLIENTS(cs) { truelight@543: SEND_COMMAND(PACKET_SERVER_SHUTDOWN)(cs); rubidium@6153: cs->Send_Packets(); truelight@543: } truelight@543: } truelight@543: richk@10994: if (_settings_client.network.server_advertise) NetworkUDPRemoveAdvertise(); truelight@765: truelight@598: DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0); truelight@598: truelight@543: NetworkClose(); truelight@543: } truelight@543: truelight@543: // Receives something from the network rubidium@6573: static bool NetworkReceive() truelight@0: { rubidium@5875: NetworkTCPSocketHandler *cs; truelight@0: int n; truelight@0: fd_set read_fd, write_fd; truelight@0: struct timeval tv; truelight@193: truelight@0: FD_ZERO(&read_fd); truelight@0: FD_ZERO(&write_fd); truelight@0: truelight@543: FOR_ALL_CLIENTS(cs) { rubidium@5875: FD_SET(cs->sock, &read_fd); rubidium@5875: FD_SET(cs->sock, &write_fd); truelight@0: } truelight@0: truelight@0: // take care of listener port Darkvater@4880: if (_network_server) FD_SET(_listensocket, &read_fd); truelight@0: truelight@0: tv.tv_sec = tv.tv_usec = 0; // don't block at all. truelight@0: #if !defined(__MORPHOS__) && !defined(__AMIGA__) truelight@0: n = select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv); truelight@0: #else truelight@0: n = WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL); truelight@0: #endif truelight@543: if (n == -1 && !_network_server) NetworkError(STR_NETWORK_ERR_LOSTCONNECTION); truelight@0: truelight@0: // accept clients.. Darkvater@4880: if (_network_server && FD_ISSET(_listensocket, &read_fd)) NetworkAcceptClients(); truelight@0: truelight@0: // read stuff from clients truelight@543: FOR_ALL_CLIENTS(cs) { rubidium@5875: cs->writable = !!FD_ISSET(cs->sock, &write_fd); rubidium@5875: if (FD_ISSET(cs->sock, &read_fd)) { tron@4077: if (_network_server) { truelight@543: NetworkServer_ReadPackets(cs); tron@4077: } else { Darkvater@4880: NetworkRecvStatus res; tron@4077: truelight@543: // The client already was quiting! Darkvater@4880: if (cs->has_quit) return false; tron@4077: tron@4077: res = NetworkClient_ReadPackets(cs); tron@4077: if (res != NETWORK_RECV_STATUS_OKAY) { truelight@543: // The client made an error of which we can not recover truelight@543: // close the client and drop back to main menu truelight@543: NetworkClientError(res, cs); truelight@543: return false; truelight@543: } truelight@543: } truelight@0: } truelight@0: } truelight@543: return true; truelight@543: } truelight@0: truelight@543: // This sends all buffered commands (if possible) rubidium@6573: static void NetworkSend() truelight@543: { rubidium@5875: NetworkTCPSocketHandler *cs; truelight@543: FOR_ALL_CLIENTS(cs) { truelight@543: if (cs->writable) { rubidium@6153: cs->Send_Packets(); truelight@0: truelight@543: if (cs->status == STATUS_MAP) { truelight@543: // This client is in the middle of a map-send, call the function for that truelight@543: SEND_COMMAND(PACKET_SERVER_MAP)(cs); truelight@0: } truelight@0: } truelight@0: } truelight@0: } truelight@0: truelight@543: // Handle the local-command-queue rubidium@6573: static void NetworkHandleLocalQueue() truelight@0: { ludde@2080: CommandPacket *cp, **cp_prev; truelight@543: ludde@2080: cp_prev = &_local_command_queue; truelight@543: ludde@2080: while ( (cp = *cp_prev) != NULL) { ludde@2080: ludde@2080: // The queue is always in order, which means ludde@2080: // that the first element will be executed first. Darkvater@4880: if (_frame_counter < cp->frame) break; ludde@2080: ludde@2080: if (_frame_counter > cp->frame) { ludde@2080: // If we reach here, it means for whatever reason, we've already executed ludde@2080: // past the command we need to execute. rubidium@6872: error("[net] Trying to execute a packet in the past!"); truelight@0: } ludde@2080: ludde@2080: // We can execute this command ludde@2080: NetworkExecuteCommand(cp); ludde@2080: ludde@2080: *cp_prev = cp->next; ludde@2080: free(cp); truelight@0: } ludde@2080: ludde@2080: // Just a safety check, to be removed in the future. ludde@2080: // Make sure that no older command appears towards the end of the queue ludde@2080: // In that case we missed executing it. This will never happen. tron@2952: for (cp = _local_command_queue; cp; cp = cp->next) { ludde@2080: assert(_frame_counter < cp->frame); ludde@2080: } ludde@2080: truelight@0: } truelight@0: rubidium@6573: static bool NetworkDoClientLoop() truelight@0: { truelight@543: _frame_counter++; truelight@0: truelight@543: NetworkHandleLocalQueue(); darkvater@211: truelight@543: StateGameLoop(); truelight@543: truelight@543: // Check if we are in sync! truelight@543: if (_sync_frame != 0) { truelight@543: if (_sync_frame == _frame_counter) { truelight@543: #ifdef NETWORK_SEND_DOUBLE_SEED rubidium@6877: if (_sync_seed_1 != _random.state[0] || _sync_seed_2 != _random.state[1]) { truelight@543: #else rubidium@6877: if (_sync_seed_1 != _random.state[0]) { truelight@543: #endif truelight@543: NetworkError(STR_NETWORK_ERR_DESYNC); rubidium@6872: DebugDumpCommands("ddc:serr:%d;%d\n", _date, _date_fract); Darkvater@5568: DEBUG(net, 0, "Sync error detected!"); truelight@543: NetworkClientError(NETWORK_RECV_STATUS_DESYNC, DEREF_CLIENT(0)); truelight@543: return false; truelight@543: } truelight@543: truelight@543: // If this is the first time we have a sync-frame, we truelight@543: // need to let the server know that we are ready and at the same truelight@543: // frame as he is.. so we can start playing! truelight@543: if (_network_first_time) { truelight@543: _network_first_time = false; truelight@543: SEND_COMMAND(PACKET_CLIENT_ACK)(); truelight@543: } truelight@543: truelight@543: _sync_frame = 0; truelight@543: } else if (_sync_frame < _frame_counter) { Darkvater@5568: DEBUG(net, 1, "Missed frame for sync-test (%d / %d)", _sync_frame, _frame_counter); truelight@543: _sync_frame = 0; dominik@105: } truelight@543: } truelight@543: truelight@543: return true; truelight@0: } truelight@0: truelight@543: // We have to do some UDP checking rubidium@6573: void NetworkUDPGameLoop() darkvater@211: { truelight@764: if (_network_udp_server) { rubidium@5870: _udp_server_socket->ReceivePackets(); rubidium@5870: _udp_master_socket->ReceivePackets(); rubidium@5870: } else { rubidium@5870: _udp_client_socket->ReceivePackets(); tron@4000: if (_network_udp_broadcast > 0) _network_udp_broadcast--; rubidium@6148: NetworkGameListRequery(); truelight@0: } truelight@0: } truelight@0: truelight@543: // The main loop called from ttd.c truelight@543: // Here we also have to do StateGameLoop if needed! rubidium@6573: void NetworkGameLoop() darkvater@211: { truelight@543: if (!_networking) return; dominik@105: truelight@543: if (!NetworkReceive()) return; truelight@543: truelight@543: if (_network_server) { richk@6720: #ifdef DEBUG_DUMP_COMMANDS richk@6720: static FILE *f = FioFOpenFile("commands.log", "rb", SAVE_DIR); richk@6720: static Date next_date = 0; richk@6720: static uint32 next_date_fract; richk@6720: static CommandPacket *cp = NULL; richk@6720: if (f == NULL && next_date == 0) { richk@6720: printf("Cannot open commands.log\n"); richk@6720: next_date = 1; richk@6720: } richk@6720: richk@6720: while (f != NULL && !feof(f)) { richk@6720: if (cp != NULL && _date == next_date && _date_fract == next_date_fract) { richk@6720: _current_player = cp->player; richk@6720: _cmd_text = cp->text; richk@6720: DoCommandP(cp->tile, cp->p1, cp->p2, NULL, cp->cmd); richk@6720: free(cp); richk@6720: cp = NULL; richk@6720: } richk@6720: richk@6720: if (cp != NULL) break; richk@6720: richk@6720: char buff[4096]; richk@6720: if (fgets(buff, lengthof(buff), f) == NULL) break; richk@6720: if (strncmp(buff, "ddc:cmd:", 8) != 0) continue; richk@6720: cp = MallocT(1); richk@6720: int player; richk@6720: 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); richk@6720: cp->player = (Owner)player; richk@6720: } rubidium@6872: #endif /* DEBUG_DUMP_COMMANDS */ richk@6720: ludde@2235: bool send_frame = false; ludde@2235: truelight@543: // We first increase the _frame_counter truelight@543: _frame_counter++; ludde@2235: // Update max-frame-counter ludde@2235: if (_frame_counter > _frame_counter_max) { richk@10991: _frame_counter_max = _frame_counter + _settings_client.network.frame_freq; ludde@2235: send_frame = true; ludde@2235: } truelight@543: truelight@543: NetworkHandleLocalQueue(); truelight@543: truelight@543: // Then we make the frame truelight@543: StateGameLoop(); truelight@543: rubidium@6877: _sync_seed_1 = _random.state[0]; truelight@543: #ifdef NETWORK_SEND_DOUBLE_SEED rubidium@6877: _sync_seed_2 = _random.state[1]; truelight@543: #endif truelight@543: ludde@2235: NetworkServer_Tick(send_frame); truelight@543: } else { truelight@543: // Client truelight@543: truelight@543: // Make sure we are at the frame were the server is (quick-frames) truelight@543: if (_frame_counter_server > _frame_counter) { truelight@543: while (_frame_counter_server > _frame_counter) { truelight@543: if (!NetworkDoClientLoop()) break; truelight@543: } truelight@543: } else { truelight@543: // Else, keep on going till _frame_counter_max Darkvater@4880: if (_frame_counter_max > _frame_counter) NetworkDoClientLoop(); truelight@543: } truelight@0: } truelight@193: truelight@543: NetworkSend(); truelight@0: } truelight@0: rubidium@6573: static void NetworkGenerateUniqueId() truelight@602: { rubidium@6872: Md5 checksum; rubidium@6872: uint8 digest[16]; truelight@602: char hex_output[16*2 + 1]; truelight@602: char coding_string[NETWORK_NAME_LENGTH]; truelight@602: int di; truelight@602: truelight@607: snprintf(coding_string, sizeof(coding_string), "%d%s", (uint)Random(), "OpenTTD Unique ID"); truelight@602: truelight@602: /* Generate the MD5 hash */ rubidium@6872: checksum.Append((const uint8*)coding_string, strlen(coding_string)); rubidium@6872: checksum.Finish(digest); truelight@602: truelight@602: for (di = 0; di < 16; ++di) truelight@602: sprintf(hex_output + di * 2, "%02x", digest[di]); truelight@602: truelight@602: /* _network_unique_id is our id */ richk@10991: snprintf(_settings_client.network.network_id, sizeof(_settings_client.network.network_id), "%s", hex_output); truelight@602: } truelight@602: truelight@6210: void NetworkStartDebugLog(const char *hostname, uint16 port) truelight@6210: { truelight@6210: extern SOCKET _debug_socket; // Comes from debug.c truelight@6210: SOCKET s; truelight@6210: struct sockaddr_in sin; truelight@6210: truelight@6210: DEBUG(net, 0, "Redirecting DEBUG() to %s:%d", hostname, port); truelight@6210: truelight@6210: s = socket(AF_INET, SOCK_STREAM, 0); truelight@6210: if (s == INVALID_SOCKET) { truelight@6210: DEBUG(net, 0, "Failed to open socket for redirection DEBUG()"); truelight@6210: return; truelight@6210: } truelight@6210: truelight@6210: if (!SetNoDelay(s)) DEBUG(net, 1, "Setting TCP_NODELAY failed"); truelight@6210: truelight@6210: sin.sin_family = AF_INET; truelight@6210: sin.sin_addr.s_addr = NetworkResolveHost(hostname); truelight@6210: sin.sin_port = htons(port); truelight@6210: truelight@6210: if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) != 0) { truelight@6210: DEBUG(net, 0, "Failed to redirection DEBUG() to %s:%d", hostname, port); truelight@6210: return; truelight@6210: } truelight@6210: truelight@6210: if (!SetNonBlocking(s)) DEBUG(net, 0, "Setting non-blocking mode failed"); truelight@6210: _debug_socket = s; truelight@6210: truelight@6210: DEBUG(net, 0, "DEBUG() is now redirected"); truelight@6210: } truelight@6210: rubidium@5775: /** This tries to launch the network for a given OS */ rubidium@6573: void NetworkStartUp() darkvater@211: { Darkvater@5568: DEBUG(net, 3, "[core] starting network..."); bjarni@770: rubidium@5775: /* Network is available */ rubidium@5775: _network_available = NetworkCoreInitialize();; truelight@543: _network_dedicated = false; peter1138@2861: _network_last_advertise_frame = 0; peter1138@2861: _network_need_advertise = true; truelight@764: _network_advertise_retries = 0; dominik@105: truelight@629: /* Load the ip from the openttd.cfg */ richk@10991: _network_server_bind_ip = inet_addr(_settings_client.network.server_bind_ip); truelight@629: /* And put the data back in it in case it was an invalid ip */ richk@10991: snprintf(_settings_client.network.server_bind_ip, sizeof(_settings_client.network.server_bind_ip), "%s", inet_ntoa(*(struct in_addr *)&_network_server_bind_ip)); truelight@629: truelight@602: /* Generate an unique id when there is none yet */ richk@10991: if (StrEmpty(_settings_client.network.network_id)) NetworkGenerateUniqueId(); truelight@602: richk@10991: memset(&_network_game_info, 0, sizeof(_network_game_info)); darkvater@211: truelight@543: NetworkInitialize(); Darkvater@5568: DEBUG(net, 3, "[core] network online, multiplayer available"); truelight@543: NetworkFindIPs(); darkvater@211: } darkvater@211: rubidium@5775: /** This shuts the network down */ rubidium@6573: void NetworkShutDown() truelight@543: { Darkvater@4829: NetworkDisconnect(); rubidium@5870: NetworkUDPShutdown(); Darkvater@4829: Darkvater@5568: DEBUG(net, 3, "[core] shutting down network"); darkvater@211: truelight@543: _network_available = false; darkvater@211: rubidium@5774: NetworkCoreShutdown(); dominik@105: } dominik@105: rubidium@6504: /** rubidium@6504: * Checks whether the given version string is compatible with our version. rubidium@6504: * @param other the version string to compare to rubidium@6504: */ rubidium@6504: bool IsNetworkCompatibleVersion(const char *other) rubidium@6504: { richk@6720: return strncmp(_openttd_revision, other, NETWORK_REVISION_LENGTH - 1) == 0; rubidium@6504: } rubidium@6504: rubidium@6872: #endif /* ENABLE_NETWORK */ richk@6720: rubidium@6872: /* NOTE: this variable needs to be always available */ rubidium@6872: PlayerID _network_playas;