Проблема с отображением цветовых значений в диалогах style=2

saradaken

Участник
Автор темы
59
6
Версия MoonLoader
.027.0-preview
Здравствуйте. Я работаю над локализацией диалогов и различных частей игры, но у меня возникла проблема с отображением цветовых значений. Я подозреваю, что проблема связана исключительно с диалогами со стилем style=2. Когда я подставляю цветовое значение в диалог, оно исчезает, хотя при отладке (debug) ID диалога сохраняется. Похоже, причина кроется в цвете. Остальные стили работают нормально. Я не уверен в причине. В настоящее время для отображения символов я использую преобразование UTF-8 в HTML-сущности (ASCII), например, ก -> ก.




1792819402415.png
1792822111089.png


.

up
 

Вложения

  • TRANSLATE_dialog_sheets_gemma4_best_safe.lua
    123.6 KB · Просмотры: 0
Последнее редактирование:

e11evated

Участник
97
53
Мы с другом посидели над этим в игре: подсунули в cef аризоны свой отладочный JS и прогнали штук 40 тестовых диалогов. Цвет и &#...; тут ни при чём, всё упирается в длину строки.

В списке (style 2) первая строка должна быть не длиннее 130 байт. Если длиннее, игра текст в cef отдаёт (в setDialogInfo он приходит целиком), но окно не показывает. Поэтому у тебя ID есть, а окна нет. На остальные строки, общий размер, msgbox и tablist это не распространяется. Первая строка на 129-130 байт открывается, на 135 уже нет. А вот вторая или последняя строка на 600 байт открывается спокойно, и 70 строк на 4270 байт тоже. Цвет вместе с тайским сущностями тоже открывается, и цвета нормальные.

Почему кажется, что виноват цвет: одна тайская буква как ก это 7 байт, а {RRGGBB} докидывает ещё 8.

Как сделать: гнать тайский не сущностями, а по байту на букву (CP874) внутри [[th:...]]. SAMP примет это как обычный CP1251, а маленький хук в cef перед отрисовкой превратит обратно в тайский. Строки получаются в 7 раз короче, цвета остаются. Проверили на меню с цветами #FFFFFF/#FFD24A вперемешку, показывается как надо.

Сначала добавь это рядом с utf8ToHtmlEntities:

Lua:
-- Тайский (U+0E01..U+0E5B) -> [[th:<байты CP874>]], 1 байт на букву вместо 7
local function thaiPack(s)
    local out, run, i = {}, {}, 1
    local function flush() if #run > 0 then out[#out + 1] = '[[th:' .. table.concat(run) .. ']]'; run = {} end end
    while i <= #s do
        local b1, b2, b3 = s:byte(i, i + 2)
        local off = (b1 == 0xE0 and (b2 == 0xB8 or b2 == 0xB9) and b3) and (b2 - 0xB8) * 64 + (b3 - 0x80) or nil
        if off and off >= 1 and off <= 0x5B then
            run[#run + 1] = string.char(0xA0 + off); i = i + 3
        elseif b1 == 32 and #run > 0 then
            run[#run + 1] = ' '; i = i + 1
        else
            flush(); out[#out + 1] = s:sub(i, i); i = i + 1
        end
    end
    flush()
    return table.concat(out)
end

-- JS-хук: [[th:...]] -> тайский перед отрисовкой диалога в cef
local THAI_DECODER_JS = [=[
(() => { try {
  if (window.__icsThaiDec) return;
  window.__icsThaiDec = 1;
  const dec = new TextDecoder('windows-1251'), back = {};
  for (let b = 0x80; b <= 0xFF; b++) back[dec.decode(new Uint8Array([b]))] = b;
  const fix = s => typeof s !== 'string' ? s : s.replace(/\[\[th:([^\]]*)\]\]/g, (m, t) =>
    [...t].map(c => { const b = back[c]; return b >= 0xA1 && b <= 0xFB ? String.fromCharCode(0x0E00 + b - 0xA0) : c; }).join(''));
  const o = window.cef.setDialogInfo;
  window.cef.setDialogInfo = function (id, type, header, body, b1, b2, ...rest) {
    return o.call(this, id, type, fix(header), fix(body), fix(b1), fix(b2), ...rest);
  };
} catch (e) {} })();
]=]

local function evalCef(code)
    local bs = raknetNewBitStream()
    raknetBitStreamWriteInt8(bs, 17)
    raknetBitStreamWriteInt32(bs, 0)
    raknetBitStreamWriteInt16(bs, #code)
    raknetBitStreamWriteInt8(bs, 0)
    raknetBitStreamWriteString(bs, code)
    raknetEmulPacketReceiveBitStream(220, bs)
    raknetDeleteBitStream(bs)
end

JS тут в [=[ ... ]=], не в [[ ... ]], это не просто так. В регулярке есть ]], и обычная длинная строка Lua на нём оборвётся. Я сам на этом споткнулся.

Дальше в utf8ToHtmlEntities надо пропускать тайские буквы, чтобы их потом упаковал thaiPack. Перед return '&#' .. cp .. ';' вставь:

Lua:
        if cp >= 0x0E01 and cp <= 0x0E5B then return ch end

И сделай общий кодировщик:

Lua:
local function encodeDlg(s) return thaiPack(utf8ToHtmlEntities(s)) end

В showTranslatedDialog перед sampShowDialog вызывай хук и кодируй через него:

Lua:
    evalCef(THAI_DECODER_JS)   -- повторный вызов ничего не делает; после реконнекта cef хук ставится заново
    -- на всякий случай: первая строка списка должна быть <= 130 байт
    if d.style == 2 then
        local first, rest = body:match('^([^\n]*)(.*)$')
        if #encodeDlg(first) > 130 then body = stripColors(first) .. rest end
    end
    sampShowDialog(d.id, encodeDlg(title), encodeDlg(body), encodeDlg(b1), encodeDlg(b2), d.style)

В layoutDialog поменяй на local function entLen(s) return #encodeDlg(s) end, тогда страницы будут считаться по реальному размеру.

Пара мелочей. Если хук почему-то не встанет, вместо тайского увидишь [[th:...]] с кракозябрами. Мы ставили его за полсекунды до показа диалога, так что если вылезет на первом диалоге после захода, вызови evalCef(THAI_DECODER_JS) ещё раз в main() после спавна. И для списков cef кладёт в inputtext текст выбранной строки, то есть тайский, а не исходный русский. Если сервер где-то смотрит на inputtext, а не на listitem, подставь в onSendDialogResponse исходную строку из d.args.
 
  • Влюблен
Реакции: saradaken

saradaken

Участник
Автор темы
59
6
Мы с другом посидели над этим в игре: подсунули в cef аризоны свой отладочный JS и прогнали штук 40 тестовых диалогов. Цвет и &#...; тут ни при чём, всё упирается в длину строки.

В списке (style 2) первая строка должна быть не длиннее 130 байт. Если длиннее, игра текст в cef отдаёт (в setDialogInfo он приходит целиком), но окно не показывает. Поэтому у тебя ID есть, а окна нет. На остальные строки, общий размер, msgbox и tablist это не распространяется. Первая строка на 129-130 байт открывается, на 135 уже нет. А вот вторая или последняя строка на 600 байт открывается спокойно, и 70 строк на 4270 байт тоже. Цвет вместе с тайским сущностями тоже открывается, и цвета нормальные.

Почему кажется, что виноват цвет: одна тайская буква как &#3585; это 7 байт, а {RRGGBB} докидывает ещё 8.

Как сделать: гнать тайский не сущностями, а по байту на букву (CP874) внутри [[th:...]]. SAMP примет это как обычный CP1251, а маленький хук в cef перед отрисовкой превратит обратно в тайский. Строки получаются в 7 раз короче, цвета остаются. Проверили на меню с цветами #FFFFFF/#FFD24A вперемешку, показывается как надо.

Сначала добавь это рядом с utf8ToHtmlEntities:

Lua:
-- Тайский (U+0E01..U+0E5B) -> [[th:<байты CP874>]], 1 байт на букву вместо 7
local function thaiPack(s)
    local out, run, i = {}, {}, 1
    local function flush() if #run > 0 then out[#out + 1] = '[[th:' .. table.concat(run) .. ']]'; run = {} end end
    while i <= #s do
        local b1, b2, b3 = s:byte(i, i + 2)
        local off = (b1 == 0xE0 and (b2 == 0xB8 or b2 == 0xB9) and b3) and (b2 - 0xB8) * 64 + (b3 - 0x80) or nil
        if off and off >= 1 and off <= 0x5B then
            run[#run + 1] = string.char(0xA0 + off); i = i + 3
        elseif b1 == 32 and #run > 0 then
            run[#run + 1] = ' '; i = i + 1
        else
            flush(); out[#out + 1] = s:sub(i, i); i = i + 1
        end
    end
    flush()
    return table.concat(out)
end

-- JS-хук: [[th:...]] -> тайский перед отрисовкой диалога в cef
local THAI_DECODER_JS = [=[
(() => { try {
  if (window.__icsThaiDec) return;
  window.__icsThaiDec = 1;
  const dec = new TextDecoder('windows-1251'), back = {};
  for (let b = 0x80; b <= 0xFF; b++) back[dec.decode(new Uint8Array([b]))] = b;
  const fix = s => typeof s !== 'string' ? s : s.replace(/\[\[th:([^\]]*)\]\]/g, (m, t) =>
    [...t].map(c => { const b = back[c]; return b >= 0xA1 && b <= 0xFB ? String.fromCharCode(0x0E00 + b - 0xA0) : c; }).join(''));
  const o = window.cef.setDialogInfo;
  window.cef.setDialogInfo = function (id, type, header, body, b1, b2, ...rest) {
    return o.call(this, id, type, fix(header), fix(body), fix(b1), fix(b2), ...rest);
  };
} catch (e) {} })();
]=]

local function evalCef(code)
    local bs = raknetNewBitStream()
    raknetBitStreamWriteInt8(bs, 17)
    raknetBitStreamWriteInt32(bs, 0)
    raknetBitStreamWriteInt16(bs, #code)
    raknetBitStreamWriteInt8(bs, 0)
    raknetBitStreamWriteString(bs, code)
    raknetEmulPacketReceiveBitStream(220, bs)
    raknetDeleteBitStream(bs)
end

JS тут в [=[ ... ]=], не в [[ ... ]], это не просто так. В регулярке есть ]], и обычная длинная строка Lua на нём оборвётся. Я сам на этом споткнулся.

Дальше в utf8ToHtmlEntities надо пропускать тайские буквы, чтобы их потом упаковал thaiPack. Перед return '&#' .. cp .. ';' вставь:

Lua:
        if cp >= 0x0E01 and cp <= 0x0E5B then return ch end

И сделай общий кодировщик:

Lua:
local function encodeDlg(s) return thaiPack(utf8ToHtmlEntities(s)) end

В showTranslatedDialog перед sampShowDialog вызывай хук и кодируй через него:

Lua:
    evalCef(THAI_DECODER_JS)   -- повторный вызов ничего не делает; после реконнекта cef хук ставится заново
    -- на всякий случай: первая строка списка должна быть <= 130 байт
    if d.style == 2 then
        local first, rest = body:match('^([^\n]*)(.*)$')
        if #encodeDlg(first) > 130 then body = stripColors(first) .. rest end
    end
    sampShowDialog(d.id, encodeDlg(title), encodeDlg(body), encodeDlg(b1), encodeDlg(b2), d.style)

В layoutDialog поменяй на local function entLen(s) return #encodeDlg(s) end, тогда страницы будут считаться по реальному размеру.

Пара мелочей. Если хук почему-то не встанет, вместо тайского увидишь [[th:...]] с кракозябрами. Мы ставили его за полсекунды до показа диалога, так что если вылезет на первом диалоге после захода, вызови evalCef(THAI_DECODER_JS) ещё раз в main() после спавна. И для списков cef кладёт в inputtext текст выбранной строки, то есть тайский, а не исходный русский. Если сервер где-то смотрит на inputtext, а не на listitem, подставь в onSendDialogResponse исходную строку из d.args.

Большое спасибо. Вы мне очень помогли. Могу ли я задать еще вопросы, кроме диалогов? Для чата или других текстовых элементов (textdraw) в игре, возможно ли использовать похожий метод, чтобы корректно отображать языки с разными кодировками?
 

e11evated

Участник
97
53
Большое спасибо. Вы мне очень помогли. Могу ли я задать еще вопросы, кроме диалогов? Для чата или других текстовых элементов (textdraw) в игре, возможно ли использовать похожий метод, чтобы корректно отображать языки с разными кодировками?
в лс форума напиши тогда