Информация Глубокое погружение в moonly №1 | Основа и диагностика

Приветствую, бластхакеры!

Сейчас вы попадете на тему, которая раскроет глубокие знания о moonly и его возможностях авто-обновления c помощью GitHub Actions (workflow). Тема может показаться очень сложной. Поэтому приготовьте себе чай, усаживайтесь поудобнее, и мы будем изучать ее постепенно, без спешки.

Дисклеймер.
Этот материал — не просто компиляция информации из открытых источников или пересказ документации. Это результат моих многолетних наработок, проб и ошибок, а также глубокого опыта, полученного в процессе разработки различных проектов на MoonLoader (Lua) с использованием moonly и систем авто-обновления. Здесь я делюсь своим авторским видением и проверенными на практике решениями, которые показали свою эффективность в реальных "боевых" условиях. Моя цель — передать вам не просто знания, а квинтэссенцию практического опыта. Также использован ИИ для исправления грамматических ошибок и прочее.

Содержание

1. Введение: Глубокое погружение в moonly для опытных разработчиков.
2. Основа moonly: Менеджер проектов Lua-скриптов под MoonLoader.
3. Работа с Moonly: Установка, инициализация проекта и основы сборки.

1. Введение: Глубокое погружение в moonly для опытных разработчиков.

За все время мы писали скрипты в одном файл без архитектуры, и получилось сложно читать и снижается качество проекта. Разработал тулс выше 10к строк? Жди беды в будущем. Да и ещё сложно повозиться с авто-обновлением. До тех пор никто не нашли лучшее решение, кроме как запихнуть в githelper.lua.

Как только появился менеджер проектов (moonly), сразу же приступил к его тестированию. Не скрою, на старте было много багов и недочетов. Однако сейчас moonly стабилизировался, и это позволяет с уверенностью использовать его для полноценной разработки сложных проектов, а также внедрение авто-обновление в GitHub Actions (в следующей статье).

Для себя называю проектом Lua - проект, который разрабатывается в moonly. Модули - часть папки в проект, например, событие, константы и ядро.

2. Основа moonly: Менеджер проектов Lua-скриптов под MoonLoader.

moonly — это менеджер проектов для разработки Lua-скриптов под MoonLoader. Он предоставляет структуру, похожую на полноценные языки программирования, позволяя писать чище, масштабнее и удобнее. В отличие от традиционного подхода, где все скрипты лежат в одной папке moonloader, moonly предлагает создавать отдельные проекты в своей собственной директории, что помогает лучше организовать код, использовать модули, работать с зависимостями и интегрироваться с редакторами. Также moonly ведет логгирование в moonloader/moonly.log и имеет конфигурацию по пути moonloader/moonly.json.
Прежде чем мы углубимся в тонкости, настоятельно рекомендую ознакомиться с темой, где представлены основы. Не забываем ставить лайк и звезду в GitHub.

По моему глубокому убеждению, moonly является одним из лучших изобретений и наиболее совершенных инструментов для разработчиков Lua под MoonLoader.

Менеджер проектов (moonly) фундаментально отличается от githelper.lua, предлагая значительно более широкий функционал и профессиональный подход к управлению проектами. Среди ключевых особенностей moonly можно выделить:
- Стабильность и активная поддержка: Гарантия надежности и постоянного развития.
- Встроенный логгер и консоль: Полный контроль над процессом отладки прямо в игре (даже в одиночной GTA:SA).
- Мощный бандлер: Автоматическое объединение нескольких исходных Lua-файлов в единый скрипт, упрощая распространение и загрузку.
- Автоматическая "горячая" перезагрузка (Hot-Reload): Мгновенное применение изменений после модификации файлов в директориях src/ и lib/, значительно ускоряющее итерацию разработки.
- Интуитивно понятный CLI (Command Line Interface): Расширенные возможности для автоматизации задач и интеграции в CI/CD процессы.

3. Работа с Moonly: Установка, инициализация проекта и основы сборки.

Для начала работы с Moonly необходимо установить две его основные компоненты: скрипт для MoonLoader и утилиту командной строки (CLI).

3.1. Установка moonly.lua в MoonLoader.

1. Перейдите на страницу последнего релиза Moonly: GitHub Releases moonly.lua.
2. Скачайте файл moonly.lua из раздела Assets.
3. Поместите его в корневую папку moonloader вашей GTA:SA.

3.2. Установка Moonly CLI и инициализация нового проекта.

1. Перейдите на страницу последнего релиза Moonly CLI: GitHub Releases moonly CLI.
2. Скачайте файл moonly.exe из раздела Assets.
3. Создайте новую папку moonly в корневой директории GTA:SA (например, gtasa/moonly/).
4. Поместите скачанный moonly.exe в эту папку.
5. Создайте рабочую папку для вашего нового проекта в удобном для вас месте (например, gtasa/moonly/test-project). Это будет корневая директория вашего будущего Lua-проекта.
6. Откройте эту папку проекта в VS Code и запустите встроенный терминал (Ctrl + ~).
7. Инициализируйте новый moonly-проект, выполнив команду: moonly init -n [название_проекта], Например: moonly init -n test-project

После создания проекта, скопируйте файл moonly.exe в корневую папку вашего нового проекта (например, test-project/moonly.exe). Это обеспечит его доступность при работе с GitHub Fork и облегчит дальнейшие действия с проектом без необходимости добавления moonly.exe в системный PATH.

3.3. Настройка VS Code для разработки с moonly.

Для комфортной разработки и корректной работы авто-дополнения (IntelliSense) рекомендуется настроить расширение Lua от sumneko в VS Code.
1. Откройте Addon Manager расширения sumneko Lua (F1 -> Lua: Open Addon Manager...).
2. Укажите в поиск moonloader и добавите.
3. Затем создайте файл .vscode/settings.json в корне вашего проекта и вставьте следующий код:
JSON:
{
    // Generated by moonly
    "[lua]": {
        "editor.defaultFormatter": "sumneko.lua",
        "files.encoding": "windows1251",
        "editor.tabSize": 2,
        "editor.insertSpaces": true,
        "editor.tabCompletion": "off"
    },
    "Lua.runtime.unicodeName": true,
    "Lua.workspace.library": [
        "${addons}/moonloader/module/library",
        "lib"
    ],
    "Lua.workspace.checkThirdParty": false,
    "Lua.runtime.version": "LuaJIT",
    "Lua.runtime.path": [
        "./src/?.lua",
        "./src/?/init.lua",
        "./lib/?.lua",
        "./lib/?/init.lua"
    ],
    "Lua.diagnostics.globals": [
        "main",
        "MOONLY_ENVIRONMENT",
        "onSystemMessage",
        "_"
    ],
    "Lua.workspace.ignoreDir": [
        "lib",
        "./.vscode",
    ],
    // End generate moonly
    // VSCode config for repository
    "window.zoomLevel": -1,
    "editor.formatOnSave": true,
    "editor.fontSize": 15,
    "editor.unicodeHighlight.nonBasicASCII": false,
    "editor.unicodeHighlight.allowedLocales": {
        "ru": true
    },
    "Lua.format.defaultConfig": {
        "quote_style": "single",
        "indent_style": "space",
        "indent_size": "2",
        "continuation_indent": "2",
        "max_line_length": "180",
        "space_around_math_operator": "true"
    },
}

3.4. Структура проекта и инициализация.

После инициализации проекта командой moonly init, автоматически создаются основные директории. Вам же необходимо убедиться, что у вас есть:
• Папка src/: Здесь будут располагаться ваши основные исходные скрипты.
• Папка lib/: Сюда следует помещать сторонние библиотеки, которые вы используете в своем проекте.

Теперь создадим стартовый файл инициализации. В папке src/ создайте файл с названием init.lua.
Lua:
-- src/init.lua:
function main()
    if not isSampfuncsLoaded() or not isSampLoaded() then return end
    while not isSampAvailable() do wait(0) end
   
    print('Привет мир!')
   
    while true do
        wait(0)
    end
end

Что такое init.lua? Слово init является сокращением от "initialize" (инициализация). В контексте Lua, init.lua играет роль точки входа в ваш проект, подобно main.py в Python или index.js в Node.js. Когда ваш скрипт будет запускаться, MoonLoader (через moonly) будет искать и выполнять именно этот файл для начала работы.

3.5. Первый запуск проекта.

Теперь, когда базовые файлы созданы, сохраните изменения и запустите игру, moonly автоматически подхватит ваш проект.
Например, если в вашем src/init.lua будет простой print('Привет мир!') или аналогичная строка, вы увидите что-то вроде:
Код:
[20:12:05.415429] (system)  Loading script 'C:\Games\gtasa\moonloader\moonly.lua'...
[20:12:05.415429] (debug)   New script: 0E03F82C
[20:12:05.615687] (system)  Loading script 'C:\Users\mikhail\AppData\Local\Temp\moonly\test-project.lua'...
[20:12:05.615687] (debug)   New script: 0E03F9B4
[20:12:05.671686] (system)  test-project.lua: Loaded successfully.
[20:12:05.672688] (system)  moonly: Loaded successfully.
[20:12:25.959677] (script)  test-project.lua: Привет мир!

Или можно так с модулем ядро:
Lua:
-- src/core/init.lua
local function initializeScript()
    if not isSampfuncsLoaded() or not isSampLoaded() then return end
    while not isSampAvailable() do wait(0) end
   
    print('Привет мир!')
   
    while true do
        wait(0)
    end
end

_G['main'] = initializeScript
Lua:
-- src/init.lua
require('core')

Сохраняем и что увидим:
Код:
[20:21:21.044269] (system)  test-project.lua: Script terminated. (0E03F9B4)
[20:21:21.044269] (system)  Loading script 'C:\Users\mikhail\AppData\Local\Temp\moonly\test-project.lua'...
[20:21:21.044269] (debug)   New script: 18BABC74
[20:21:21.046277] (system)  test-project.lua: Loaded successfully.
[20:21:21.046277] (script)  test-project.lua: Привет мир!

В контексте moonly, даже при наличии всего одной строки в src/init.lua, вы уже можете инициировать ядро скрипта и вызвать свою основную функцию для запуска скрипта. Это подчеркивает главную особенность moonly: он предоставляет практически безграничные архитектурные возможности и полную свободу в построении структуры проекта.

Вы вольны создавать архитектуру любой сложности: от миниатюрной "деревни" с несколькими модулями до полноценного "мегаполиса" с сотнями файлов и сложной системой зависимостей. moonly не диктует жестких правил, а дает инструменты для масштабирования вашего Lua-проекта до любого уровня сложности, какой только потребуется.

Вот что получилось дерево проекта:
Код:
test-project
├───moonly.exe
├───project.json
├───.vscode
│   └───settings.json
├───lib
└───src
    ├───init.lua
    └───core
        └───init.lua

3.6. Сборка проекта (Bundling) и оптимизация.

moonly предлагает мощные инструменты для сборки вашего Lua-проекта. Команда moonly bundle объединяет ваших исходных файлов (src/) в один исполняемый скрипт.
1. Откройте командную строку (CLI) в корневой папке вашего проекта (test-project/).
2. Выполните команду: moonly bundle
3. После выполнения команды будет создана папка dist/ в корне вашего проекта.

Содержание собранный скрипт test-project/dist/test-project.lua:
Lua:
-- dist/test-project.lua
-- Bundled using Moonly CLI
-- Get Moonly CLI from <github.com/themusaigen/moonly-command-tool>
-- Get Moonly from <github.com/themusaigen/moonly>

MOONLY_BUNDLED = true
MOONLY_BUNDLE_TIMESTAMP = 1767782209645

-- Package <init.lua> (src\core\init.lua)
package.preload["core"] = function()
  local function initializeScript()
    if not isSampfuncsLoaded() or not isSampLoaded() then return end
    while not isSampAvailable() do wait(0) end

    print('Hello world!')

    while true do
      wait(0)
    end
  end

  _G['main'] = initializeScript

end

-- Core file <init.lua> (src\init.lua)
require('core')

При открытии test-project.lua вы увидите, что moonly автоматически объединил все ваши исходные файлы и добавил две важные константы:
- MOONLY_BUNDLED = true: Эта константа указывает на то, что данный файл является результатом сборки (бандлинга) Moonly.
- MOONLY_BUNDLE_TIMESTAMP = число: Время компиляции (timestamp), которое может использоваться как версия сборки или для других целей отслеживания.

3.7. Двойная компиляция: Открытый и закрытый скрипты (.lua и .luac)

Для повышения безопасности или оптимизации, moonly позволяет создавать сборку открытый .lua файл, а затем с помощью батника создавать второй сторонний компилированный в байт-код LuaJIT (.luac).
1. Скачайте luajit в конце выложенных файлов.
2. Скопируйте эту папку luajit из архива в корневую директорию вашего проекта (должно быть gtasa/moonly/test-project/luajit).
3. В корневой папке вашего проекта создайте новый батник compile.bat. И вставьте в него следующий код:
Bash:
@echo off

set "name_project=test-project"
set "latest_path=%~dp0"
set "input_file=%latest_path%\dist\%name_project%.lua"

echo Bundling script ...
moonly bundle

echo Compiling script ...
cd /D %latest_path%\luajit
luajit.exe -b "%input_file%" "%latest_path%\dist\%name_project%.luac"

echo Success compiled!
cd %latest_path%

Внимание! Если в VS Code ругается что в папке luajit, dist и lib присутствуют ошибки. Добавляем игнор папки в .vscode/settings.json:
JSON:
"Lua.workspace.ignoreDir": [
    "./.vscode",
    "./lib",
    "./dist",
    "./luajit"
],

3.8. Тестовая диагностика ошибок компиляции.

Давайте теперь намеренно создадим синтаксическую ошибку, чтобы продемонстрировать, как Lua sumneko и LuaJIT помогают её обнаруживать. Намеренно добавьте синтаксическую ошибку. Например, оставьте одиночную цифру 4 в конце файла src/core/init.lua или в любом другом неожиданном месте и сохраняем. Видим что расширение Lua sumneko выделяет красный файл, это обозначает как фатальная ошибка, а значит что вообще-то не будет работать, в компиляции и в игре.
Пишем в командной строке compile.

Код:
C:\Games\gtasa\moonly\test-project>compile
Bundling script ...
-> Added file header.
-> Performing constant propagation.
 --> Added 'MOONLY_BUNDLED' constant.
 --> Added 'MOONLY_BUNDLE_TIMESTAMP' constant.
-> Collecting scripts from 'src' directory.
 -> Bundled 'src\core\init.lua' script as package 'core'.
-> Bundled core 'init.lua'.
Compiling script ...
luajit: C:\Games\gtasa\moonly\test-project\\dist\test-project.lua:22: unexpected symbol near '4'
Success compiled!

C:\Games\gtasa\moonly\test-project>

Как и ожидалось, компиляция провалилась. Сообщение luajit: C:\Games\gtasa\moonly\test-project\\dist\test-project.lua:22: unexpected symbol near '4' четко указывает на проблему. LuaJIT не понимает, что означает одиночная цифра 4 в этом контексте, ожидая завершения файла или какой-либо допустимой конструкции.

Этот пример наглядно демонстрирует, что компиляция с LuaJIT является важным этапом для обеспечения корректной работы скрипта и раннего выявления синтаксических ошибок. Вместо того чтобы сталкиваться с runtime-ошибками уже в игре, мы можем обнаружить их на стадии сборки. Ошибка есть — значит, её нужно найти и исправить. Такой подход позволяет убедиться в синтаксической чистоте кода до его сборки.

# # #

В следующей статье мы углубимся в тему внутренних тестов и рассмотрим полноценную реализацию автоматического обновления ваших проектов с moonly.
Пожалуйста, не судите строго, это моя единственная первая статья за все время который я писал столь большой текст без картинки :D.
Спасибо большое за то, что уделили время прочтению этой статьи!

UPD: Исправил некорректности в заголовках.
 

Вложения

  • luajit.zip
    430.5 KB · Просмотры: 1
Последнее редактирование: