Обновить

Комментарии 8

я когда делал интерфейс и атласы для своего пет-проекта, чотко увидел где бы я вставил скрипт, я бы вставил скрипт на то, чтобы можно было поставить интерфейс, и уже скрипт отправляет все центровки в движок, если вы пользуетесь Юнити или Анриал вы можете не увидеть драуколы, как например в Opengl4.6, так вот, получается, что интерфейс это буквально слои, и его проще получать сразу весь откуда-то, тоесть скрипт собирает(но есть вероятность, что теряем скорость), а апи(это уже С++ или Раст тоесть реализация соединения скрипт-язык-апи(это слово апи - графическое) отрисовки) кидает это в отрисовку 1 куском или не одним смотря как нужно

просто без скрипта таже ситуация, собираем интерфейс в буфер и отправляем в отрисовку, чтобы минимизировать затраты, а если мы пользуемся texturearray(тоесть у нас есть карты иконок и текстур, шрифта, интерфейс в файлах png например, 256х256, 16х16 1 тайлик - тут всё вообще красиво получается) и на язык, то эта идея вообще вкусная ситуация доводится только до центровок и айдишек иконок, со стилями панелек

вопрос на засыпку минусующему как бы вы обновляли интерфейс такого типа,

Скрытый текст
пример интерфейса тут перекрестие, иконки, селектор и ячейки рисуются 1 драу колом, при помощи текстурного массива
пример интерфейса тут перекрестие, иконки, селектор и ячейки рисуются 1 драу колом, при помощи текстурного массива

очевидно, что это

Скрытый текст
    let mut ui = DisplayUI::new();
    // 9 ячеек хотбара
    // ui.add_sprite(100.0, 100.0, 40.0, 40.0, 0.0, 1.0);
    ui.add_sprite(engie.width as f32/2.0-20.0,engie.height as f32/2.0-20.0, 40.0, 40.0, 32.0,1.0);
    for i in 0..9 {
        ui.add_sprite(engie.width as f32/2.0-(180.0) + (i as f32 * 40.0), 40.0, 40.0, 40.0, 1.0,1.0);
    }
    for (i, &item_id) in mod_pl.inventory.slots.iter().enumerate(){
        if item_id == 0 {continue;}
        match item_id{
            2=>{ui.add_sprite(engie.width as f32/2.0-(180.0) + (i as f32 * 40.0)+4.0, 44.0, 32.0, 32.0, 2.0,0.0);},
            3=>{ui.add_sprite(engie.width as f32/2.0-(180.0) + (i as f32 * 40.0)+4.0, 44.0, 32.0, 32.0, 3.0,0.0);},
            101=>{ui.add_sprite(engie.width as f32/2.0-(180.0) + (i as f32 * 40.0)+4.0, 44.0, 32.0, 32.0, 18.0,1.0);}
            _=>{continue;}
        }

    }

    // Селектор поверх нужной ячейки
    ui.add_sprite(engie.width as f32/2.0-(180.0) +((engie.toogle_active_item_hotbar as f32 - 1.0) * 40.0), 40.0, 40.0, 40.0, 0.0,1.0);
....
    //обновление только селектора логика засечек, продолжение логики центровок, засечка на текстуру уже задана и известна
    pub fn update_selector(&mut self, slot: u32, screen_width: f32) {
        // 1. Находим индекс селектора.
        // Если вы всегда добавляете его последним, то это len() - 1.
        let selector_idx = self.instances.len().saturating_sub(1);

        if let Some(selector) = self.instances.get_mut(selector_idx) {
            // 2. Рассчитываем новую позицию
            let new_x = screen_width / 2.0 - 180.0 + ((slot as f32 - 1.0) * 40.0);
            let new_y = 40.0; // Ваша фиксированная высота хотбара

            // 3. Обновляем данные на CPU
            selector.offset = [new_x, new_y];

            // 4. Оптимизация: мгновенно обновляем только этот кусочек в GPU
            unsafe {
                gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
                let offset_in_bytes = (selector_idx * std::mem::size_of::<InstanceData>()) as isize;
                let data_size = 8; // Размер двух f32 (x, y)

                gl::BufferSubData(
                    gl::ARRAY_BUFFER,
                    offset_in_bytes,
                    data_size,
                    selector.offset.as_ptr() as *const _,
                );
            }
        }
    }
...
и просто в шейдере используем 
layout (location = 8)uniform sampler2DArray u_WorldTextures; // Блоки
layout (location = 13)uniform sampler2DArray u_UITextures;    // UI
layout (location = 14)uniform sampler2DArray u_UIText;    // UItext

тогда такое проще собирать в скрипте как раз - это и будет профит, и не натянутое использование интеграции lua, а придаст удобства в движок и управление интерфейсом

тогда шрифт например аски из атласа 256х256 красиво превращается в удобные кодпойнты - тут минус все языки придётся пикселями рисовать, я пример пока недообновил до нарисованного шрифта с текстуратласом, но там принцип тот же, просто указываю центровку и рисую тайл символа из слоя атласа

А что насчет ошибок и остановки в месте ошибки, с возможностью перегрузить песочницу после исправления? Выглядит как естественное решение.

Если я правильно понял вопрос, то на данном этапе из инструментов у нас есть следующее:

Запуск скриптов осуществляется через:

auto run(std::string_view script) -> sol::protected_function_result;
auto runFile(const fs::path &scriptFile) -> sol::protected_function_result;

Которые возвращают sol::protected_function_result, он как раз и позволяет отлавливать ошибки.


Как для всего скрипта:

LuaRuntime lua;
LuaSandbox sandbox(lua, LuaSandbox::Presets::Minimal);

const auto corruptedScript = R"(
  error("Huston we have a problem")
)";
auto result = sandbox.run(corruptedScript);
if (!result.valid()) {
  sol::error err = result;
  std::cerr << "lua error: " << err.what() << "\n";
  // обработка ошибки
} 

Так и для отдельных функций:

const auto trickyCorruptedScript = R"(
  function huston()
    error("Huston we have a problem")
  end
)";

auto result = sandbox.run(trickyCorruptedScript);
if (!result.valid()) {...} // Эту проверку пройдёт

auto fnResult = sandbox["huston"](); // Вызов функции
if (!fnResult.valid()) {...} // <-- А здесь уже не проскочит

С функциями, правда чуть сложнее -- нужно явно указать, что мы хотим именно sol::protected_function_result

// Либо глобально включать соответствующую опцию sol2
#define SOL_ALL_SAFETIES_ON 1

// Либо явно указывать ожидаемый тип результата
sol::protected_function_result fnResult = sandbox["huston"]();

Ну а для перезагрузки песочницы у нас уже есть reset который автоматом подгружает библиотеки, которые были загружены в него ранее.

void LuaSandbox::reset(bool doCollectGrbg /* = false */)
{
    sandbox = sol::environment(lua->state, sol::create);
    sandbox["_G"] = sandbox;

    if (loadedLibs.empty()) {
        // Для конструктора используем список из пресета.
        loadLibs(sandboxPresets.at(preset));
    } else {
        // Если же инкарнация уже не первая, то грузим всё, что было в прошлой жизни.
        loadLibs(loadedLibs);
    }
    // Принудительная уборка мусора, в случае необходимости
    if (doCollectGrbg) {
        lua->state.collect_garbage();
    }
}

Правда полностью стейт не восстановит - для этого всё-таки скрипты придётся по-новой запускать.

А смотрели в сторону Luau? Это такой диалект от Roblox:

  • они собаку съели на песочницах, и за 20 лет собрали много заботливо разложенных граблей. PUC-Rio Lua печально известен своей сложностью изолироваться

  • type annotations и соответствующие плюшки (подсказки, проверки типов)

  • обещают, что они быстрее PUC-Rio

Спасибо за наводку. Слышал про него краем уха, но палкой потыкать так руки и не дошли, т.к. задача была изолировать именно ванильный(-ую?) Lua. Гляну.

Luau is safe to embed. Broadly speaking, this means that even in the face of untrusted (and in Roblox case, actively malicious) code [...]

actively malicious - Вот уж кто-то, а эти точно не одну собаку должны были съесть в части ограничений творческих порывов особо рьяных энтузиастов ))

wow тоже использует lua, есть открытые апи - список, есть возможность писать макросы прям в игре, есть возможность настройки интерфейса, наперёд забегая, там всё запрещено что нельзя, ниразу сколько ставил интерфейсы не встречал проблем, на ваниле был квести, на люа получается

возможно если вы говорите про мододелов и то как ограничивать что можно что нельзя, посмотрите реализацию интеграции Eluna lua engine, это возможно пример

В WoW Lua сильно кастомизированный, правда. Например, код делится на привилегированный и пользовательский и есть понятие загрязнения. И при этом доступны многие "опасные" функции типа loadstring, getfenv/setfenv, getmetatable/setmetatable.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации