diff --git a/Extension/Multiplayer/Test/protocol_tests.cpp b/Extension/Multiplayer/Test/protocol_tests.cpp index f8b93e1..fb7bf45 100644 --- a/Extension/Multiplayer/Test/protocol_tests.cpp +++ b/Extension/Multiplayer/Test/protocol_tests.cpp @@ -856,6 +856,17 @@ void dedicated_server_codec() { check(row && row->dedicated && row->name == "Big friendly server" && row->map == ad.map && row->players == 3 && row->capacity == 16 && row->password_required && row->code == format_invite({server, ad.secret}), "Server tags did not round trip"); + // A whole last character is kept, and one the byte limit cuts through is dropped whole. + const auto accented = read_server_tags(dingosdk::server::server_tags({"Skate Caf\xC3\xA9", "Caf\xC3\xA9", 1, 8, false, true, 1}), server); + check(accented && accented->name == "Skate Caf\xC3\xA9" && accented->map == "Caf\xC3\xA9", + "A server name lost its non-ASCII last letter"); + for (std::size_t lead = 0; lead < 3; ++lead) { + std::string euros(lead, 'a'); + for (int i = 0; i < 50; ++i) euros += "\xE2\x82\xAC"; + const auto cut = dingosdk::server::server_tags({euros, "Caf\xC3\xA9", 1, 8, false, true, 1}); + const auto name = cut.substr(cut.find(",n") + 2); + check(cut.size() < 128 && name.size() > lead && (name.size() - lead) % 3 == 0, "A server name was cut inside a character"); + } check(!read_server_tags(tags, player), "A player's ID read as a server"); check(!read_server_tags("reskate,v1,k1,c4", server), "An incompatible server version listed"); } diff --git a/Server/steam_server.cpp b/Server/steam_server.cpp index e2a3fa8..6692169 100644 --- a/Server/steam_server.cpp +++ b/Server/steam_server.cpp @@ -1,4 +1,5 @@ #include "steam_server.h" +#include "server_text.h" #include "Extension/Multiplayer/Net/protocol.h" #ifdef _WIN32 #include @@ -12,6 +13,7 @@ #include #include #endif +#include #include #include #include @@ -45,14 +47,11 @@ struct SteamIP { // Tags are comma separated, so a name loses its commas; the whole list must // stay under Steam's 128 byte limit. std::string tag_text(std::string_view text, std::size_t limit) { - std::string result; - for (const char c : text) { - if (result.size() >= limit) break; - result += c == ',' ? ' ' : c; - } - // Never end inside a UTF-8 sequence. - while (!result.empty() && (static_cast(result.back()) & 0xC0) == 0x80) result.pop_back(); - if (!result.empty() && (static_cast(result.back()) & 0xC0) == 0xC0) result.pop_back(); + std::string result(text); + std::replace(result.begin(), result.end(), ',', ' '); + // Cut at the limit only, and never through a character: a whole last character + // is kept ("Café" stays "Café"), one the limit splits is dropped. + cut_text(result, limit); return result; } // Steam's library prints its own crash-reporting notes ("Setting breakpad