@kvirund, это про ветку kvirund/utf8-migration. Прикладной случай уже закрыт в мастере (#3762), но общий приём в ветке остался, и он даёт видимый игроку мусор.
Что видит игрок
Из #3761, промпт в бою:
...[Белотур:Невредим] [Сальгард:Невредим] [летучая мышь падальщик:О.тяжело ранен?
Строка обрывается, а на конце знак вопроса. Это не артефакт клиента: обрезка разрезала русскую букву пополам, и перекодировщик на выходе честно показал оставшийся байт как ?.
Откуда
iosystem.cpp:810:
strncat(i, MakePrompt(t).c_str(), kMaxPromptLength);
Обрезка по байтам. В KOI8-R байт равен символу, и хуже усечённого слова ничего не случалось. В UTF-8 разрез приходится на середину многобайтовой последовательности, и вместо слова получается битый символ.
В мастере я поднял kMaxPromptLength с 256 до 1024 — это убирает практический повод срабатывать (тот промпт из issue занимал в UTF-8 ровно 256 байт, впритык). Но сам приём никуда не делся: при достаточно длинном промпте разрежет снова, просто реже.
Что предлагаю
В ветке для этого уже есть готовое: native_text::truncate_offset(s, max_bytes) — наибольшее смещение, не разрывающее символ. Тогда строка кончается целой буквой, даже если не влезла.
const std::string prompt = MakePrompt(t);
strncat(i, prompt.c_str(), native_text::truncate_offset(prompt, kMaxPromptLength));
Сам я править не стал: место твоё, и ты лучше знаешь, не завязано ли что-то ещё на побайтовую семантику этого лимита.
Заодно рядом
Прочесал ветку на тот же приём. truncate_offset/char_offset вне самого native_text используется один раз на всю кодовую базу, а мест, где режут по байтам, заметно больше. Кандидаты, где в буфер может приехать русский текст:
iosystem.cpp:831 — strncat(o, str_goahead, kMaxPromptLength), тот же лимит;
iosystem.cpp:596-605 — склейка подстановки, три обрезки по kMaxInputLength;
identify.cpp:329/339/357/388 — дописывание \r\n в остаток буфера;
jewelry.cpp:326, login.cpp:1896/1905 — копирование аргумента и почты в фиксированные поля.
Часть безобидна (там дописывают ASCII), но проверить стоит все: симптом у этого класса всегда один и тот же — вопросительный знак или ромбик на конце обрезанной строки, и жалоба приходит не от того, кто может объяснить, что случилось.
Для полноты: буферы [128]/[256] я вчера прошёл отдельно, там переполнений не осталось — правки 9ce270254, e529a9463, bcb7aa816, 7aa9abbf5.
@kvirund, это про ветку
kvirund/utf8-migration. Прикладной случай уже закрыт в мастере (#3762), но общий приём в ветке остался, и он даёт видимый игроку мусор.Что видит игрок
Из #3761, промпт в бою:
Строка обрывается, а на конце знак вопроса. Это не артефакт клиента: обрезка разрезала русскую букву пополам, и перекодировщик на выходе честно показал оставшийся байт как
?.Откуда
iosystem.cpp:810:strncat(i, MakePrompt(t).c_str(), kMaxPromptLength);Обрезка по байтам. В KOI8-R байт равен символу, и хуже усечённого слова ничего не случалось. В UTF-8 разрез приходится на середину многобайтовой последовательности, и вместо слова получается битый символ.
В мастере я поднял
kMaxPromptLengthс 256 до 1024 — это убирает практический повод срабатывать (тот промпт из issue занимал в UTF-8 ровно 256 байт, впритык). Но сам приём никуда не делся: при достаточно длинном промпте разрежет снова, просто реже.Что предлагаю
В ветке для этого уже есть готовое:
native_text::truncate_offset(s, max_bytes)— наибольшее смещение, не разрывающее символ. Тогда строка кончается целой буквой, даже если не влезла.Сам я править не стал: место твоё, и ты лучше знаешь, не завязано ли что-то ещё на побайтовую семантику этого лимита.
Заодно рядом
Прочесал ветку на тот же приём.
truncate_offset/char_offsetвне самогоnative_textиспользуется один раз на всю кодовую базу, а мест, где режут по байтам, заметно больше. Кандидаты, где в буфер может приехать русский текст:iosystem.cpp:831—strncat(o, str_goahead, kMaxPromptLength), тот же лимит;iosystem.cpp:596-605— склейка подстановки, три обрезки поkMaxInputLength;identify.cpp:329/339/357/388— дописывание\r\nв остаток буфера;jewelry.cpp:326,login.cpp:1896/1905— копирование аргумента и почты в фиксированные поля.Часть безобидна (там дописывают ASCII), но проверить стоит все: симптом у этого класса всегда один и тот же — вопросительный знак или ромбик на конце обрезанной строки, и жалоба приходит не от того, кто может объяснить, что случилось.
Для полноты: буферы
[128]/[256]я вчера прошёл отдельно, там переполнений не осталось — правки9ce270254,e529a9463,bcb7aa816,7aa9abbf5.