truelight@0: #include "stdafx.h" tron@1299: #include "debug.h" tron@1317: #include "string.h" tron@1309: #include "strings.h" tron@679: #include "map.h" truelight@543: #include "network_data.h" truelight@1602: #include "command.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: 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: // Network copy of patches, so the patches of a client are not fucked up truelight@543: // after he joined a server truelight@543: static Patches network_tmp_patches; 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: truelight@543: for (ci = _network_client_info; ci != &_network_client_info[MAX_CLIENT_INFO]; ci++) truelight@543: if (ci->client_index == client_index) truelight@543: return ci; truelight@543: truelight@543: return NULL; truelight@543: } truelight@543: 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: truelight@543: for (cs = _clients; cs != &_clients[MAX_CLIENT_INFO]; cs++) truelight@543: if (cs->index == client_index) truelight@543: return cs; 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: { truelight@543: NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs); truelight@543: if (ci->client_name[0] == '\0') truelight@543: snprintf(client_name, size, "Client #%d", cs->index); truelight@543: else truelight@543: snprintf(client_name, size, "%s", ci->client_name); truelight@543: } truelight@543: 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); truelight@543: vsprintf(buf, str, va); truelight@543: va_end(va); truelight@543: truelight@543: switch (action) { truelight@722: case NETWORK_ACTION_JOIN: truelight@722: GetString(temp, STR_NETWORK_CLIENT_JOINED); truelight@722: snprintf(message, sizeof(message), "*** %s %s", name, temp); truelight@722: break; truelight@722: case NETWORK_ACTION_LEAVE: truelight@722: GetString(temp, STR_NETWORK_ERR_LEFT); 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)); truelight@722: GetString(temp, STR_NETWORK_GAVE_MONEY_AWAY); truelight@722: snprintf(message, sizeof(message), "*** %s", temp); truelight@722: } else { truelight@722: SetDParam(0, atoi(buf)); truelight@722: GetString(temp, STR_NETWORK_GIVE_MONEY); truelight@722: snprintf(message, sizeof(message), "*** %s %s", name, temp); truelight@722: } truelight@543: break; truelight@543: case NETWORK_ACTION_CHAT_PLAYER: truelight@722: if (self_send) { ludde@2055: SetDParamStr(0, name); truelight@722: GetString(temp, STR_NETWORK_CHAT_TO_COMPANY); truelight@722: snprintf(message, sizeof(message), "%s %s", temp, buf); truelight@722: } else { ludde@2055: SetDParamStr(0, name); truelight@722: GetString(temp, STR_NETWORK_CHAT_COMPANY); truelight@722: snprintf(message, sizeof(message), "%s %s", temp, buf); truelight@722: } truelight@543: break; truelight@543: case NETWORK_ACTION_CHAT_CLIENT: truelight@722: if (self_send) { ludde@2055: SetDParamStr(0, name); truelight@722: GetString(temp, STR_NETWORK_CHAT_TO_CLIENT); truelight@722: snprintf(message, sizeof(message), "%s %s", temp, buf); truelight@722: } else { ludde@2055: SetDParamStr(0, name); truelight@722: GetString(temp, STR_NETWORK_CHAT_CLIENT); truelight@722: snprintf(message, sizeof(message), "%s %s", temp, buf); truelight@722: } truelight@543: break; truelight@543: case NETWORK_ACTION_NAME_CHANGE: truelight@722: GetString(temp, STR_NETWORK_NAME_CHANGE); truelight@722: snprintf(message, sizeof(message), "*** %s %s %s", name, temp, buf); truelight@543: break; truelight@543: default: ludde@2055: SetDParamStr(0, name); truelight@722: GetString(temp, STR_NETWORK_CHAT_ALL); truelight@722: snprintf(message, sizeof(message), "%s %s", temp, buf); 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: { truelight@543: DEBUG(net, 0)("[NET] 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: { truelight@543: DEBUG(net, 0)("[NET] Server could not start network: %s",error); truelight@543: NetworkError(STR_NETWORK_ERR_SERVER_START); signde@208: } signde@208: truelight@716: static void NetworkClientError(byte res, NetworkClientState *cs) { 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) { truelight@543: cs->quited = 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: truelight@543: switch(res) { truelight@543: case NETWORK_RECV_STATUS_DESYNC: errorno = NETWORK_ERROR_DESYNC; break; truelight@543: case NETWORK_RECV_STATUS_SAVEGAME: errorno = NETWORK_ERROR_SAVEGAME_FAILED; break; truelight@543: default: errorno = NETWORK_ERROR_GENERAL; 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: truelight@543: // Find all IP-aliases for this host truelight@716: static void NetworkFindIPs(void) truelight@0: { truelight@543: int i, last; 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 truelight@543: _network_ip_list[0] = 0; truelight@543: truelight@543: if (sock < 0) { truelight@543: DEBUG(net, 0)("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) { truelight@543: DEBUG(net, 0)("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; 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: } truelight@543: inaddr.s_addr = htonl((uint32)i1 << 24 | (uint32)i2 << 16 | (uint32)i3 << 8 | (uint32)i4); truelight@543: if (inaddr.s_addr != 0) { truelight@543: _network_ip_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 truelight@543: _network_ip_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) { truelight@543: if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET) truelight@543: continue; truelight@543: _network_ip_list[i] = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; truelight@543: i++; truelight@543: } truelight@543: freeifaddrs(ifap); truelight@0: truelight@543: #else /* not HAVE_GETIFADDRS */ truelight@543: truelight@543: unsigned long len = 0; truelight@543: SOCKET sock; truelight@543: IFREQ ifo[MAX_INTERFACES]; truelight@543: truelight@543: #ifndef WIN32 truelight@543: struct ifconf if_conf; truelight@543: #endif truelight@543: truelight@543: // If something fails, make sure the list is empty truelight@543: _network_ip_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: // On windows it is easy 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: #else truelight@543: // On linux a bit harder truelight@543: if_conf.ifc_len = (sizeof (struct ifreq)) * MAX_INTERFACES; truelight@543: if_conf.ifc_buf = (char *)&ifo[0]; truelight@543: if ((ioctl(sock, SIOCGIFCONF, &if_conf)) == -1) { truelight@543: closesocket(sock); truelight@543: return; truelight@543: } truelight@543: len = if_conf.ifc_len; truelight@543: #endif /* WIN32 */ truelight@543: truelight@543: // Now walk through all IPs and list them truelight@543: for (i = 0; i < (int)(len / sizeof(IFREQ)); i++) { truelight@543: // Request IP for this interface truelight@543: #ifdef WIN32 truelight@543: _network_ip_list[i] = *(&ifo[i].iiAddress.AddressIn.sin_addr.s_addr); truelight@543: #else truelight@543: if ((ioctl(sock, SIOCGIFADDR, &ifo[i])) != 0) { truelight@543: closesocket(sock); truelight@543: return; truelight@0: } truelight@0: truelight@543: _network_ip_list[i] = ((struct sockaddr_in *)&ifo[i].ifr_addr)->sin_addr.s_addr; truelight@543: #endif truelight@0: } truelight@0: truelight@543: closesocket(sock); truelight@0: truelight@543: #endif /* not HAVE_GETIFADDRS */ truelight@543: truelight@543: _network_ip_list[i] = 0; truelight@543: last = i - 1; truelight@543: truelight@543: DEBUG(net, 3)("Detected IPs:"); truelight@543: // Now display to the debug all the detected ips truelight@543: i = 0; truelight@543: while (_network_ip_list[i] != 0) { truelight@543: // Also check for non-used ips (127.0.0.1) truelight@543: if (_network_ip_list[i] == inet_addr("127.0.0.1")) { truelight@543: // If there is an ip after thisone, put him in here truelight@543: if (last > i) truelight@543: _network_ip_list[i] = _network_ip_list[last]; truelight@543: // Clear the last ip truelight@543: _network_ip_list[last] = 0; truelight@543: // And we have 1 ip less truelight@543: last--; truelight@543: continue; truelight@543: } truelight@543: truelight@543: DEBUG(net, 3)(" %d) %s", i, inet_ntoa(*(struct in_addr *)&_network_ip_list[i]));//inet_ntoa(inaddr)); truelight@543: i++; 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) { truelight@543: DEBUG(net, 0) ("[NET] Cannot resolve %s", hostname); truelight@543: } else { truelight@543: struct in_addr addr = *(struct in_addr *)he->h_addr_list[0]; truelight@543: DEBUG(net, 1) ("[NET] 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: *player = p + 1; truelight@543: *p = '\0'; 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; truelight@543: NetworkClientInfo *ci; truelight@543: byte client_no; truelight@0: truelight@543: client_no = 0; truelight@0: truelight@543: if (_network_server) { truelight@543: // Can we handle a new client? truelight@543: if (_network_clients_connected >= MAX_CLIENTS) truelight@543: return NULL; truelight@0: truelight@543: if (_network_game_info.clients_on >= _network_game_info.clients_max) truelight@543: return NULL; signde@206: truelight@543: // Register the login truelight@543: client_no = _network_clients_connected++; truelight@543: } truelight@543: truelight@543: cs = &_clients[client_no]; truelight@543: memset(cs, 0, sizeof(*cs)); truelight@543: cs->socket = s; truelight@543: cs->last_frame = 0; truelight@543: cs->quited = 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) { truelight@662: 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; 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) { truelight@903: cs->quited = true; truelight@903: return; truelight@903: } truelight@260: truelight@543: DEBUG(net, 1) ("[NET] Closed client connection"); truelight@0: truelight@543: if (!cs->quited && _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]; truelight@543: char client_name[NETWORK_NAME_LENGTH]; truelight@716: NetworkClientState *new_cs; truelight@0: truelight@543: NetworkGetClientName(client_name, sizeof(client_name), cs); truelight@0: truelight@722: GetString(str, STR_NETWORK_ERR_CLIENT_GENERAL + errorno); truelight@0: truelight@722: NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, client_name, 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); truelight@1604: NetworkServer_HandleChat(NETWORK_ACTION_CHAT, DESTTYPE_BROADCAST, 0, "Game unpaused", NETWORK_SERVER_INDEX); truelight@0: } truelight@0: truelight@543: closesocket(cs->socket); truelight@543: cs->writable = false; truelight@903: cs->quited = 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 :( truelight@543: if (cs->status > STATUS_INACTIVE) truelight@543: _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; 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: truelight@543: DEBUG(net, 1) ("[NET] 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: tron@1332: if (!SetNoDelay(s)) tron@1332: DEBUG(net, 1)("[NET] 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: truelight@543: if (connect(s, (struct sockaddr*) &sin, sizeof(sin)) != 0) { truelight@543: // We failed to connect for which reason what so ever truelight@543: return false; truelight@543: } truelight@543: tron@1332: if (!SetNonBlocking(s)) tron@1332: DEBUG(net, 0)("[NET] Setting non-blocking 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@543: memcpy(&network_tmp_patches, &_patches, sizeof(_patches)); truelight@0: 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@0: SOCKET s; 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 (;;) { tron@1332: socklen_t sin_len; tron@1332: truelight@0: sin_len = sizeof(sin); truelight@0: 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: truelight@543: DEBUG(net, 1) ("[NET] 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++) { truelight@841: if (_network_ban_list[i] == NULL) truelight@841: 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: truelight@841: DEBUG(net, 1)("[NET] 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 */ truelight@841: if (banned) truelight@841: 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: truelight@543: { truelight@543: // Save the IP of the client truelight@543: NetworkClientInfo *ci; truelight@543: ci = DEREF_CLIENT_INFO(cs); truelight@543: ci->client_ip = sin.sin_addr.s_addr; truelight@0: } 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: int port; dominik@105: dominik@105: port = _network_server_port; dominik@105: truelight@629: DEBUG(net, 1) ("[NET] Listening on %s:%d", _network_server_bind_ip_host, 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: tron@1332: if (!SetNonBlocking(ls)) tron@1332: DEBUG(net, 0)("[NET] Setting non-blocking 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; dominik@105: sin.sin_port = htons(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; truelight@543: DEBUG(net, 1) ("[NET] 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 truelight@543: memset(_network_client_info, 0, sizeof(_network_client_info)); truelight@543: memset(_network_player_info, 0, sizeof(_network_player_info)); truelight@734: _network_lobby_company_count = 0; truelight@543: truelight@543: _sync_frame = 0; truelight@543: _network_first_time = true; truelight@543: truelight@543: _network_reconnect = 0; truelight@543: truelight@543: InitPlayerRandoms(); 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: truelight@543: if (game_info) { dominik@738: return NetworkUDPQueryServer(host, port); truelight@543: } 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: // ttd_strlcpy(_network_last_host, host, sizeof(_network_last_host)); truelight@543: // _network_last_port = port; truelight@543: truelight@543: // We are connected truelight@543: if (_networking) { truelight@543: SEND_COMMAND(PACKET_CLIENT_COMPANY_INFO)(); dominik@738: return NULL; truelight@543: } truelight@543: truelight@543: // No networking, close everything down again truelight@543: NetworkDisconnect(); dominik@738: return NULL; truelight@543: } truelight@543: dominik@738: /* Validates an address entered as a string and adds the server to dominik@738: * the list. If you use this functions, 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: dominik@710: if (player != NULL) _network_playas = atoi(player); 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; dominik@738: NetworkGameList *item = _network_game_list; truelight@841: while (item != NULL && i != lengthof(_network_host_list)) { dominik@738: if (item->manually) dominik@895: _network_host_list[i++] = str_fmt("%s:%i", item->info.hostname, item->port); dominik@738: item = item->next; dominik@738: } dominik@738: truelight@841: for (; i < lengthof(_network_host_list); i++) { dominik@738: _network_host_list[i] = strdup(""); dominik@710: } dominik@710: } dominik@710: truelight@543: // Used by clients, to connect to a server tron@1329: bool NetworkClientConnectGame(const char* host, unsigned short 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: truelight@543: // The server is a client too ;) truelight@543: if (_network_dedicated) { truelight@543: _network_game_info.clients_on = 0; truelight@543: _network_game_info.dedicated = true; truelight@543: } else { truelight@543: _network_game_info.clients_on = 1; truelight@543: _network_game_info.dedicated = false; truelight@543: } truelight@693: ttd_strlcpy(_network_game_info.server_revision, _openttd_revision, sizeof(_network_game_info.server_revision)); truelight@543: _network_game_info.spectators_on = 0; truelight@543: _network_game_info.game_date = _date; truelight@543: _network_game_info.start_date = ConvertIntDate(_patches.starting_date); 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; truelight@543: if (_network_dedicated) truelight@543: ci->client_playas = OWNER_SPECTATOR; truelight@543: else truelight@543: ci->client_playas = _local_player + 1; 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(); truelight@543: if (!NetworkListen()) truelight@543: 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; truelight@543: _network_own_client_index = NETWORK_SERVER_INDEX; truelight@543: truelight@683: if (!_network_dedicated) truelight@683: _network_playas = 1; 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: truelight@668: /* Try to register us to the master server */ truelight@668: _network_last_advertise_date = 0; 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: truelight@1832: if (_network_advertise) truelight@1832: 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: if (_networking && !_network_server) { truelight@543: memcpy(&_patches, &network_tmp_patches, sizeof(_patches)); 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 truelight@543: if (_network_server) { truelight@0: FD_SET(_listensocket, &read_fd); truelight@0: } 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.. truelight@543: if (_network_server && FD_ISSET(_listensocket, &read_fd)) truelight@0: 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)) { truelight@543: if (_network_server) truelight@543: NetworkServer_ReadPackets(cs); truelight@543: else { truelight@543: byte res; truelight@543: // The client already was quiting! truelight@543: if (cs->quited) return false; truelight@543: if ((res = NetworkClient_ReadPackets(cs)) != 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: 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: { truelight@543: if (_local_command_queue != NULL) { truelight@543: CommandPacket *cp; truelight@543: CommandPacket *cp_prev; truelight@0: truelight@543: cp = _local_command_queue; truelight@543: cp_prev = NULL; truelight@543: truelight@543: while (cp != NULL) { truelight@543: if (_frame_counter > cp->frame) { truelight@543: // We can execute this command truelight@543: NetworkExecuteCommand(cp); truelight@543: truelight@543: if (cp_prev != NULL) { truelight@543: cp_prev->next = cp->next; truelight@543: free(cp); truelight@543: cp = cp_prev->next; truelight@543: } else { truelight@543: // This means we are at our first packet truelight@543: _local_command_queue = cp->next; truelight@543: free(cp); truelight@543: cp = _local_command_queue; truelight@543: } truelight@543: truelight@543: } else { truelight@543: // Command is in the future, skip to next truelight@543: // (commands don't have to be in order in the queue!!) truelight@543: cp_prev = cp; truelight@543: cp = cp->next; truelight@543: } truelight@0: } truelight@0: } 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); truelight@543: DEBUG(net, 0)("[NET] 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) { truelight@543: DEBUG(net, 1)("[NET] 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: } truelight@764: } truelight@543: else if (_udp_client_socket != INVALID_SOCKET) { truelight@764: NetworkUDPReceive(_udp_client_socket); truelight@543: if (_network_udp_broadcast > 0) truelight@543: _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) { truelight@543: // We first increase the _frame_counter truelight@543: _frame_counter++; 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: truelight@543: NetworkServer_Tick(); 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 truelight@543: if (_frame_counter_max > _frame_counter) { truelight@543: NetworkDoClientLoop(); truelight@543: } 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); truelight@602: md5_append(&state, 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: { truelight@543: DEBUG(net, 3) ("[NET][Core] Starting network..."); bjarni@770: bjarni@770: #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: */ bjarni@770: { bjarni@770: DEBUG(misc,3) ("[NET][Core] Loading bsd socket library"); bjarni@770: if (!(SocketBase = OpenLibrary("bsdsocket.library", 4))) { bjarni@770: DEBUG(net, 0) ("[NET][Core] Error: couldn't open bsdsocket.library version 4. Network not available."); bjarni@770: _network_available = false; bjarni@770: return; bjarni@770: } bjarni@770: bjarni@770: #if defined(__AMIGA__) bjarni@770: // for usleep() implementation (only required for legacy AmigaOS builds) bjarni@770: if ( (TimerPort = CreateMsgPort()) ) { bjarni@770: if ( (TimerRequest = (struct timerequest *) CreateIORequest(TimerPort, sizeof(struct timerequest))) ) { bjarni@770: if ( OpenDevice("timer.device", UNIT_MICROHZ, (struct IORequest *) TimerRequest, 0) == 0 ) { bjarni@770: if ( !(TimerBase = TimerRequest->tr_node.io_Device) ) { bjarni@770: // free ressources... bjarni@770: DEBUG(net, 0) ("[NET][Core] Error: couldn't initialize timer. Network not available."); bjarni@770: _network_available = false; bjarni@770: return; bjarni@770: } bjarni@770: } bjarni@770: } bjarni@770: } bjarni@770: #endif // __AMIGA__ bjarni@770: } bjarni@770: #endif // __MORPHOS__ / __AMIGA__ truelight@841: bjarni@770: // Network is available truelight@543: _network_available = true; truelight@543: _network_dedicated = false; truelight@668: _network_last_advertise_date = 0; 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 */ truelight@602: if (_network_unique_id[0] == '\0') truelight@602: NetworkGenerateUniqueId(); truelight@602: truelight@543: memset(&_network_game_info, 0, sizeof(_network_game_info)); truelight@193: truelight@543: /* XXX - Hard number here, because the strings can currently handle no more miham@826: than 10 clients -- TrueLight */ truelight@543: _network_game_info.clients_max = 10; dominik@105: truelight@543: // Let's load the network in windows darkvater@211: #if defined(WIN32) darkvater@211: { darkvater@211: WSADATA wsa; truelight@543: DEBUG(net, 3) ("[NET][Core] Loading windows socket library"); darkvater@211: if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) { truelight@543: DEBUG(net, 0) ("[NET][Core] Error: WSAStartup failed. Network not available."); truelight@543: _network_available = false; truelight@543: return; truelight@543: } darkvater@211: } truelight@543: #endif // WIN32 darkvater@211: truelight@543: NetworkInitialize(); truelight@543: DEBUG(net, 3) ("[NET][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: { truelight@543: DEBUG(net, 3) ("[NET][Core] Shutting down the network."); darkvater@211: truelight@543: _network_available = false; darkvater@211: darkvater@211: #if defined(__MORPHOS__) || defined(__AMIGA__) darkvater@211: { darkvater@211: // free allocated ressources bjarni@770: #if defined(__AMIGA__) truelight@543: if (TimerBase) { CloseDevice((struct IORequest *) TimerRequest); } truelight@543: if (TimerRequest) { DeleteIORequest(TimerRequest); } truelight@543: if (TimerPort) { DeleteMsgPort(TimerPort); } darkvater@211: #endif darkvater@211: darkvater@211: if (SocketBase) { darkvater@211: CloseLibrary(SocketBase); darkvater@211: } darkvater@144: } truelight@193: #endif darkvater@173: darkvater@211: #if defined(WIN32) truelight@543: { truelight@543: WSACleanup(); truelight@543: } darkvater@173: #endif dominik@105: } truelight@543: #else darkvater@211: Darkvater@1622: void ParseConnectionString(const char **player, const char **port, char *connection_string) {} truelight@543: void NetworkUpdateClientInfo(uint16 client_index) {} dominik@105: truelight@543: #endif /* ENABLE_NETWORK */