1 parent
1943996608
commit
f10b654953
34 files changed
+2099
-4576
No files matched your search
+40
-53
@@ -1,75 +1,62 @@
|
||||
# zlibreference.h — ZlibReference
|
||||
# zlibreference.h
|
||||
|
||||
## Обзор
|
||||
`zlibreference.h`, `zlibreference.cpp`
|
||||
|
||||
`Minecraft_launcher` умеет ставить модлоадеры Forge и NeoForge, а их установщики — это Java-программы,
|
||||
которые собирают часть jar-файлов прямо на машине пользователя и сверяют sha1 каждого собранного
|
||||
файла с эталоном из `install_profile.json`.
|
||||
Обход одной конкретной проблемы на Linux: Forge и NeoForge не ставятся на системах, где вместо
|
||||
zlib стоит zlib-ng.
|
||||
|
||||
Эталон посчитан на обычном zlib. Дистрибутивы вроде CachyOS и Fedora подставляют вместо него
|
||||
zlib-ng: сжатие корректное, но побайтово другое — поэтому установка падает на любой версии игры
|
||||
сообщением «Processor failed, invalid outputs». Сменить Java не выйдет: сборки OpenJDK под Linux
|
||||
берут `libz.so.1` из системы.
|
||||
## В чём проблема
|
||||
|
||||
`ZlibReference` — обход этой проблемы. Рядом с лаунчером лежит собранный обычный zlib, и процессу
|
||||
установщика он подсовывается через `LD_PRELOAD`. Системный zlib-ng при этом остаётся на месте:
|
||||
подмена живёт ровно один процесс.
|
||||
Установщики Forge и NeoForge собирают часть jar прямо на машине пользователя и сверяют sha1
|
||||
каждого собранного файла с эталоном из `install_profile.json`. Эталон посчитан на обычном zlib.
|
||||
CachyOS, Fedora и некоторые другие дистрибутивы ставят вместо него zlib-ng в режиме совместимости.
|
||||
Он сжимает корректно, но результат побайтово другой. Поэтому установка падает на любой версии игры
|
||||
с сообщением `Processor failed, invalid outputs`.
|
||||
|
||||
Пространство имён используется установщиком модлоадеров при подготовке окружения дочернего
|
||||
процесса Java.
|
||||
Другая Java не помогает: сборки OpenJDK под Linux берут `libz.so.1` из системы.
|
||||
|
||||
## Пространства имён
|
||||
## Что сделано
|
||||
|
||||
`ZlibReference` группирует три функции: проверку системной библиотеки, поиск собранного эталона и
|
||||
подготовку окружения процесса. Состояния нет.
|
||||
В репозитории лежат исходники zlib 1.3.1 (каталог `zlib/`). На Linux CMake собирает из них цель
|
||||
`launcher_zlib_reference` (`libz.so.1`). В дереве сборки она лежит в `<папка бинарника>/zlib/`, при
|
||||
установке — в `<prefix>/lib/BiLauncher/`. Сам лаунчер с ней не линкуется. Библиотека
|
||||
подкладывается через `LD_PRELOAD` только процессу установщика, системный zlib-ng не трогается.
|
||||
|
||||
Исходники лежат в репозитории, а не скачиваются при сборке, по двум причинам: сборка не зависит от
|
||||
сети, и версия зафиксирована. От версии зависит побайтовый результат сжатия.
|
||||
|
||||
## Функции
|
||||
|
||||
#### bool systemIsZlibNg()
|
||||
На Windows и macOS все три функции ничего не делают: `systemIsZlibNg()` возвращает `false`,
|
||||
`bundledPath()` пустую строку, `applyTo()` — `true`.
|
||||
|
||||
Отвечает на вопрос, окажется ли `libz.so.1`, который достанется процессу java, библиотекой zlib-ng.
|
||||
От ответа зависит, нужна ли подмена вообще: на системе с обычным zlib она бессмысленна.
|
||||
### systemIsZlibNg()
|
||||
|
||||
#### QString bundledPath()
|
||||
Открывает `libz.so.1` через `dlopen`, то есть ту же библиотеку, которую загрузчик выдаст процессу
|
||||
java, и смотрит, есть ли `zlib-ng` в строке `zlibVersion()`. Результат вычисляется один раз за
|
||||
запуск.
|
||||
|
||||
Путь к собранному рядом эталонному `libz.so.1` или пустая строка, если его нет. Библиотека
|
||||
собирается целью `launcher_zlib_reference` в `CMakeLists.txt` из исходников каталога `zlib/`,
|
||||
которые лежат в репозитории, чтобы сборка не зависела от сети, а версия была зафиксирована — от
|
||||
неё зависит побайтовый результат сжатия. Собирается только на Linux: на Windows и macOS проблемы
|
||||
подмены системного zlib нет.
|
||||
### bundledPath()
|
||||
|
||||
#### bool applyTo(QProcessEnvironment &env, QString *note = nullptr)
|
||||
Ищет эталонную библиотеку по порядку:
|
||||
|
||||
Дописывает `LD_PRELOAD` в переданное окружение, если подмена нужна. Изменяет `env` на месте.
|
||||
1. `<applicationDirPath>/zlib/libz.so.1` — запуск из дерева сборки;
|
||||
2. `<applicationDirPath>/../lib/BiLauncher/libz.so.1` — установленный пакет, tar.gz и AppImage;
|
||||
3. `LAUNCHER_ZLIB_INSTALL_DIR/libz.so.1` — путь, заданный при конфигурации CMake.
|
||||
|
||||
Возвращает `true`, когда окружение готово, — в том числе в случае, когда подменять нечего:
|
||||
система с обычным zlib или платформа, где вопрос не стоит. `false` означает, что подмена нужна, но
|
||||
эталонной библиотеки нет на месте.
|
||||
Второй вариант рассчитан на `CMAKE_INSTALL_LIBDIR=lib`. Поэтому CI на Ubuntu передаёт его явно,
|
||||
иначе библиотека легла бы в `lib/x86_64-linux-gnu`.
|
||||
|
||||
Необязательный параметр `note` заполняется строкой, пригодной и для журнала, и для текста ошибки:
|
||||
она объясняет, была ли подмена применена и почему.
|
||||
### applyTo(env, ¬e)
|
||||
|
||||
## Зависимости
|
||||
- zlib-ng в системе нет — подменять нечего, возвращает `true`, `note` пуст.
|
||||
- zlib-ng есть, эталон найден — дописывает его путь в начало `LD_PRELOAD` (чужое содержимое
|
||||
переменной сохраняется), возвращает `true`, в `note` пишет, что подставлено.
|
||||
- zlib-ng есть, эталона нет — возвращает `false`, в `note` пишет предупреждение.
|
||||
|
||||
Подключает `QString`; `QProcessEnvironment` объявлен вперёд и используется только по ссылке.
|
||||
Реализация опирается на макрос `LAUNCHER_ZLIB_INSTALL_DIR`, который `CMakeLists.txt` определяет на
|
||||
Linux — это путь установки библиотеки в системе, помимо каталога рядом с исполняемым файлом.
|
||||
|
||||
## Пример использования
|
||||
|
||||
```cpp
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
|
||||
QString note;
|
||||
if (!ZlibReference::applyTo(env, ¬e)) {
|
||||
emit failed(note);
|
||||
return;
|
||||
}
|
||||
|
||||
QProcess installer;
|
||||
installer.setProcessEnvironment(env);
|
||||
installer.start(javaPath, arguments);
|
||||
```
|
||||
[ModLoaderInstaller](ModLoaderInstaller.md) пишет `note` в журнал установщика. Если `applyTo()`
|
||||
вернула `false`, а установщик упал с ошибкой контрольных сумм, к ошибке добавляется прямая
|
||||
подсказка про zlib-ng.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in new issue
Block a user