Перейти к содержанию

Граница Lua и Java: исследовать API Build 42

Разберитесь, как Lua обращается к объектам Java, какие преобразования и ограничения описывает источник и как исследовать локальную установку с javap.

Граница между Lua и Java в Build 42

В руководстве объясняется модель объектов Java, доступных Lua, описываются поддерживаемые операции и преобразования, а также показано, как исследовать локальный API с помощью javap. Автор утверждает, что сведения о движке проверены на .jar Build 42 за октябрь 2026 года; эта проверка и точная версия в данной работе не повторялись.

Модель взаимодействия

На диаграмме статьи projectzomboid.jar работает в JVM; LuaManager открывает классы и функции интерпретатору Kahlua (Lua на Java), а вызовы методов Java из Lua проходят через reflection.

┌──────────── JVM (projectzomboid.jar) ────────────┐ │ IsoPlayer, IsoZombie, InventoryItem, ... │ │ LuaManager ── expone clases y funciones ──┐ │ │ ▼ │ │ Kahlua (intérprete Lua escrito en Java) │ │ tu Lua ── llama métodos por reflexión ──► │ └────────────────────────────────────────────────────┘

  • Kahlua не копирует объект Java в Lua: Lua получает ссылку, а каждый вызов obj:metodo(args) преобразуется в вызов Java через reflection.
  • Из Lua доступны только классы, которые открыл движок. По статье, этим занимается zombie.Lua.LuaManager$Exposer: exposeAll() вызывает setExposed(Clase) примерно 1 000 раз — для каждого доступного Lua класса. Если класса нет в этом списке, он недоступен Lua, даже если есть в .jar. Примеры автора — сетевые классы GameClient и GameServer.
  • Глобальные функции вроде getPlayer, getText и instanceof описаны как статические методы zombie.Lua.LuaManager$GlobalObject с аннотацией @LuaMethod(name = "...", global = true). В источнике указано 759 таких функций.

Что можно делать с объектом Java из Lua

ОперацияЗаписьПример и замечание источника
Вызвать методobj:metodo(args) (двоеточие)getPlayer():getInventory()
Создать объектClase.new(args)ArrayList.new() (53 применения), IsoThumpable.new(...) (38), ColorInfo.new() (100)
Вызвать статический методClase.metodo(args)ScriptManager.instance:getItem("Base.Apple"): статическое поле instance и метод
Использовать enumEnum.VALORItemTag.MAGNIFIER, CharacterTrait.ILLITERATE, Keyboard.KEY_F11
Проверить классinstanceof(obj, "IsoZombie")Статья сообщает 221 применение. Функция Java GlobalObject.instof ищет открытый класс и вызывает Class.isInstance; поэтому подходит и родительский класс, например IsoGameCharacter.
Хранить пользовательские данные в объектеobj:getModData() → таблица LuaМодуль 14 курса.

Ограничения, перечисленные в статье: Kahlua открывает методы, но не произвольные поля наподобие obj.x. По утверждению автора для рассматриваемой версии, без подходящих методов Lua не может читать или изменять произвольное поле объекта Java. Reflection, используемый инспектором объектов (getNumClassFields, getClassField, getClassFieldVal), по словам автора, закрыт вне debug-режима: getClassFieldVal сначала вызывает LuaManager.validateReflectionAccess, которое выбрасывает IllegalStateException("Not in debug"). Источник предупреждает, что это средство инспекции, не для опубликованного мода.

  • Нельзя добавлять поля в объект Java из Lua; руководство предлагает использовать getModData().
  • Нельзя наследовать Java-класс из Lua. Автор объясняет, что расширяемые части игры — интерфейс и действия — написаны на Lua.

Преобразование типов

JavaВ Lua по руководствуНа что обратить внимание
int, float, double, longnumber (double)Для параметра int конвертер zombie.Lua.KahluaNumberConverter применяет Double.intValue(): 3.7 превращается в 3, а -3.7 в -3; значение усекается к нулю, а не округляется.
booleanboolean
Stringstring
nullnilПеред цепочкой вызовов проверяйте nil; getPlayer():getVehicle():getSpeed() завершится ошибкой, если персонаж не в машине.
ArrayList, ListОбъект JavaИндексы начинаются с 0: list:get(0); размер — list:size(). В статье указано 1 206 вызовов size().
KahluaTabletableПредставление таблицы Lua в Java; так приходят таблицы, переданные в методы, которые их принимают.

Пример обхода Java ArrayList из источника сохранён буквально:

local items = getPlayer():getInventory():getItems() -- ArrayList de Java for i = 0, items:size() - 1 do -- desde 0 hasta size-1 local it = items:get(i) print(it:getFullType()) end

Автор насчитал в игре 565 вызовов :get(i) и 328 вызовов :get(i-1). Второй вариант обходит индексы for i = 1, list:size() и вычитает 1. По статье, оба способа допустимы, но их смешивание приводит к ошибке.

В Java могут быть перегруженные методы с одним именем и разными параметрами, например AddItem(String) и AddItem(InventoryItem). Согласно руководству, Kahlua выбирает вариант по числу и типам аргументов. Несовпадение типов может дать непонятную ошибку «no such method», хотя такой метод существует.

Как выяснить реальный API

  • Найти, как метод использует Lua игры. В статье приведён пример rg -n "getVehicle\(\)" _juego/lua.
  • Посмотреть живой объект в консоли с помощью инспектора из предыдущего упражнения или debug-окна client/DebugUIs/ObjectViewer.lua.
  • Проверить через javap публичные методы класса и их типы в .jar. Публичная сигнатура сама по себе не доказывает, что класс доступен Lua.
  • Декомпилировать метод, чтобы понять его поведение (модуль 08).

Когда разные источники противоречат друг другу, автор предлагает исходить из установленной игры.

Использовать javap

Для javap нужен JDK: встроенной в игру Java недостаточно. Скопируйте projectzomboid.jar из собственной установки игры в _juego и обновляйте копию после обновления игры; приведённый путь — пример для macOS. Руководство предлагает распаковать .jar и запросить публичные методы IsoPlayer. Последняя команда исследует Item.DoParam, чтобы извлечь строки с ключами парсера предметов. Примеры команд сохранены без изменений и не запускались:

mkdir -p /tmp/pzjar && cd /tmp/pzjar && unzip -oq ~/Zomboid/Workshop/_juego/projectzomboid.jar javap -cp /tmp/pzjar -public zombie.characters.IsoPlayer | less # métodos públicos y firmas javap -cp /tmp/pzjar -p -c zombie.scripting.objects.Item \ | awk '/public void DoParam\(java.lang.String, java.lang.String\)/,/^$/' \ | grep -oE '// String [A-Za-z]+' | sort -u # las claves que acepta el parser de ítems

Пояснение автора: парсер каждого типа скрипта — класс Java, а его строковые константы показывают поддерживаемые ключи. Для Build 42 за октябрь 2026 автор утверждает, что каждая строка «Ключ = значение» предмета обрабатывается Item.DoParam(String, String), содержащим 407 различных строк и 440 сравнений equalsIgnoreCase; поэтому регистр ключей не важен. В более ранней версии автор насчитал 404 строки в Item.Load и отмечает, что код меняется между патчами. Эти числа не проверялись в данной работе.

Упражнения

Упражнение 7.1 · Безопасная цепочка вызовов

Напишите CursoLab.velocidad(), которая возвращает скорость автомобиля игрока или 0, если он идёт пешком, и никогда не вызывает ошибку. Проверьте пешком, на пассажирском месте и за рулём.

Упражнение 7.2 · Обход коллекций Java

Реализуйте CursoLab.inventarioPorCategoria(), возвращающую таблицу Lua вида { Food = 3, Tool = 5, ... } для инвентаря игрока, включая предметы внутри рюкзаков. Подсказка: у предмета-контейнера есть собственный getInventory(); ищите в luautils.countItemsRecursive обход вложенных инвентарей. Критерий — совпадение с интерфейсом с рюкзаком и без него.

Упражнение 7.3 · Изучить API через javap

  1. На распакованном .jar перечислите публичные методы IsoPlayer, которые начинаются с get и возвращают boolean; посчитайте их.
  2. Найдите в IsoGameCharacter метод, сообщающий, находится ли персонаж в помещении, и проверьте его в консоли.
  3. Извлеките ключи Item.DoParam и сравните с ключами модуля 02; найдите те, которые игра молча игнорирует.

Упражнение 7.4 · Что открыто Lua

Найдите в .jar класс zombie.Lua, который регистрирует видимые Lua классы; ищите setExposed или exposeAll. Сохраните полный список в notas/expuestas.txt, проверьте IsoZombie и VehicleManager, а также отсутствие GameClient. Затем выберите закрытый класс и проверьте, что Lua его не видит. Цель — обосновать по свидетельствам, доступен ли Lua конкретный класс.

Упражнение 7.5 · Глобальные функции

Найдите аннотацию, которой в классе zombie.Lua помечены функции вроде getPlayer(). Выгрузите список глобальных функций в notas/globales.txt, отметьте десять незнакомых и проверьте три.

Задача со звёздочкой 7

Напишите pzapi.py: скрипт получает имя класса, например IsoPlayer, и на основе javap создаёт файл типов Lua Language Server с аннотациями ---@class, ---@field и ---@return. Поместите его в .vscode и проверьте, дополняет ли VS Code методы getPlayer():.

Информация
Числа открытых классов и функций, методы, ключи парсера и подсчитанные использования — утверждения статьи об одном конкретном .jar. Команды, декомпиляция, игра и упражнения не запускались; пути macOS приведены как пример. Публичный вывод javap не подтверждает доступность API из Lua. Файлы игры не распространяются.