Эксклюзив Софт LuaCEF - приватный CEF-биндинг под Lua

LuaCEF - библиотека для Lua, но также имеющая C-API интерфейс (чем собственно и пользуется библиотека-обертка под Lua), позволяющая работать с Chromium Embedded Framework (CEF)



Основная проблема существующей реализации это то, что она не работает с ENB и конфликтует с лаунчерами других проектов, эта же реализация полностью независима от окружения вашей игры, тем самым, избегая эту проблему.
Lua:
LuaCEF::version()                                     -- возвращает версию библиотеки
LuaCEF::inited()                                      -- проверяет, инициализирована ли библиотека
LuaCEF::isAnyInputActive()                            -- проверяет, активен ли ввод хотя бы в одном браузере
LuaCEF::create(url, x, y, width, height, cb)          -- создает браузер по указанным координатам и размерам
LuaCEF::createFullscreen(url, cb)                     -- создает полноэкранный браузер

RemoteWebView::close()                                -- закрывает браузер
RemoteWebView::getTexture()                           -- возвращает указатель на Direct3D-текстуру браузера

RemoteWebView::isInputActive()                        -- возвращает состояние ввода
RemoteWebView::setInput(enable)                       -- устанавливает состояние ввода браузера

RemoteWebView::isFocused()                            -- возвращает состояние фокуса браузера
RemoteWebView::setFocused(enable)                     -- устанавливает состояние фокуса браузера

RemoteWebView::isEnabledFocusMode()                   -- возвращает состояние режима фокусировки
RemoteWebView::setEnabledFocusMode(enable)            -- включает или отключает режим фокусировки

RemoteWebView::isRenderEnabled()                      -- возвращает состояние рендеринга браузера
RemoteWebView::setRenderEnabled(enable)               -- включает или отключает рендеринг браузера

RemoteWebView::isOffscreenRenderingEnabled()          -- возвращает состояние offscreen-рендеринга
RemoteWebView::setOffscreenRenderingEnabled(enable)   -- включает или отключает offscreen-рендеринг

RemoteWebView::getURL()                               -- возвращает текущий URL страницы
RemoteWebView::loadURL(url)                           -- загружает указанный URL

RemoteWebView::reloadPage(ignoreCache)                -- перезагружает страницу
RemoteWebView::goBack()                               -- переходит на предыдущую страницу
RemoteWebView::goForward()                            -- переходит на следующую страницу

RemoteWebView::executeJS(code)                        -- выполняет указанный JavaScript-код
RemoteWebView::addBindFunction(name, cb)              -- регистрирует функцию для вызова из JavaScript

RemoteWebView::addOnAfterCreateCallback(cb)           -- добавляет callback, вызываемый после создания браузера
RemoteWebView::addOnLoadingCallback(cb)               -- добавляет callback, вызываемый после завершения загрузки страницы
RemoteWebView::addOnCloseCallback(cb)                 -- добавляет callback, вызываемый при закрытии браузера

RemoteWebView::setFPS(fps)                            -- устанавливает частоту обновления браузера
RemoteWebView::clearCallbacks()                       -- удаляет все зарегистрированные callback'и

Lua:
---@diagnostic disable: undefined-global

local luacef = require("luacef")
local imgui = require("mimgui")
local vkeys = require("vkeys")
local windowsMessage = require("windows.message")

local webView = nil
local webView2 = nil

function main()
    while not isSampAvailable() do
        wait(100)
    end

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

    sampAddChatMessage("LuaCEF version: " .. luacef.VERSION, 0xFFFF00)

    local pathUrl = "file:///" .. getWorkingDirectory() .. "\\resource\\testcef\\index.html"
    webView = luacef.create(
        pathUrl, 500, 500, 500, 500, function(_)
            sampAddChatMessage("[1] WebView created!", 0x00FF00)
        end)

    webView:setEnabledFocusMode(false)
    webView:setOffscreenRenderingEnabled(true)

    webView:addBindFunction("test", function(_, args)
        for k, v in pairs(args) do
            print(k, v)
        end
    end)

    webView:addBindFunction("inputChanged", function(_, args)
        sampAddChatMessage("Input: " .. args[1], -1)
    end)

    webView:addOnLoadingCallback(function(_, httpStatusCode)
        sampAddChatMessage("[1] Loading status: " .. httpStatusCode, 0x00FF00)

        if httpStatusCode == 200 then
            webView:setFPS(120)
            webView:setInput(true)
            webView:executeJS([[document.getElementById("title").style.color = "red";]])
        end
    end)

    webView:addOnCloseCallback(function(_)
        sampAddChatMessage("WebView closed!", 0x00FF00)
        notifications:remove(webView)
    end)

    --
    webView2 = luacef.create(
        "https://music.wntn.ru", 0, 0, 500, 500, function(_)
            sampAddChatMessage("[2] WebView created!", 0x00FF00)
        end)

    webView2:addOnLoadingCallback(function(_, httpStatusCode)
        sampAddChatMessage("[2] Loading status: " .. httpStatusCode, 0x00FF00)
        if httpStatusCode == 200 then
            webView2:setFPS(120)
            webView2:setInput(true)
        end
    end)

    wait(-1)
end

imgui.OnFrame(
    function() return true end,
    function(player)
        if webView ~= nil then
            local size = imgui.ImVec2(500, 500)
            local start = imgui.ImVec2(500, 500)
            local texture = webView:getTexture()
            imgui.GetBackgroundDrawList():AddImage(texture, start,
                start + size)
        end
    end
).HideCursor = true

addEventHandler("onWindowMessage", function(msg, wparam, lparam)
    if msg == windowsMessage.WM_KEYDOWN or msg == windowsMessage.WM_KEYUP then
        if wparam == vkeys.VK_F9 then
            if webView2 then
                consumeWindowMessage(true, false)
                if msg == windowsMessage.WM_KEYUP then
                    if webView2:isRenderEnabled() then
                        webView2:setRenderEnabled(false)
                        webView2:setInput(false)
                    else
                        webView2:setRenderEnabled(true)
                        webView2:setInput(true)
                    end
                end
            end
        end
    end
end)
HTML:
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>LuaCEF Test</title>
</head>

<body>
    <script>
        function test() {
            lua.call(
                "test",
                "abc",
                123,
                true,
                null,
                undefined,
                { a: 1, b: 2 }
            );
        }

        function sendInput() {
            const text =
                document.getElementById("input").value;

            lua.call(
                "inputChanged",
                text
            );
        }

        function changeTitleColor(color) {
            document.getElementById("title").style.color = color;
        }
    </script>

    <h1 id="title">Hello World!</h1>

    <button onclick="test()">
        Test
    </button>

    <br><br>

    <input id="input" type="text" placeholder="Type something..." style="width: 300px;">

    <button onclick="sendInput()">
        Send Input
    </button>

    <br><br>

    <button onclick="changeTitleColor('red')">
        Red
    </button>

    <button onclick="changeTitleColor('green')">
        Green
    </button>

    <button onclick="changeTitleColor('blue')">
        Blue
    </button>
</body>

</html>

Проект изначально был спонсирован Ethereum Longer специально для Advance RP, но в дальнейшем было принято решение выложить его в общий доступ для всех проектов

Установка: перекинуть все файлы из архива в папку с gta_sa.exe

1784158813650.png






Credits:
Ethereum Longer (спонсор)
@хуега52 (реализация)
@ARMOR - помощь с адаптация под DL и R5 версии​
 
Последнее редактирование:

Eobard_Thawne

Известный
23
21
Круто, спасибо за реализацию, мб у меня у одного такое, но на 2к мониторе крайне шакальное качество окна браузера.
Трайнул несколько вариантов:
LuaCEF.createFullscreen(url, cb)
LuaCEF.create(url, 0, 0, sw, sh, cb)
LuaCEF.create(url, 0, 0, sw * 2, sh * 2, cb)

Во всех режимах качество одинаково плохое, увеличение размеров браузера/viewport чет не помогает
Есть ли способ управлять device scale factor, DPI, backing texture size?
Можно ли отключить фильтрацию текстуры или заставить рендериться 1:1 к screen resolution?
Есть ли рекомендуемый режим для чёткого fullscreen юайки?
Может ли быть, что текущая сборка ASI всегда рендерит браузер в фиксированном низком разрешении?

Заранее спасибо за ответы, благо хоть эта либа не крашит с лаунчерами))
 
  • Влюблен
Реакции: вайега52

вайега52

cache miss
Автор темы
Модератор
3,063
3,299
Круто, спасибо за реализацию, мб у меня у одного такое, но на 2к мониторе крайне шакальное качество окна браузера
Хм, интересно. У меня монитор FHD, поэтому нет возможности потестить напрямую, но попробую занейрослопить данный вопрос
Есть ли способ управлять device scale factor, DPI, backing texture size?
Не уверен, не сталкивался с таким)
Можно ли отключить фильтрацию текстуры или заставить рендериться 1:1 к screen resolution?
Да, можно. Через getTexture получать д3д9 текстуру и уже напрямую ее рендерить (есть пример на imgui, но в коде я рендерю напрямую через D3D9 вызовы)

Понял на счет проблемы с качеством, возможно в ближайшее время займусь этим вопросом на досуге. Спасибо за фидбек)
 

Eobard_Thawne

Известный
23
21
Хм, интересно. У меня монитор FHD, поэтому нет возможности потестить напрямую, но попробую занейрослопить данный вопрос

Не уверен, не сталкивался с таким)

Да, можно. Через getTexture получать д3д9 текстуру и уже напрямую ее рендерить (есть пример на imgui, но в коде я рендерю напрямую через D3D9 вызовы)

Понял на счет проблемы с качеством, возможно в ближайшее время займусь этим вопросом на досуге. Спасибо за фидбек)
Привет, удалось глянуть?)
 

Eobard_Thawne

Известный
23
21
Йоу, кароч, я посмотрел, почему браузер на 2K+ выглядит шакально. Проблема не в самой странице, а в том, что CEF, shared memory и D3D-текстура не всегда работают в одном реальном размере.

Что нашел по местам:
В host/BrowserView.cpp / .hpp надо добавить нормальный GetScreenInfo() для CEF offscreen render и принудительно выставить масштаб:
screen_info.device_scale_factor = 1.0f;
screen_info.rect = view_rect;
screen_info.available_rect = view_rect;
Я так понял без этого CEF может брать DPI монитора, например 125% или 150%, и рисовать буфер другого размера. На 2K/4K это потом растягивается или режется и при этом выглядит неиграбельно.

Там же в SetRect() надо при изменении размера дергать:
browser->GetHost()->WasResized();
browser->GetHost()->NotifyScreenInfoChanged();
Тут надо что б CEF реально понял, что размер окна поменялся, и перерисовал view под новый размер.

В plugin/Plugin.cpp, в CreateFullscreen, лучше не брать размер из RsGlobal.maximumWidth/maximumHeight как единственный источник. Надо взять реальный размер D3D backbuffer через GetBackBuffer() и GetDesc():
device->GetBackBuffer(...);
backBuffer->GetDesc(&desc);
width = desc.Width;
height = desc.Height;
Вычитал в какой-то доке, что RsGlobal может не совпадать с тем, что реально рендерит игра, особенно при windowed mode, ENB, всяких врапперах, нестандартном разрешении и DPI. Если игра реально в 2560x1440, браузер тоже должен быть 2560x1440, получается.

В plugin/render/RenderManager.cpp надо убрать линейную фильтрацию при выводе браузерной текстуры:
D3DTEXF_POINT
D3DTADDRESS_CLAMP
И добавить D3D9 half-pixel offset:
x - 0.5f
y - 0.5f
Тут D3DTEXF_LINEAR мылит текст даже когда текстура совпадает с экраном 1:1. Для UI/браузера лучше point sampling, иначе получается эффект как будто картинку чуть растянули.


Ваще если коротко, то CEF должен рисовать ровно в реальный размер игры, без DPI-масштаба Windows, а D3D должен выводить эту текстуру 1:1 без линейной фильтрации. Тогда LuaCEF.createFullscreen(...) нормально работает на FullHD, 2K, 4K и любых других разрешениях без костылей типа width * 2.

Я форкнуть форкнул, доработки у себя посмотрел, все ок, но ПР не оформлял. Лучше канеш тебе как автору доделать, я больше по Джаве с Котлином. Полюбас где-нибудь память не освободил и пипяу. Еще кстати заметил забавный момент. Если как-то нестабильно вырубить ПК, например если вырубят свет, при повторном заходе в самп открывается Хроминум луаСефа с плашкой мол завершено некорректно, восстановить ли вам окна или нет. Пока не нашел, как чинить

Но то, что не крашит из-за обоссаных лаунчеров это ваще топ пушка, вебкор к сожалению оч сильно с этим конфликтовал, да и исходников там не было, что б можно было чет посмотреть да поправить.
 
  • Нравится
Реакции: вайега52

вайега52

cache miss
Автор темы
Модератор
3,063
3,299
Йоу, кароч, я посмотрел, почему браузер на 2K+ выглядит шакально. Проблема не в самой странице, а в том, что CEF, shared memory и D3D-текстура не всегда работают в одном реальном размере.

Что нашел по местам:
В host/BrowserView.cpp / .hpp надо добавить нормальный GetScreenInfo() для CEF offscreen render и принудительно выставить масштаб:
screen_info.device_scale_factor = 1.0f;
screen_info.rect = view_rect;
screen_info.available_rect = view_rect;
Я так понял без этого CEF может брать DPI монитора, например 125% или 150%, и рисовать буфер другого размера. На 2K/4K это потом растягивается или режется и при этом выглядит неиграбельно.

Там же в SetRect() надо при изменении размера дергать:
browser->GetHost()->WasResized();
browser->GetHost()->NotifyScreenInfoChanged();
Тут надо что б CEF реально понял, что размер окна поменялся, и перерисовал view под новый размер.

В plugin/Plugin.cpp, в CreateFullscreen, лучше не брать размер из RsGlobal.maximumWidth/maximumHeight как единственный источник. Надо взять реальный размер D3D backbuffer через GetBackBuffer() и GetDesc():
device->GetBackBuffer(...);
backBuffer->GetDesc(&desc);
width = desc.Width;
height = desc.Height;
Вычитал в какой-то доке, что RsGlobal может не совпадать с тем, что реально рендерит игра, особенно при windowed mode, ENB, всяких врапперах, нестандартном разрешении и DPI. Если игра реально в 2560x1440, браузер тоже должен быть 2560x1440, получается.

В plugin/render/RenderManager.cpp надо убрать линейную фильтрацию при выводе браузерной текстуры:
D3DTEXF_POINT
D3DTADDRESS_CLAMP
И добавить D3D9 half-pixel offset:
x - 0.5f
y - 0.5f
Тут D3DTEXF_LINEAR мылит текст даже когда текстура совпадает с экраном 1:1. Для UI/браузера лучше point sampling, иначе получается эффект как будто картинку чуть растянули.


Ваще если коротко, то CEF должен рисовать ровно в реальный размер игры, без DPI-масштаба Windows, а D3D должен выводить эту текстуру 1:1 без линейной фильтрации. Тогда LuaCEF.createFullscreen(...) нормально работает на FullHD, 2K, 4K и любых других разрешениях без костылей типа width * 2.

Я форкнуть форкнул, доработки у себя посмотрел, все ок, но ПР не оформлял. Лучше канеш тебе как автору доделать, я больше по Джаве с Котлином. Полюбас где-нибудь память не освободил и пипяу. Еще кстати заметил забавный момент. Если как-то нестабильно вырубить ПК, например если вырубят свет, при повторном заходе в самп открывается Хроминум луаСефа с плашкой мол завершено некорректно, восстановить ли вам окна или нет. Пока не нашел, как чинить

Но то, что не крашит из-за обоссаных лаунчеров это ваще топ пушка, вебкор к сожалению оч сильно с этим конфликтовал, да и исходников там не было, что б можно было чет посмотреть да поправить.
Спасибо за поправки, как будет возможность, посмотрю это, но если не жалко, то можешь пока оставить свой форк где-нибудь, я до момента, как исправлю, могу закрепить его в теме)