diff --git a/lang/english.txt b/lang/english.txt index 11512303e7..26360d36f9 100644 --- a/lang/english.txt +++ b/lang/english.txt @@ -1308,6 +1308,7 @@ STR_NETWORK_PASSWORD :{SILVER}Passwor STR_NETWORK_SERVER_OFFLINE :{SILVER}SERVER OFFLINE STR_NETWORK_SERVER_FULL :{SILVER}SERVER FULL STR_NETWORK_VERSION_MISMATCH :{SILVER}VERSION MISMATCH +STR_NETWORK_GRF_MISMATCH :{SILVER}NEWGRF MISMATCH STR_NETWORK_JOIN_GAME :{BLACK}Join game diff --git a/network.h b/network.h index 1b3416a519..5453658e42 100644 --- a/network.h +++ b/network.h @@ -54,6 +54,17 @@ enum { NETWORK_CLIENT_NAME_LENGTH = 25, NETWORK_RCONCOMMAND_LENGTH = 500, + NETWORK_GRF_NAME_LENGTH = 80, ///< Maximum length of the name of a GRF + /* Maximum number of GRFs that can be sent. + * This value is related to number of handles (files) OpenTTD can open. + * This is currently 64 and about 10 are currently used when OpenTTD loads + * without any NewGRFs. Therefore one can only load about 55 NewGRFs, so + * this is not a limit, but rather a way to easily check whether the limit + * imposed by the handle count is reached. Secondly it isn't possible to + * send much more GRF IDs + MD5sums in the PACKET_UDP_SERVER_RESPONSE, due + * to the limited size of UDP packets. */ + NETWORK_MAX_GRF_COUNT = 55, + NETWORK_NUM_LANGUAGES = 4, }; @@ -66,7 +77,8 @@ typedef struct NetworkGameInfo { char server_revision[NETWORK_REVISION_LENGTH]; // The SVN version number the server is using (e.g.: 'r304') // It even shows a SVN version in release-version, so // it is easy to compare if a server is of the correct version - bool compatible; // Can we connect to this server or not? (based on server_revision) + bool version_compatible; // Can we connect to this server or not? (based on server_revision) + bool compatible; // Can we connect to this server or not? (based on server_revision _and_ grf_match byte server_lang; // Language of the server (we should make a nice table for this) byte use_password; // Is set to != 0 if it uses a password char server_password[NETWORK_PASSWORD_LENGTH]; // On the server: the game password, on the client: != "" if server has password @@ -84,6 +96,7 @@ typedef struct NetworkGameInfo { byte map_set; // Graphical set bool dedicated; // Is this a dedicated server? char rcon_password[NETWORK_PASSWORD_LENGTH]; // RCon password for the server. "" if rcon is disabled + struct GRFConfig *grfconfig; // List of NewGRF files required } NetworkGameInfo; typedef struct NetworkPlayerInfo { diff --git a/network_data.h b/network_data.h index 80724a8de7..16263c7762 100644 --- a/network_data.h +++ b/network_data.h @@ -18,7 +18,7 @@ #define NETWORK_EMPTY_INDEX 0 // What version of game-info do we use? -#define NETWORK_GAME_INFO_VERSION 3 +#define NETWORK_GAME_INFO_VERSION 4 // What version of company info is this? #define NETWORK_COMPANY_INFO_VERSION 4 // What version of master-server-protocol do we use? diff --git a/network_gamelist.c b/network_gamelist.c index 57f24b281c..f52272307f 100644 --- a/network_gamelist.c +++ b/network_gamelist.c @@ -5,6 +5,7 @@ #include "stdafx.h" #include "debug.h" #include "network_data.h" +#include "newgrf_config.h" // This file handles the GameList // Also, it handles the request to a server for data about the server @@ -57,6 +58,9 @@ void NetworkGameListRemoveItem(NetworkGameList *remove) prev_item->next = remove->next; } + /* Remove GRFConfig information */ + ClearGRFConfigList(remove->info.grfconfig); + free(remove); DEBUG(net, 4) ("[NET][GameList] Removed server from list"); UpdateNetworkGameWindow(false); diff --git a/network_gui.c b/network_gui.c index 30efc8e517..4f55912222 100644 --- a/network_gui.c +++ b/network_gui.c @@ -26,6 +26,7 @@ #include "settings.h" #include "string.h" #include "town.h" +#include "newgrf.h" #define BGC 5 #define BTC 15 @@ -241,6 +242,10 @@ static void NetworkGameWindowWndProc(Window *w, WindowEvent *e) sel->info.clients_on >= sel->info.clients_max || // Server full !sel->info.compatible); // Revision mismatch + SetWindowWidgetHiddenState(w, 18, sel == NULL || + !sel->online || + sel->info.grfconfig == NULL); + SetDParam(0, 0x00); SetDParam(7, _lan_internet_types_dropdown[_network_lan_internet]); DrawWindowWidgets(w); @@ -288,7 +293,7 @@ static void NetworkGameWindowWndProc(Window *w, WindowEvent *e) if (cur_item->info.use_password) DrawSprite(SPR_LOCK, w->widget[8].left + 5, y - 1); // draw red or green icon, depending on compatibility with server. - DrawSprite(SPR_BLOT | (cur_item->info.compatible ? PALETTE_TO_GREEN : PALETTE_TO_RED), w->widget[8].left + 15, y); + DrawSprite(SPR_BLOT | (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), w->widget[8].left + 15, y); // draw flag according to server language DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, w->widget[8].left + 25, y); @@ -362,7 +367,7 @@ static void NetworkGameWindowWndProc(Window *w, WindowEvent *e) y += 2; if (!sel->info.compatible) { - DrawStringCentered(425, y, STR_NETWORK_VERSION_MISMATCH, 0); // server mismatch + DrawStringCentered(425, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, 0); // server mismatch } else if (sel->info.clients_on == sel->info.clients_max) { // Show: server full, when clients_on == clients_max DrawStringCentered(425, y, STR_NETWORK_SERVER_FULL, 0); // server full @@ -436,6 +441,9 @@ static void NetworkGameWindowWndProc(Window *w, WindowEvent *e) if (nd->server != NULL) NetworkQueryServer(nd->server->info.hostname, nd->server->port, true); break; + case 18: // NewGRF Settings + if (nd->server != NULL) ShowNewGRFSettings(false, false, &nd->server->info.grfconfig); + break; } break; @@ -496,7 +504,7 @@ static void NetworkGameWindowWndProc(Window *w, WindowEvent *e) static const Widget _network_game_window_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, { WWT_CAPTION, RESIZE_NONE, BGC, 11, 549, 0, 13, STR_NETWORK_MULTIPLAYER, STR_NULL}, -{ WWT_PANEL, RESIZE_NONE, BGC, 0, 549, 14, 249, 0x0, STR_NULL}, +{ WWT_PANEL, RESIZE_NONE, BGC, 0, 549, 14, 261, 0x0, STR_NULL}, /* LEFT SIDE */ { WWT_PANEL, RESIZE_NONE, BGC, 310, 461, 22, 33, 0x0, STR_NETWORK_ENTER_NAME_TIP}, @@ -508,26 +516,28 @@ static const Widget _network_game_window_widgets[] = { { WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 171, 250, 42, 53, STR_NETWORK_CLIENTS_CAPTION, STR_NETWORK_CLIENTS_CAPTION_TIP}, { WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 251, 290, 42, 53, STR_EMPTY, STR_NETWORK_INFO_ICONS_TIP}, -{ WWT_MATRIX, RESIZE_NONE, BGC, 10, 290, 54, 222, (12 << 8) + 1, STR_NETWORK_CLICK_GAME_TO_SELECT}, -{ WWT_SCROLLBAR, RESIZE_NONE, BGC, 291, 302, 42, 222, STR_NULL, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +{ WWT_MATRIX, RESIZE_NONE, BGC, 10, 290, 54, 234, (12 << 8) + 1, STR_NETWORK_CLICK_GAME_TO_SELECT}, +{ WWT_SCROLLBAR, RESIZE_NONE, BGC, 291, 302, 42, 234, STR_NULL, STR_0190_SCROLL_BAR_SCROLLS_LIST}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 30, 130, 232, 243, STR_NETWORK_FIND_SERVER, STR_NETWORK_FIND_SERVER_TIP}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 180, 280, 232, 243, STR_NETWORK_ADD_SERVER, STR_NETWORK_ADD_SERVER_TIP}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 30, 130, 244, 255, STR_NETWORK_FIND_SERVER, STR_NETWORK_FIND_SERVER_TIP}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 180, 280, 244, 255, STR_NETWORK_ADD_SERVER, STR_NETWORK_ADD_SERVER_TIP}, /* RIGHT SIDE */ -{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 315, 415, 232, 243, STR_NETWORK_START_SERVER, STR_NETWORK_START_SERVER_TIP}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 430, 535, 232, 243, STR_012E_CANCEL, STR_NULL}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 315, 415, 244, 255, STR_NETWORK_START_SERVER, STR_NETWORK_START_SERVER_TIP}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 430, 535, 244, 255, STR_012E_CANCEL, STR_NULL}, + +{ WWT_PANEL, RESIZE_NONE, BGC, 310, 540, 42, 234, 0x0, STR_NULL}, -{ WWT_PANEL, RESIZE_NONE, BGC, 310, 540, 42, 222, 0x0, STR_NULL}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 315, 415, 213, 224, STR_NETWORK_JOIN_GAME, STR_NULL}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 430, 535, 213, 224, STR_NETWORK_REFRESH, STR_NETWORK_REFRESH_TIP}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 315, 415, 201, 212, STR_NETWORK_JOIN_GAME, STR_NULL}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 430, 535, 201, 212, STR_NETWORK_REFRESH, STR_NETWORK_REFRESH_TIP}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 430, 535, 195, 206, STR_NEWGRF_SETTINGS_BUTTON, STR_NULL}, { WIDGETS_END}, }; static const WindowDesc _network_game_window_desc = { - WDP_CENTER, WDP_CENTER, 550, 250, + WDP_CENTER, WDP_CENTER, 550, 262, WC_NETWORK_WINDOW,0, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _network_game_window_widgets, diff --git a/network_udp.c b/network_udp.c index ca66dfd013..680c984fcd 100644 --- a/network_udp.c +++ b/network_udp.c @@ -11,6 +11,7 @@ #include "network_gamelist.h" #include "network_udp.h" #include "variables.h" +#include "newgrf_config.h" // // This file handles all the LAN-stuff @@ -28,6 +29,8 @@ typedef enum { PACKET_UDP_CLIENT_GET_LIST, // Request for serverlist from master server PACKET_UDP_MASTER_RESPONSE_LIST, // Response from master server with server ip's + port's PACKET_UDP_SERVER_UNREGISTER, // Request to be removed from the server-list + PACKET_UDP_CLIENT_GET_NEWGRFS, // Requests the name for a list of GRFs (GRF_ID and MD5) + PACKET_UDP_SERVER_NEWGRFS, // Sends the list of NewGRF's requested. PACKET_UDP_END } PacketUDPType; @@ -42,6 +45,34 @@ static void NetworkSendUDP_Packet(SOCKET udp, Packet* p, struct sockaddr_in* rec static NetworkClientState _udp_cs; +/** + * Serializes the GRFIdentifier (GRF ID and MD5 checksum) to the packet + * @param p the packet to write the data to + * @param c the configuration to write the GRF ID and MD5 checksum from + */ +static void NetworkSend_GRFIdentifier(Packet *p, const GRFConfig *c) +{ + uint j; + NetworkSend_uint32(p, c->grfid); + for (j = 0; j < sizeof(c->md5sum); j++) { + NetworkSend_uint8 (p, c->md5sum[j]); + } +} + +/** + * Deserializes the GRFIdentifier (GRF ID and MD5 checksum) from the packet + * @param p the packet to read the data from + * @param c the configuration to write the GRF ID and MD5 checksum to + */ +static void NetworkRecv_GRFIdentifier(Packet *p, GRFConfig *c) +{ + uint j; + c->grfid = NetworkRecv_uint32(&_udp_cs, p); + for (j = 0; j < sizeof(c->md5sum); j++) { + c->md5sum[j] = NetworkRecv_uint8(&_udp_cs, p); + } +} + DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_FIND_SERVER) { Packet *packet; @@ -59,6 +90,27 @@ DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_FIND_SERVER) NetworkSend_uint8 (packet, NETWORK_GAME_INFO_VERSION); + /* NETWORK_GAME_INFO_VERSION = 4 */ + { + /* Only send the GRF Identification (GRF_ID and MD5 checksum) of + * the GRFs that are needed, i.e. the ones that the server has + * selected in the NewGRF GUI and not the ones that are used due + * to the fact that they are in [newgrf-static] in openttd.cfg */ + const GRFConfig *c; + uint i = 0; + + /* Count number of GRFs to send information about */ + for (c = _grfconfig; c != NULL; c = c->next) { + if (!HASBIT(c->flags, GCF_STATIC)) i++; + } + NetworkSend_uint8 (packet, i); // Send number of GRFs + + /* Send actual GRF Identifications */ + for (c = _grfconfig; c != NULL; c = c->next) { + if (!HASBIT(c->flags, GCF_STATIC)) NetworkSend_GRFIdentifier(packet, c); + } + } + /* NETWORK_GAME_INFO_VERSION = 3 */ NetworkSend_uint32(packet, _network_game_info.game_date); NetworkSend_uint32(packet, _network_game_info.start_date); @@ -109,9 +161,41 @@ DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_RESPONSE) // Find next item item = NetworkGameListAddItem(inet_addr(inet_ntoa(client_addr->sin_addr)), ntohs(client_addr->sin_port)); + item->info.compatible = true; /* Please observer the order. In the order in which packets are sent * they are to be received */ switch (game_info_version) { + case 4: { + GRFConfig *c, **dst = &item->info.grfconfig; + const GRFConfig *f; + uint i; + uint num_grfs = NetworkRecv_uint8(&_udp_cs, p); + + for (i = 0; i < num_grfs; i++) { + c = calloc(1, sizeof(*c)); + NetworkRecv_GRFIdentifier(p, c); + + /* Find the matching GRF file */ + f = FindGRFConfig(c->grfid, c->md5sum); + if (f == NULL) { + /* Don't know the GRF, so mark game incompatible and the (possibly) + * already resolved name for this GRF (another server has sent the + * name of the GRF already */ + item->info.compatible = false; + c->name = FindUnknownGRFName(c->grfid, c->md5sum, true); + SETBIT(c->flags, GCF_NOT_FOUND); + } else { + c->filename = f->filename; + c->name = f->name; + c->info = f->info; + } + SETBIT(c->flags, GCF_COPY); + + /* Append GRFConfig to the list */ + *dst = c; + dst = &c->next; + } + } /* Fallthrough */ case 3: item->info.game_date = NetworkRecv_uint32(&_udp_cs, p); item->info.start_date = NetworkRecv_uint32(&_udp_cs, p); @@ -146,12 +230,50 @@ DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_RESPONSE) snprintf(item->info.hostname, sizeof(item->info.hostname), "%s", inet_ntoa(client_addr->sin_addr)); /* Check if we are allowed on this server based on the revision-match */ - item->info.compatible = + item->info.version_compatible = strcmp(item->info.server_revision, _openttd_revision) == 0 || strcmp(item->info.server_revision, NOREV_STRING) == 0; + item->info.compatible &= item->info.version_compatible; // Already contains match for GRFs break; } + { + /* Checks whether there needs to be a request for names of GRFs and makes + * the request if necessary. GRFs that need to be requested are the GRFs + * that do not exist on the clients system and we do not have the name + * resolved of, i.e. the name is still UNKNOWN_GRF_NAME_PLACEHOLDER. + * The in_request array and in_request_count are used so there is no need + * to do a second loop over the GRF list, which can be relatively expensive + * due to the string comparisons. */ + const GRFConfig *in_request[NETWORK_MAX_GRF_COUNT]; + const GRFConfig *c; + uint in_request_count = 0; + struct sockaddr_in out_addr; + + for (c = item->info.grfconfig; c != NULL; c = c->next) { + if (!HASBIT(c->flags, GCF_NOT_FOUND) || strcmp(c->name, UNKNOWN_GRF_NAME_PLACEHOLDER) != 0) continue; + in_request[in_request_count] = c; + in_request_count++; + } + + if (in_request_count > 0) { + /* There are 'unknown' GRFs, now send a request for them */ + uint i; + Packet *packet = NetworkSend_Init(PACKET_UDP_CLIENT_GET_NEWGRFS); + + NetworkSend_uint8 (packet, in_request_count); + for (i = 0; i < in_request_count; i++) { + NetworkSend_GRFIdentifier(packet, in_request[i]); + } + + out_addr.sin_family = AF_INET; + out_addr.sin_port = htons(item->port); + out_addr.sin_addr.s_addr = item->ip; + NetworkSendUDP_Packet(_udp_client_socket, packet, &out_addr); + free(packet); + } + } + item->online = true; UpdateNetworkGameWindow(false); @@ -300,6 +422,104 @@ DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_MASTER_ACK_REGISTER) } } +/** + * A client has requested the names of some NewGRFs. + * + * Replying this can be tricky as we have a limit of SEND_MTU bytes + * in the reply packet and we can send up to 100 bytes per NewGRF + * (GRF ID, MD5sum and NETWORK_GRF_NAME_LENGTH bytes for the name). + * As SEND_MTU is _much_ less than 100 * NETWORK_MAX_GRF_COUNT, it + * could be that a packet overflows. To stop this we only reply + * with the first N NewGRFs so that if the first N + 1 NewGRFs + * would be sent, the packet overflows. + * in_reply and in_reply_count are used to keep a list of GRFs to + * send in the reply. + */ +DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_GET_NEWGRFS) +{ + uint8 num_grfs; + uint i; + + const GRFConfig *in_reply[NETWORK_MAX_GRF_COUNT]; + Packet *packet; + uint8 in_reply_count = 0; + uint packet_len = 0; + + /* Just a fail-safe.. should never happen */ + if (_udp_cs.has_quit) return; + + DEBUG(net, 6)("[NET][UDP] NewGRF data request from %s:%d", inet_ntoa(client_addr->sin_addr), ntohs(client_addr->sin_port)); + + num_grfs = NetworkRecv_uint8 (&_udp_cs, p); + if (num_grfs > NETWORK_MAX_GRF_COUNT) return; + + for (i = 0; i < num_grfs; i++) { + GRFConfig c; + const GRFConfig *f; + + NetworkRecv_GRFIdentifier(p, &c); + + /* Find the matching GRF file */ + f = FindGRFConfig(c.grfid, c.md5sum); + if (f == NULL) continue; // The GRF is unknown to this server + + /* If the reply might exceed the size of the packet, only reply + * the current list and do not send the other data */ + packet_len += sizeof(c.grfid) + sizeof(c.md5sum) + min(strlen(f->name) + 1, NETWORK_GRF_NAME_LENGTH); + if (packet_len > SEND_MTU - 4) { // 4 is 3 byte header + grf count in reply + break; + } + in_reply[in_reply_count] = f; + in_reply_count++; + } + + if (in_reply_count == 0) return; + + packet = NetworkSend_Init(PACKET_UDP_SERVER_NEWGRFS); + NetworkSend_uint8 (packet, in_reply_count); + for (i = 0; i < in_reply_count; i++) { + char name[NETWORK_GRF_NAME_LENGTH]; + ttd_strlcpy(name, in_reply[i]->name, sizeof(name)); + NetworkSend_GRFIdentifier(packet, in_reply[i]); + NetworkSend_string(packet, name); + } + + NetworkSendUDP_Packet(_udp_server_socket, packet, client_addr); + free(packet); +} + +/** The return of the client's request of the names of some NewGRFs */ +DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_NEWGRFS) +{ + uint8 num_grfs; + uint i; + + /* Just a fail-safe.. should never happen */ + if (_udp_cs.has_quit) return; + + DEBUG(net, 6)("[NET][UDP] NewGRF data reply from %s:%d", inet_ntoa(client_addr->sin_addr),ntohs(client_addr->sin_port)); + + num_grfs = NetworkRecv_uint8 (&_udp_cs, p); + if (num_grfs > NETWORK_MAX_GRF_COUNT) return; + + for (i = 0; i < num_grfs; i++) { + char *unknown_name; + char name[NETWORK_GRF_NAME_LENGTH]; + GRFConfig c; + + NetworkRecv_GRFIdentifier(p, &c); + NetworkRecv_string(&_udp_cs, p, name, sizeof(name)); + + /* Finds the fake GRFConfig for the just read GRF ID and MD5sum tuple. + * If it exists and not resolved yet, then name of the fake GRF is + * overwritten with the name from the reply. */ + unknown_name = FindUnknownGRFName(c.grfid, c.md5sum, false); + if (unknown_name != NULL && strcmp(unknown_name, UNKNOWN_GRF_NAME_PLACEHOLDER) == 0) { + ttd_strlcpy(unknown_name, name, NETWORK_GRF_NAME_LENGTH); + } + } +} + // The layout for the receive-functions by UDP typedef void NetworkUDPPacket(Packet *p, struct sockaddr_in *client_addr); @@ -313,7 +533,9 @@ static NetworkUDPPacket* const _network_udp_packet[] = { RECEIVE_COMMAND(PACKET_UDP_MASTER_ACK_REGISTER), NULL, RECEIVE_COMMAND(PACKET_UDP_MASTER_RESPONSE_LIST), - NULL + NULL, + RECEIVE_COMMAND(PACKET_UDP_CLIENT_GET_NEWGRFS), + RECEIVE_COMMAND(PACKET_UDP_SERVER_NEWGRFS), }; diff --git a/newgrf_config.c b/newgrf_config.c index 7299be4fee..924c3c6280 100644 --- a/newgrf_config.c +++ b/newgrf_config.c @@ -9,6 +9,7 @@ #include "string.h" #include "saveload.h" #include "md5.h" +#include "network_data.h" #include "newgrf.h" #include "newgrf_config.h" @@ -83,9 +84,12 @@ bool FillGRFDetails(GRFConfig *config, bool is_static) void ClearGRFConfig(GRFConfig *config) { - free(config->filename); - free(config->name); - free(config->info); + /* GCF_COPY as in NOT strdupped/alloced the filename, name and info */ + if (!HASBIT(config->flags, GCF_COPY)) { + free(config->filename); + free(config->name); + free(config->info); + } free(config); } @@ -264,6 +268,55 @@ const GRFConfig *FindGRFConfig(uint32 grfid, uint8 *md5sum) return NULL; } +/** Structure for UnknownGRFs; this is a lightweight variant of GRFConfig */ +typedef struct UnknownGRF UnknownGRF; +struct UnknownGRF { + UnknownGRF *next; + uint32 grfid; + uint8 md5sum[16]; + char name[NETWORK_GRF_NAME_LENGTH]; +}; + +/** + * Finds the name of a NewGRF in the list of names for unknown GRFs. An + * unknown GRF is a GRF where the .grf is not found during scanning. + * + * The names are resolved via UDP calls to servers that should know the name, + * though the replies may not come. This leaves "" as name, though + * that shouldn't matter _very_ much as they need GRF crawler or so to look + * up the GRF anyway and that works better with the GRF ID. + * + * @param grfid the GRF ID part of the 'unique' GRF identifier + * @param md5sum the MD5 checksum part of the 'unique' GRF identifier + * @param create whether to create a new GRFConfig if the GRFConfig did not + * exist in the fake list of GRFConfigs. + * @return the GRFConfig with the given GRF ID and MD5 checksum or NULL when + * it does not exist and create is false. This value must NEVER be + * freed by the caller. + */ +char *FindUnknownGRFName(uint32 grfid, uint8 *md5sum, bool create) +{ + UnknownGRF *grf; + static UnknownGRF *unknown_grfs = NULL; + + for (grf = unknown_grfs; grf != NULL; grf = grf->next) { + if (grf->grfid == grfid) { + if (memcmp(md5sum, grf->md5sum, sizeof(grf->md5sum)) == 0) return grf->name; + } + } + + if (!create) return NULL; + + grf = calloc(1, sizeof(*grf)); + grf->grfid = grfid; + grf->next = unknown_grfs; + ttd_strlcpy(grf->name, UNKNOWN_GRF_NAME_PLACEHOLDER, sizeof(grf->name)); + memcpy(grf->md5sum, md5sum, sizeof(grf->md5sum)); + + unknown_grfs = grf; + return grf->name; +} + /* Retrieve a NewGRF from the current config by its grfid */ GRFConfig *GetGRFConfig(uint32 grfid) diff --git a/newgrf_config.h b/newgrf_config.h index 2cee7429bf..42ab2feefa 100644 --- a/newgrf_config.h +++ b/newgrf_config.h @@ -11,6 +11,7 @@ enum { GCF_SYSTEM, GCF_UNSAFE, GCF_STATIC, + GCF_COPY, ///< The data is copied from a grf in _all_grfs }; typedef struct GRFConfig { @@ -52,4 +53,8 @@ char *GRFBuildParamList(char *dst, const GRFConfig *c, const char *last); /* In newgrf_gui.c */ void ShowNewGRFSettings(bool editable, bool show_params, GRFConfig **config); +/* For communication about GRFs over the network */ +#define UNKNOWN_GRF_NAME_PLACEHOLDER "" +char *FindUnknownGRFName(uint32 grfid, uint8 *md5sum, bool create); + #endif /* NEWGRF_CONFIG_H */ diff --git a/newgrf_gui.c b/newgrf_gui.c index b31c0b177b..1b01a1a5a9 100644 --- a/newgrf_gui.c +++ b/newgrf_gui.c @@ -43,9 +43,11 @@ static void ShowNewGRFInfo(const GRFConfig *c, uint x, uint y, uint w, bool show char *s; uint i; - /* Draw filename */ - SetDParamStr(0, c->filename); - y += DrawStringMultiLine(x, y, STR_NEWGRF_FILENAME, w); + /* Draw filename or not if it is not known (GRF sent over internet) */ + if (c->filename != NULL) { + SetDParamStr(0, c->filename); + y += DrawStringMultiLine(x, y, STR_NEWGRF_FILENAME, w); + } /* Prepare and draw GRF ID */ snprintf(buff, lengthof(buff), "%08X", (uint32)BSWAP32(c->grfid));