Помогите найти причину, почему js не отправляет в lua

MSIshka

Участник
Автор темы
48
3
Доброго дня, использую скрипт webcore.asi(для вывода html, css, js в samp). Сделал чат всё работает кроме одного. Когда я пишу в инпут нажимаю enter js не отправляет в lua.
Код:
const chat1 = [];

let currentColor = "#FFFFFF"; // Цвет по умолчанию

// Добавление сообщения в чат
function addMessageToChat(message, color = currentColor) {
    const formattedMessage = HTMLstringify(colorify(message, color));
    const chatLog = document.querySelector(".chatlog");

    if (chatLog) {
        chatLog.innerHTML += formattedMessage;
        chatLog.scrollTop = chatLog.scrollHeight; // Автопрокрутка
    }

    chat1.push({ message, color });
}

// Парсинг строки с цветом (#FF0000Текст)
function colorify(message, defaultColor = "#FFFFFF") {
    let result = '';
    let lastIndex = 0;

    const regex = /#([0-9A-F]{6}|[0-9A-F]{3}|[0-9A-F]{4}|[0-9A-F]{5})([^#]*)/gi;

    let match;
    while ((match = regex.exec(message)) !== null) {
        const [fullMatch, hex, text] = match;

        // Если длина <6, дополним до 6 (например, #F00 → #FF0000)
        let fullHex = hex;
        if (hex.length === 3) {
            fullHex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
        } else if (hex.length < 6) {
            fullHex += '0'.repeat(6 - hex.length);
        }

        // Текст до цвета
        if (message.slice(lastIndex, match.index).length > 0) {
            result += `<span style="color:${defaultColor}">${message.slice(lastIndex, match.index)}</span>`;
        }

        // Цветной текст
        result += `<span style="color:#${fullHex}">${text}</span>`;

        lastIndex = match.index + fullMatch.length;
    }

    // Оставшийся текст после последнего цвета
    if (lastIndex < message.length) {
        result += `<span style="color:${defaultColor}">${message.slice(lastIndex)}</span>`;
    }

    return result;
}

// Создание HTML из строки
function HTMLstringify(line) {
    return `<p>${line}</p>`;
}


function addMessageToSAMP(message) {
    console.log("Отправка в SAMP:", message);

    // Проверяем, доступна ли функция
    if (typeof sendMessage === "function") {
        sendMessage([message]); // Отправка через CEF → Lua
    } else {
        console.warn("❌ sendMessage не определена");
    }
}

document.addEventListener("DOMContentLoaded", () => {
    console.log("✅ DOM загружен");

    const input = document.getElementById('chat-input');
    if (input) {
        input.addEventListener('keydown', function(e) {
            if (e.key === 'Enter') {
                const value = input.value.trim();
                if (value.length > 0) {
                    addMessageToSAMP(value);
                    input.value = '';
                }
            }
        });
    }

    // Проверка доступности sendMessage
    setTimeout(() => {
        if (typeof sendMessage === 'function') {
            console.log("✅ sendMessage доступна");
        } else {
            console.warn("❌ sendMessage ещё не определена");
        }
    }, 1000);
});

Код:
local encoding = require 'encoding'
encoding.default = 'CP1251'
u8 = encoding.UTF8


local memory = require 'memory'
local sampev = require 'lib.samp.events'
local ffi = require 'ffi'
local bit = require 'bit'

local hasWebcore, webcore = pcall(require, 'webcore')
local browser = nil
local json = require 'cjson'
local inicfg = require 'inicfg'
local chatActive = false

local hook = {hooks = {}}
addEventHandler('onScriptTerminate', function(scr)
    if scr == script.this then
        for i, hook in ipairs(hook.hooks) do
            if hook.status then
                hook.stop()
            end
        end
    end
end)

local CHAT_VISIBLE_ADDR = 0xCAA65C
ffi.cdef [[
    int __stdcall VirtualProtect(void* lpAddress, unsigned long dwSize, unsigned long flNewProtect, unsigned long* lpflOldProtect);
]]

function hook.new(cast, callback, hook_addr, size)
    local size = size or 5
    local new_hook = {}
    local detour_addr = tonumber(ffi.cast('intptr_t', ffi.cast('void*', ffi.cast(cast, callback))))
    local void_addr = ffi.cast('void*', hook_addr)
    local old_prot = ffi.new('unsigned long[1]')
    
    local org_bytes = ffi.new('uint8_t[?]', size)
    ffi.copy(org_bytes, void_addr, size)
    local hook_bytes = ffi.new('uint8_t[?]', size, 0x90)
    hook_bytes[0] = 0xE9
    ffi.cast('uint32_t*', hook_bytes + 1)[0] = detour_addr - hook_addr - 5
    new_hook.call = ffi.cast(cast, hook_addr)
    new_hook.status = false
    local function set_status(bool)
        new_hook.status = bool
        ffi.C.VirtualProtect(void_addr, size, 0x40, old_prot)
        ffi.copy(void_addr, bool and hook_bytes or org_bytes, size)
        ffi.C.VirtualProtect(void_addr, size, old_prot[0], old_prot)
    end
    new_hook.stop = function() set_status(false) end
    new_hook.start = function() set_status(true) end
    new_hook.start()
    table.insert(hook.hooks, new_hook)
    return setmetatable(new_hook, {
        __call = function(self, ...)
            self.stop()
            local res = self.call(...)
            self.start()
            return res
        end
    })
end

function convertSampColorToHtml(text)
    if type(text) ~= "string" then return "" end
    return text:gsub("{(%x%x%x%x%x%x)}", "#%1")
end

function sendToCEFChat(text)
    if not browser then return end
    text = convertSampColorToHtml(text)
    text = text:gsub('"', '\\"'):gsub("'", "\\'"):gsub("\n", "\\n")
    browser:execute_js(string.format('addMessageToChat("%s")', text))
end

local activateChatAddr = getModuleHandle("samp.dll") + 0x64F50

function main()
    
    if not isSampfuncsLoaded() or not isSampLoaded() then return end  -- Скрываем вывод сообщений SAMP
    while not isSampAvailable() do wait(100) end
    memory.setuint8(sampGetBase() + 0x71480, 0xEB, true)

    local sampBase = getModuleHandle('samp.dll')
    if sampBase == 0 then return end

    -- Правильный адрес функции CChat::Activate
    local activateChatAddr = sampBase + 0x657E0

    -- Хук на функцию активации чата
    hook.new(
        'void(__thiscall *)(void* this)',
        function(this)
            -- Не вызываем оригинальную функцию — тем самым блокируем открытие чата
            sampAddChatMessage(u8:decode("Попытка открыть чат заблокирован"), -1)
        end,
        activateChatAddr,
        5 -- размер JMP инструкции
    )

    if not hasWebcore then
        sampAddChatMessage(u8:decode("Ошибка: WebCore.asi не загружен!"), -1)
        return
    end
    sampAddChatMessage(u8:decode("WebCore версия: ") .. webcore.version(), -1)

    while not webcore.inited() do wait(100) end

    browser = webcore:create_fullscreen("about:blank")

    browser:add_function("sendMessage", function(args)
        local msg = args and args[1]
        if msg then
            print("Lua получил сообщение из JS: " .. msg)
            sampSendChat(msg)
        end
    end)

    browser:set_create_cb(function()
        browser:load_url("file:///cef/chat/chat.html")
    end)

    browser:set_loading_cb(function(_, status)
        if status == 0 then
            print("CEF: Страница загружена")

            browser:execute_js([[
                console.log("CEF JS готов!");
                const input = document.getElementById('chat-input');
                if (input) input.focus();
            ]])
        end
    end)

    while true do
        if browser and not browser:input_active() then
            if isKeyJustPressed(0x42) then -- B
                browser:set_input(true)
                browser:execute_js('toggleCEFChat(true)')
                wait(100)
                browser:execute_js([[
                    var input = document.getElementById('chat-input');
                    if (input) {
                        input.focus();
                    }
                ]])
            end
            if browser ~= nil then
            end
        end
        wait(0)
    end
end

-- Обработка событий из JS

function sampev.onServerMessage(color, text)
    if text then
        -- Извлекаем RGB из цвета
        -- Формируем hex-строку #RRGGBB
        local hexColor = bit.tohex(bit.rshift(color, 8), 6)

        -- Добавляем цвет в начало сообщения
        local formattedText = "#" .. hexColor .. text

        -- Отправляем в CEF
        sendToCEFChat(u8:encode(formattedText))
    end
end

function onScriptTerminate(s, q)
    if s == thisScript() then
        webcore:close(browser) -- close without callback
        browser = nil
    end
end
 
Решение
Функции нужно регистрировать после создания браузера, желательно в колбеке на создание, т.к. сам браузер создаётся немного позже, чем его объект.

С такой конструкцией всё должно работать нормально:
Lua:
browser = webcore:create_fullscreen("about:blank", function() -- создание браузера с колбеком на создание, вместо вызова browser:set_create_cb
    browser:load_url("file:///cef/chat/chat.html") -- ссылку можно было передать при создании
    
    browser:add_function("sendMessage", function(_, _, args) -- первые два аргумента - браузер и имя вызванной функции, дальше - аргументы в виде таблицы
        local msg = args[1]
        if msg then
            print("Lua получил сообщение из JS: " .. msg)
            sampSendChat(msg)
        end...

redcode

🤔
Друг
166
1,375
Функции нужно регистрировать после создания браузера, желательно в колбеке на создание, т.к. сам браузер создаётся немного позже, чем его объект.

С такой конструкцией всё должно работать нормально:
Lua:
browser = webcore:create_fullscreen("about:blank", function() -- создание браузера с колбеком на создание, вместо вызова browser:set_create_cb
    browser:load_url("file:///cef/chat/chat.html") -- ссылку можно было передать при создании
    
    browser:add_function("sendMessage", function(_, _, args) -- первые два аргумента - браузер и имя вызванной функции, дальше - аргументы в виде таблицы
        local msg = args[1]
        if msg then
            print("Lua получил сообщение из JS: " .. msg)
            sampSendChat(msg)
        end
    end)

    -- ...
end)

-- тут место для browser:set_loading_cb
 
  • Нравится
Реакции: MSIshka

MSIshka

Участник
Автор темы
48
3
Функции нужно регистрировать после создания браузера, желательно в колбеке на создание, т.к. сам браузер создаётся немного позже, чем его объект.

С такой конструкцией всё должно работать нормально:
Lua:
browser = webcore:create_fullscreen("about:blank", function() -- создание браузера с колбеком на создание, вместо вызова browser:set_create_cb
    browser:load_url("file:///cef/chat/chat.html") -- ссылку можно было передать при создании
   
    browser:add_function("sendMessage", function(_, _, args) -- первые два аргумента - браузер и имя вызванной функции, дальше - аргументы в виде таблицы
        local msg = args[1]
        if msg then
            print("Lua получил сообщение из JS: " .. msg)
            sampSendChat(msg)
        end
    end)

    -- ...
end)

-- тут место для browser:set_loading_cb
Да спасибо, уже сам разобрался