tron@2186: /* $Id$ */ tron@2186: truelight@0: #include "stdafx.h" truelight@543: #include "network_data.h" truelight@0: darkvater@663: #if defined(WITH_REV) darkvater@663: extern const char _openttd_revision[]; darkvater@663: #elif defined(WITH_REV_HACK) darkvater@663: #define WITH_REV darkvater@663: const char _openttd_revision[] = WITH_REV_HACK; darkvater@663: #else darkvater@663: const char _openttd_revision[] = NOREV_STRING; darkvater@663: #endif darkvater@663: darkvater@663: truelight@543: #ifdef ENABLE_NETWORK truelight@0: Darkvater@4826: #include "openttd.h" Darkvater@4826: #include "debug.h" Darkvater@4826: #include "functions.h" Darkvater@4826: #include "string.h" Darkvater@4826: #include "strings.h" Darkvater@4826: #include "map.h" Darkvater@4826: #include "command.h" Darkvater@4826: #include "variables.h" Darkvater@4826: #include "date.h" truelight@543: #include "table/strings.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" truelight@543: #include "console.h" /* IConsoleCmdExec */ truelight@543: #include /* va_list */ truelight@602: #include "md5.h" truelight@0: bjarni@770: #ifdef __MORPHOS__ bjarni@770: // the library base is required here bjarni@770: struct Library *SocketBase = NULL; bjarni@770: #endif bjarni@770: 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 */ tron@1093: extern void ShowJoinStatusWindow(void); tron@1093: extern void StateGameLoop(void); 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 truelight@716: NetworkClientState *NetworkFindClientStateFromIndex(uint16 client_index) truelight@543: { truelight@716: NetworkClientState *cs; truelight@543: tron@4077: for (cs = _clients; cs != &_clients[MAX_CLIENT_INFO]; 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. truelight@716: void NetworkGetClientName(char *client_name, size_t size, const NetworkClientState *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: Darkvater@2944: byte NetworkSpectatorCount(void) Darkvater@2944: { Darkvater@2944: const NetworkClientState *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 truelight@722: void CDECL NetworkTextMessage(NetworkAction action, uint16 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: Darkvater@4944: color = 1; Darkvater@4944: snprintf(message, sizeof(message), "*** %s", buf); Darkvater@4944: break; truelight@722: case NETWORK_ACTION_JOIN: Darkvater@4944: color = 1; 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: Darkvater@4944: color = 1; 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: truelight@732: IConsolePrintF(color, "%s", message); truelight@732: AddTextMessage(color, duration, "%s", message); truelight@543: } truelight@543: truelight@543: // Calculate the frame-lag of a client truelight@716: uint NetworkCalculateLag(const NetworkClientState *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 truelight@543: if (cs->last_frame_server + DAY_TICKS + _network_frame_freq < _frame_counter) truelight@543: lag += _frame_counter - (cs->last_frame_server + DAY_TICKS + _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; 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: Darkvater@4880: static void NetworkClientError(NetworkRecvStatus res, NetworkClientState* 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) { tron@4077: case NETWORK_RECV_STATUS_DESYNC: errorno = NETWORK_ERROR_DESYNC; break; truelight@543: case NETWORK_RECV_STATUS_SAVEGAME: errorno = NETWORK_ERROR_SAVEGAME_FAILED; break; tron@4077: 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 truelight@543: NetworkSend_Packets(DEREF_CLIENT(0)); 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, 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: Darkvater@3437: if (err >= lengthof(network_error_strings)) err = 0; 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 */ peter1138@4716: static uint NetworkCountPlayers(void) peter1138@4716: { Darkvater@4880: const NetworkClientState *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 */ peter1138@4716: void CheckMinPlayers(void) peter1138@4716: { peter1138@4716: if (!_network_dedicated) return; peter1138@4716: peter1138@4716: if (NetworkCountPlayers() < _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); Darkvater@4944: NetworkServer_HandleChat(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); Darkvater@4944: NetworkServer_HandleChat(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 truelight@716: static void NetworkFindIPs(void) truelight@0: { 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", truelight@543: &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++; 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++; 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; 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; tron@4033: } truelight@0: } truelight@0: tron@4033: p += sizeof(struct ifreq); tron@4033: #ifdef AF_LINK 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@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@543: struct hostent *he = gethostbyname(hostname); truelight@543: if (he == NULL) { Darkvater@5568: DEBUG(net, 0, "Cannot resolve '%s'", hostname); truelight@543: } else { truelight@543: struct in_addr addr = *(struct in_addr *)he->h_addr_list[0]; Darkvater@5568: DEBUG(net, 1, "Resolved '%s' to %s", hostname, inet_ntoa(addr)); truelight@543: ip = addr.s_addr; truelight@543: } truelight@543: } truelight@543: return ip; truelight@543: } truelight@543: truelight@543: // Converts a string to ip/port/player truelight@543: // Format: IP#player:port truelight@543: // truelight@543: // connection_string will be re-terminated to seperate out the hostname, and player and port will truelight@543: // be set to the player and port strings given by the user, inside the memory area originally truelight@543: // 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++) { truelight@543: if (*p == '#') { truelight@543: *p = '\0'; Darkvater@4857: *player = ++p; peter1138@5108: while (IsValidChar(*p, CS_NUMERAL)) p++; Darkvater@4857: if (*p == '\0') break; truelight@543: } else if (*p == ':') { truelight@543: *port = p + 1; truelight@543: *p = '\0'; 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 truelight@716: static NetworkClientState *NetworkAllocClient(SOCKET s) truelight@0: { truelight@716: NetworkClientState *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; Darkvater@4880: if (_network_game_info.clients_on >= _network_game_info.clients_max) 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); truelight@543: memset(cs, 0, sizeof(*cs)); truelight@543: cs->socket = s; truelight@543: cs->last_frame = 0; Darkvater@4880: cs->has_quit = false; truelight@543: 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 truelight@716: void NetworkCloseClient(NetworkClientState *cs) truelight@0: { truelight@543: NetworkClientInfo *ci; truelight@543: // Socket is already dead truelight@903: if (cs->socket == 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]; truelight@716: NetworkClientState *new_cs; truelight@0: truelight@543: NetworkGetClientName(client_name, sizeof(client_name), cs); truelight@0: Darkvater@4912: GetNetworkErrorMsg(str, errorno, lastof(str)); truelight@0: tron@2373: NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, 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 */ truelight@1604: if (cs->status == STATUS_PRE_ACTIVE && _network_pause_on_join) { truelight@1604: DoCommandP(0, 0, 0, NULL, CMD_PAUSE); Darkvater@4944: NetworkServer_HandleChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game unpaused", NETWORK_SERVER_INDEX); truelight@0: } truelight@0: truelight@543: closesocket(cs->socket); truelight@543: cs->writable = false; Darkvater@4880: cs->has_quit = true; truelight@185: truelight@543: // Free all pending and partially received packets truelight@543: while (cs->packet_queue != NULL) { truelight@543: Packet *p = cs->packet_queue->next; truelight@543: free(cs->packet_queue); truelight@543: cs->packet_queue = p; truelight@0: } truelight@543: free(cs->packet_recv); truelight@543: cs->packet_recv = NULL; truelight@0: truelight@543: while (cs->command_queue != NULL) { truelight@543: CommandPacket *p = cs->command_queue->next; truelight@543: free(cs->command_queue); truelight@543: cs->command_queue = p; truelight@543: } 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 :( Darkvater@2944: if (cs->status > STATUS_INACTIVE) _network_game_info.clients_on--; truelight@543: _network_clients_connected--; truelight@543: truelight@543: while ((cs + 1) != DEREF_CLIENT(MAX_CLIENTS) && (cs + 1)->socket != 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 truelight@0: cs->socket = 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: truelight@543: ShowJoinStatusWindow(); truelight@543: truelight@0: return true; truelight@0: } truelight@0: truelight@543: // For the server, to accept new clients truelight@543: static void NetworkAcceptClients(void) truelight@0: { truelight@0: struct sockaddr_in sin; truelight@716: NetworkClientState *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])) { truelight@841: Packet *p = NetworkSend_Init(PACKET_SERVER_BANNED); truelight@841: Darkvater@5568: DEBUG(net, 1, "Banned ip tried to join (%s), refused", _network_ban_list[i]); truelight@841: truelight@841: p->buffer[0] = p->size & 0xFF; truelight@841: p->buffer[1] = p->size >> 8; truelight@841: truelight@841: send(s, p->buffer, p->size, 0); truelight@841: closesocket(s); truelight@841: truelight@841: free(p); 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! truelight@543: Packet *p = NetworkSend_Init(PACKET_SERVER_FULL); truelight@543: truelight@543: p->buffer[0] = p->size & 0xFF; truelight@543: p->buffer[1] = p->size >> 8; truelight@543: truelight@543: send(s, p->buffer, p->size, 0); truelight@0: closesocket(s); truelight@543: truelight@543: free(p); 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 truelight@716: static bool NetworkListen(void) darkvater@211: { dominik@105: SOCKET ls; dominik@105: struct sockaddr_in sin; dominik@105: Darkvater@5568: DEBUG(net, 1, "Listening on %s:%d", _network_server_bind_ip_host, _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; Darkvater@4880: sin.sin_port = htons(_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 truelight@716: static void NetworkClose(void) truelight@543: { truelight@716: NetworkClientState *cs; truelight@543: truelight@543: FOR_ALL_CLIENTS(cs) { truelight@543: if (!_network_server) { truelight@543: SEND_COMMAND(PACKET_CLIENT_QUIT)("leaving"); truelight@543: NetworkSend_Packets(cs); truelight@543: } truelight@716: NetworkCloseClient(cs); truelight@543: } truelight@543: truelight@543: if (_network_server) { truelight@543: // 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: NetworkUDPClose(); truelight@543: } truelight@543: } truelight@543: truelight@543: // Inits the network (cleans sockets and stuff) truelight@716: static void NetworkInitialize(void) truelight@543: { truelight@716: NetworkClientState *cs; truelight@543: truelight@543: _local_command_queue = NULL; truelight@543: truelight@543: // Clean all client-sockets truelight@662: memset(_clients, 0, sizeof(_clients)); truelight@543: for (cs = _clients; cs != &_clients[MAX_CLIENTS]; cs++) { truelight@543: cs->socket = INVALID_SOCKET; truelight@543: cs->status = STATUS_INACTIVE; truelight@543: cs->command_queue = NULL; 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 tron@1329: NetworkGameList *NetworkQueryServer(const char* host, unsigned short port, bool game_info) truelight@543: { dominik@738: if (!_network_available) return NULL; truelight@543: truelight@543: NetworkDisconnect(); truelight@543: Darkvater@4880: if (game_info) return NetworkUDPQueryServer(host, port); 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: dominik@738: return NULL; 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') { dominik@738: NetworkGameList *item; 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: dominik@710: ttd_strlcpy(_network_default_ip, b, lengthof(_network_default_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: dominik@738: item = NetworkQueryServer(host, rport, true); dominik@738: item->manually = 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. */ tron@1093: void NetworkRebuildHostList(void) 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: truelight@543: ttd_strlcpy(_network_last_host, host, sizeof(_network_last_host)); truelight@543: _network_last_port = port; truelight@543: truelight@543: NetworkDisconnect(); truelight@543: NetworkUDPClose(); 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: truelight@716: static void NetworkInitGameInfo(void) truelight@543: { truelight@543: NetworkClientInfo *ci; truelight@543: truelight@543: ttd_strlcpy(_network_game_info.server_name, _network_server_name, sizeof(_network_game_info.server_name)); truelight@1026: ttd_strlcpy(_network_game_info.server_password, _network_server_password, sizeof(_network_server_password)); truelight@1026: ttd_strlcpy(_network_game_info.rcon_password, _network_rcon_password, sizeof(_network_rcon_password)); truelight@543: if (_network_game_info.server_name[0] == '\0') truelight@543: snprintf(_network_game_info.server_name, sizeof(_network_game_info.server_name), "Unnamed Server"); truelight@543: Darkvater@2879: ttd_strlcpy(_network_game_info.server_revision, _openttd_revision, sizeof(_network_game_info.server_revision)); Darkvater@2879: truelight@543: // The server is a client too ;) truelight@543: if (_network_dedicated) { truelight@543: _network_game_info.clients_on = 0; Darkvater@2879: _network_game_info.companies_on = 0; truelight@543: _network_game_info.dedicated = true; truelight@543: } else { truelight@543: _network_game_info.clients_on = 1; Darkvater@2879: _network_game_info.companies_on = 1; truelight@543: _network_game_info.dedicated = false; truelight@543: } Darkvater@2879: truelight@543: _network_game_info.spectators_on = 0; Darkvater@2879: truelight@543: _network_game_info.game_date = _date; rubidium@4295: _network_game_info.start_date = ConvertYMDToDate(_patches.starting_year, 0, 1); tron@863: _network_game_info.map_width = MapSizeX(); tron@863: _network_game_info.map_height = MapSizeY(); truelight@543: _network_game_info.map_set = _opt.landscape; truelight@543: tron@1899: _network_game_info.use_password = (_network_server_password[0] != '\0'); 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: truelight@693: ttd_strlcpy(ci->client_name, _network_player_name, sizeof(ci->client_name)); truelight@693: ttd_strlcpy(ci->unique_id, _network_unique_id, sizeof(ci->unique_id)); truelight@543: } truelight@543: truelight@543: bool NetworkServerStart(void) 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; truelight@764: _network_udp_server = NetworkUDPListen(&_udp_server_socket, _network_server_bind_ip, _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 */ Darkvater@4878: if (!_network_dedicated) _network_playas = 0; 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 truelight@543: void NetworkReboot(void) truelight@543: { truelight@543: if (_network_server) { truelight@716: NetworkClientState *cs; truelight@543: FOR_ALL_CLIENTS(cs) { truelight@543: SEND_COMMAND(PACKET_SERVER_NEWGAME)(cs); truelight@543: NetworkSend_Packets(cs); truelight@543: } truelight@543: } truelight@543: truelight@543: NetworkClose(); truelight@543: truelight@543: // Free all queued commands truelight@543: while (_local_command_queue != NULL) { truelight@543: CommandPacket *p = _local_command_queue; truelight@543: _local_command_queue = _local_command_queue->next; truelight@543: free(p); truelight@543: } truelight@543: truelight@543: _networking = false; truelight@543: _network_server = false; truelight@543: } truelight@543: truelight@543: // We want to disconnect from the host/clients truelight@543: void NetworkDisconnect(void) truelight@543: { truelight@543: if (_network_server) { truelight@716: NetworkClientState *cs; truelight@543: FOR_ALL_CLIENTS(cs) { truelight@543: SEND_COMMAND(PACKET_SERVER_SHUTDOWN)(cs); truelight@543: NetworkSend_Packets(cs); truelight@543: } truelight@543: } truelight@543: Darkvater@4880: if (_network_advertise) NetworkUDPRemoveAdvertise(); truelight@765: truelight@598: DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0); truelight@598: truelight@543: NetworkClose(); truelight@543: truelight@543: // Free all queued commands truelight@543: while (_local_command_queue != NULL) { truelight@543: CommandPacket *p = _local_command_queue; truelight@543: _local_command_queue = _local_command_queue->next; truelight@543: free(p); truelight@543: } truelight@543: truelight@543: _networking = false; truelight@543: _network_server = false; truelight@543: } truelight@543: truelight@543: // Receives something from the network truelight@716: static bool NetworkReceive(void) truelight@0: { truelight@716: NetworkClientState *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) { truelight@0: FD_SET(cs->socket, &read_fd); truelight@0: FD_SET(cs->socket, &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) { truelight@0: cs->writable = !!FD_ISSET(cs->socket, &write_fd); truelight@0: if (FD_ISSET(cs->socket, &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) truelight@543: static void NetworkSend(void) truelight@543: { truelight@716: NetworkClientState *cs; truelight@543: FOR_ALL_CLIENTS(cs) { truelight@543: if (cs->writable) { truelight@543: NetworkSend_Packets(cs); 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 truelight@716: static void NetworkHandleLocalQueue(void) 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. Darkvater@5568: DEBUG(net, 0, "Trying to execute a packet in the past!"); ludde@2080: assert(0); 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: truelight@716: static bool NetworkDoClientLoop(void) 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 truelight@543: if (_sync_seed_1 != _random_seeds[0][0] || _sync_seed_2 != _random_seeds[0][1]) { truelight@543: #else truelight@543: if (_sync_seed_1 != _random_seeds[0][0]) { truelight@543: #endif truelight@543: NetworkError(STR_NETWORK_ERR_DESYNC); 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 truelight@543: void NetworkUDPGameLoop(void) darkvater@211: { truelight@764: if (_network_udp_server) { truelight@764: NetworkUDPReceive(_udp_server_socket); truelight@764: if (_udp_master_socket != INVALID_SOCKET) { truelight@764: NetworkUDPReceive(_udp_master_socket); truelight@764: } tron@4000: } else if (_udp_client_socket != INVALID_SOCKET) { truelight@764: NetworkUDPReceive(_udp_client_socket); tron@4000: if (_network_udp_broadcast > 0) _network_udp_broadcast--; 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! truelight@543: void NetworkGameLoop(void) darkvater@211: { truelight@543: if (!_networking) return; dominik@105: truelight@543: if (!NetworkReceive()) return; truelight@543: truelight@543: if (_network_server) { 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) { ludde@2235: _frame_counter_max = _frame_counter + _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: truelight@543: _sync_seed_1 = _random_seeds[0][0]; truelight@543: #ifdef NETWORK_SEND_DOUBLE_SEED truelight@543: _sync_seed_2 = _random_seeds[0][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: truelight@716: static void NetworkGenerateUniqueId(void) truelight@602: { truelight@602: md5_state_t state; truelight@602: md5_byte_t 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 */ truelight@602: md5_init(&state); tron@2639: md5_append(&state, (const md5_byte_t*)coding_string, strlen(coding_string)); truelight@602: md5_finish(&state, 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 */ truelight@602: snprintf(_network_unique_id, sizeof(_network_unique_id), "%s", hex_output); truelight@602: } truelight@602: truelight@543: // This tries to launch the network for a given OS truelight@543: void NetworkStartUp(void) darkvater@211: { Darkvater@5568: DEBUG(net, 3, "[core] starting network..."); bjarni@770: tron@4077: #if defined(__MORPHOS__) || defined(__AMIGA__) truelight@841: /* bjarni@770: * IMPORTANT NOTE: SocketBase needs to be initialized before we use _any_ bjarni@770: * network related function, else: crash. truelight@841: */ Darkvater@5568: DEBUG(net, 3, "[core] loading bsd socket library"); tron@4077: SocketBase = OpenLibrary("bsdsocket.library", 4); tron@4077: if (SocketBase == NULL) { Darkvater@5568: DEBUG(net, 0, "[core] can't open bsdsocket.library version 4, network unavailable"); tron@4077: _network_available = false; tron@4077: return; tron@4077: } bjarni@770: tron@4077: #if defined(__AMIGA__) tron@4077: // for usleep() implementation (only required for legacy AmigaOS builds) tron@4077: TimerPort = CreateMsgPort(); tron@4077: if (TimerPort != NULL) { tron@4077: TimerRequest = (struct timerequest*)CreateIORequest(TimerPort, sizeof(struct timerequest); tron@4077: if (TimerRequest != NULL) { tron@4077: if (OpenDevice("timer.device", UNIT_MICROHZ, (struct IORequest*)TimerRequest, 0) == 0) { tron@4077: TimerBase = TimerRequest->tr_node.io_Device; tron@4077: if (TimerBase == NULL) { tron@4077: // free ressources... Darkvater@5568: DEBUG(net, 0, "[core] can't initialize timer, network unavailable"); tron@4077: _network_available = false; tron@4077: return; bjarni@770: } bjarni@770: } bjarni@770: } bjarni@770: } tron@4077: #endif // __AMIGA__ tron@4077: #endif // __MORPHOS__ / __AMIGA__ truelight@841: tron@4077: // Network is available truelight@543: _network_available = true; 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 */ truelight@629: _network_server_bind_ip = inet_addr(_network_server_bind_ip_host); truelight@629: /* And put the data back in it in case it was an invalid ip */ truelight@629: snprintf(_network_server_bind_ip_host, sizeof(_network_server_bind_ip_host), "%s", inet_ntoa(*(struct in_addr *)&_network_server_bind_ip)); truelight@629: truelight@602: /* Generate an unique id when there is none yet */ Darkvater@2879: if (_network_unique_id[0] == '\0') NetworkGenerateUniqueId(); truelight@602: Darkvater@4417: { Darkvater@4417: byte cl_max = _network_game_info.clients_max; Darkvater@4417: byte cp_max = _network_game_info.companies_max; Darkvater@4417: byte sp_max = _network_game_info.spectators_max; Darkvater@4417: Darkvater@4417: memset(&_network_game_info, 0, sizeof(_network_game_info)); Darkvater@4417: _network_game_info.clients_max = cl_max; Darkvater@4417: _network_game_info.companies_max = cp_max; Darkvater@4417: _network_game_info.spectators_max = sp_max; Darkvater@4417: } dominik@105: truelight@543: // Let's load the network in windows darkvater@211: #if defined(WIN32) darkvater@211: { darkvater@211: WSADATA wsa; Darkvater@5568: DEBUG(net, 3, "[core] loading windows socket library"); darkvater@211: if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) { Darkvater@5568: DEBUG(net, 0, "[core] WSAStartup failed, network unavailable"); truelight@543: _network_available = false; truelight@543: return; truelight@543: } darkvater@211: } truelight@543: #endif // WIN32 darkvater@211: truelight@543: NetworkInitialize(); Darkvater@5568: DEBUG(net, 3, "[core] network online, multiplayer available"); truelight@543: NetworkFindIPs(); darkvater@211: } darkvater@211: truelight@543: // This shuts the network down truelight@543: void NetworkShutDown(void) truelight@543: { Darkvater@4829: NetworkDisconnect(); Darkvater@4829: NetworkUDPClose(); Darkvater@4829: Darkvater@5568: DEBUG(net, 3, "[core] shutting down network"); darkvater@211: truelight@543: _network_available = false; darkvater@211: tron@4077: #if defined(__MORPHOS__) || defined(__AMIGA__) tron@4077: // free allocated ressources tron@4077: #if defined(__AMIGA__) tron@4077: if (TimerBase != NULL) CloseDevice((struct IORequest*)TimerRequest); // XXX This smells wrong tron@4077: if (TimerRequest != NULL) DeleteIORequest(TimerRequest); tron@4077: if (TimerPort != NULL) DeleteMsgPort(TimerPort); tron@4077: #endif darkvater@211: tron@4077: if (SocketBase != NULL) CloseLibrary(SocketBase); tron@4077: #endif darkvater@173: tron@4077: #if defined(WIN32) tron@4077: WSACleanup(); tron@4077: #endif dominik@105: } dominik@105: truelight@543: #endif /* ENABLE_NETWORK */