diff --git a/src/network/core/game.h b/src/network/core/game.h --- a/src/network/core/game.h +++ b/src/network/core/game.h @@ -56,6 +56,8 @@ struct NetworkGameInfo : NetworkServerGa byte map_set; ///< Graphical set }; +const char * GetNetworkRevisionString(); + #endif /* ENABLE_NETWORK */ #endif /* NETWORK_CORE_GAME_H */ diff --git a/src/network/core/tcp_http.cpp b/src/network/core/tcp_http.cpp --- a/src/network/core/tcp_http.cpp +++ b/src/network/core/tcp_http.cpp @@ -45,14 +45,14 @@ NetworkHTTPSocketHandler::NetworkHTTPSoc redirect_depth(depth), sock(s) { - size_t bufferSize = strlen(url) + strlen(host) + strlen(_openttd_revision) + (data == NULL ? 0 : strlen(data)) + 128; + size_t bufferSize = strlen(url) + strlen(host) + strlen(GetNetworkRevisionString()) + (data == NULL ? 0 : strlen(data)) + 128; char *buffer = AllocaM(char, bufferSize); DEBUG(net, 7, "[tcp/http] requesting %s%s", host, url); if (data != NULL) { - seprintf(buffer, buffer + bufferSize - 1, "POST %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: OpenTTD/%s\r\nContent-Type: text/plain\r\nContent-Length: %d\r\n\r\n%s\r\n", url, host, _openttd_revision, (int)strlen(data), data); + seprintf(buffer, buffer + bufferSize - 1, "POST %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: OpenTTD/%s\r\nContent-Type: text/plain\r\nContent-Length: %d\r\n\r\n%s\r\n", url, host, GetNetworkRevisionString(), (int)strlen(data), data); } else { - seprintf(buffer, buffer + bufferSize - 1, "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: OpenTTD/%s\r\n\r\n", url, host, _openttd_revision); + seprintf(buffer, buffer + bufferSize - 1, "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: OpenTTD/%s\r\n\r\n", url, host, GetNetworkRevisionString()); } ssize_t size = strlen(buffer); diff --git a/src/network/network.cpp b/src/network/network.cpp --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -1101,12 +1101,67 @@ void NetworkShutDown() } /** + * How many hex digits of the git hash to include in network revision string. + * Determined as 10 hex digits + 2 characters for -g/-u/-m prefix. + */ +static const uint GITHASH_SUFFIX_LEN = 12; + +/** + * Get the network version string used by this build. + * The returned string is guaranteed to be at most NETWORK_REVISON_LENGTH bytes. + */ +const char * GetNetworkRevisionString() +{ + /* This will be allocated on heap and never free'd, but only once so not a "real" leak. */ + static char *network_revision = nullptr; + + if (!network_revision) { + /* Start by taking a chance on the full revision string. */ + network_revision = stredup(_openttd_revision); + /* Ensure it's not longer than the packet buffer length. */ + if (strlen(network_revision) >= NETWORK_REVISION_LENGTH) network_revision[NETWORK_REVISION_LENGTH - 1] = '\0'; + + /* Release version names are not mangled further. */ + if (IsReleasedVersion()) return network_revision; + + /* Prepare a prefix of the git hash. + * Size is length + 1 for terminator, +2 for -g prefix. */ + assert(_openttd_revision_modified < 3); + char githash_suffix[GITHASH_SUFFIX_LEN + 1] = "-"; + githash_suffix[1] = "gum"[_openttd_revision_modified]; + for (uint i = 2; i < GITHASH_SUFFIX_LEN; i++) { + githash_suffix[i] = _openttd_revision_hash[i-2]; + } + + /* Where did the hash start in the original string? + * Overwrite from that position, unless that would go past end of packet buffer length. */ + ptrdiff_t hashofs = strrchr(_openttd_revision, '-') - _openttd_revision; + if (hashofs + strlen(githash_suffix) + 1 > NETWORK_REVISION_LENGTH) hashofs = strlen(network_revision) - strlen(githash_suffix); + /* Replace the git hash in revision string. */ + strecpy(network_revision + hashofs, githash_suffix, network_revision + NETWORK_REVISION_LENGTH); + assert(strlen(network_revision) < NETWORK_REVISION_LENGTH); // strlen does not include terminator, constant does, hence strictly less than + } + + return network_revision; +} + +static const char *ExtractNetworkRevisionHash(const char *revstr) +{ + return strrchr(revstr, '-'); +} + +/** * Checks whether the given version string is compatible with our version. + * First tries to match the full string, if that fails, attempts to compare just git hashes. * @param other the version string to compare to */ bool IsNetworkCompatibleVersion(const char *other) { - return strncmp(_openttd_revision, other, NETWORK_REVISION_LENGTH - 1) == 0; + if (strncmp(GetNetworkRevisionString(), other, NETWORK_REVISION_LENGTH - 1) == 0) return true; + + const char *hash1 = ExtractNetworkRevisionHash(GetNetworkRevisionString()); + const char *hash2 = ExtractNetworkRevisionHash(other); + return hash1 && hash2 && (strncmp(hash1, hash2, GITHASH_SUFFIX_LEN) == 0); } #endif /* ENABLE_NETWORK */ diff --git a/src/network/network_admin.cpp b/src/network/network_admin.cpp --- a/src/network/network_admin.cpp +++ b/src/network/network_admin.cpp @@ -172,7 +172,7 @@ NetworkRecvStatus ServerNetworkAdminSock Packet *p = new Packet(ADMIN_PACKET_SERVER_WELCOME); p->Send_string(_settings_client.network.server_name); - p->Send_string(_openttd_revision); + p->Send_string(GetNetworkRevisionString()); p->Send_bool (_network_dedicated); p->Send_string(_network_game_info.map_name); diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -343,7 +343,7 @@ NetworkRecvStatus ClientNetworkGameSocke SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN); Packet *p = new Packet(PACKET_CLIENT_JOIN); - p->Send_string(_openttd_revision); + p->Send_string(GetNetworkRevisionString()); p->Send_uint32(_openttd_newgrf_version); p->Send_string(_settings_client.network.client_name); // Client name p->Send_uint8 (_network_join_as); // PlayAs diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -191,7 +191,7 @@ void ServerNetworkUDPSocketHandler::Rece strecpy(ngi.map_name, _network_game_info.map_name, lastof(ngi.map_name)); strecpy(ngi.server_name, _settings_client.network.server_name, lastof(ngi.server_name)); - strecpy(ngi.server_revision, _openttd_revision, lastof(ngi.server_revision)); + strecpy(ngi.server_revision, GetNetworkRevisionString(), lastof(ngi.server_revision)); Packet packet(PACKET_UDP_SERVER_RESPONSE); this->SendNetworkGameInfo(&packet, &ngi);