Лаунчер для Electronvolt

Майские праздники не прошли даром: на пару дней я основательно засел за Python и написал собственный лаунчер. Зачем? Он нужен для того, чтобы не привязывать игру к конкретной игровой платформе. В моем случае, я использую GameJolt, но такой подход работает для любого сервиса. Лаунчер служит посредником между игрой и API площадки. Все, что нужно делать игре — это сообщать лаунчеру о событиях наподобие «игрок получил такую-то ачивку». Лаунчер, в свою очередь, передает эти данные игровому сервису. Если надо переехать на другой сервис, достаточно просто обновить лаунчер, а игру патчить не придется — это удобно и экономит массу времени.

Для создания интерфейса я использовал WebView. Можно спорить до хрипоты о недостатках веб-стека, но на сегодняшний день он остается самым простым и универсальным решением для создания GUI. Нативные тулкиты платформозависимы, требуют соблюдения особой архитектуры приложения и чаще всего выглядят скучновато, а браузерный движок позволяет буквально за несколько часов наваять красивые формы с анимациями и спецэффектами — сделал и забыл!

Долгое время я писал веб-интерфейсы на CEF (Chromium Embedded Framework) — «Electron для Python». Он предоставляет portable браузерный движок и в целом свою задачу решает, но имеет ряд проблем. CEF Python уже несколько лет как не развивается, и версия Chromium в нем заметно устарела, новейшие свойства CSS не поддерживаются. Кроме того, в собранном виде CEF-приложение весит около 200 Мб, что для небольшого приложения, мягко говоря, жирновато. Все эти проблемы я решил переходом на PyWebView! Единственное, чем придется пожертвовать — совместимостью с Windows 8, что в 2025 году совсем не критично. Собранный PyInstaller’ом, лаунчер стал весить менее 50 Мб (используя UPX, можно сжать еще сильнее). А код под PyWebView гораздо компактнее и проще, чем под CEF Python.

Основная задача моего лаунчера — служить посредником между игрой и внешними сервисами. Для этого используется механизм IPC: игра отправляет события лаунчеру через TCP-соединение по кастомному протоколу. Например, когда игрок получает достижение, игра отправляет команду типа "eV:award=AchievementName". Лаунчер, получив такую команду, передает данные на сервер.

Важная особенность этого механизма — асинхронность: блокировать игровой цикл, чтобы отправить что-то по сети — не комильфо, задержки бывают в секунду и даже дольше. Выручил taskPool из std.parallelism — все вызовы IPC оборачиваются в task и заносятся в очередь пула задач:

taskPool.put(task!ipcSendSync(message));

Это не блокирует поток и обеспечивает плавный игровой процесс. Кроме того, очередь гарантирует, что вызовы будут обработаны последовательно — не будет состояния гонки за исходящий сокет, если отправить несколько сообщений подряд.

GameJolt имеет простой и удобный API, который легко интегрировать в проект. Однако, чтобы не компрометировать приватный ключ, я решил не внедрять его напрямую в игру. Вместо этого ключ хранится на моем сервере, который и отправляет запросы API. Лаунчер напрямую с API не взаимодействует — он общается только с сервером. Пользователь может по желанию авторизоваться в лаунчере, используя логин и токен GameJolt — токен был специально придуман, чтобы не сообщать играм пароль от аккаунта. В случае успешной авторизации достижения в игре будут синхронизироваться с аккаунтом.

Лаунчер также позволяет пользователю настроить графику игры перед запуском. Для этого он взаимодействует с конфигурационным файлом игры — settings.conf. Лаунчер считывает текущие настройки, обновляет их в зависимости от выбранных пользователем параметров и сохраняет изменения обратно в файл. Также он мониторит изменения в конфиге — если игра обновила файл, интерфейс лаунчера подхватит изменения.

На будущее есть план реализовать систему автообновления (полезно для игроков, которые не используют игровые платформы и предпочитают устанавливать игры вручную). Кроме того, можно добавить установку модов.

Dagon 0.17 и другие обновления

Выпустил новую версию движка Dagon. Релиз включает поддержку экранов с высокой частотой обновления, поддержку Wayland под Linux, новые методы класса Application для управления окном приложения и методы класса Game для управления сценами. Шейдер HUD-объектов теперь поддерживает прозрачность материала. Исправлен баг с неправильным удалением из памяти сцен GLTF, также исправлен прыжок в контроллере персонажей Newton. Движок и расширения теперь используют самые свежие версии всех BindBC-биндингов.

bindbc-soloud обновлен до версии 0.2 — биндинг теперь поддерживает SoLoud 20200207.

Обновлена демка механики игры Electronvolt: добавил главное меню, опции, паузу и множество других мелких улучшений.

Статус по проектам

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

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

Dagon. Развивается, но медленно. В данный момент я работаю над версией 0.16, в которую войдут нововведения прошлого года: подповерхностное рассеивание и зонды освещения среды. Я планирую работать над движком и дальше, планы по нему довольно масштабные: так, я хочу сделать встроенный редактор на основе ImGUI — он будет генерировать исходники на D и вызывать DUB для сборки проектов, таким образом движок станет намного дружелюбнее для начинающих. Также в планах переписать стек постпроцессинга.

dlib. Понемногу развивается ветка 1.x, а вот 2.x пока заморожена. Исследую возможность поддержки прогрессивных JPEG.

Electronvolt (проект Atrium). Временно заморожен, но не исключено, что я к нему еще вернусь.

bindbc-wgpu, dusk и прочие наработки по WebGPU на D. Периодически обновляю, но для серьезного перехода на WebGPU пока нет мотивации и ресурса. Никаких особых планов на этот счет тоже нет.

dray. Новый проект — движок рейкастинга на основе dlib. Находится в разработке. Я в него переношу код из моего старого физ. движка dmech, а то пропадают даром хорошие алгоритмы) На сегодняшний день dray уже поддерживает пересечение луча с произвольной выпуклой геометрией, движок включает классы для таких стандартных тел, как сфера, параллелепипед, цилиндр, конус, эллипсоид. Тела могут быть произвольным образом трансформированы при помощи матриц 4×4.

Журнал FPS. Он жив-здоров, просто уже не имеет PDF-версии и существует в виде новостной странички. У меня есть идея окончательно объединить его с порталом Xtreme3D — думаю, это будет сайт или даже целая соцсеть по любительской разработке игр.

Этос Метамодерна. Telegram-канал для пространных философских рассуждений, которые по формату не подходят для других моих площадок.

Живопись и 3D-моделирование для стоков я окончательно забросил — коммерческий выхлоп там теперь уже нулевой, а времени работать для души совершенно перестало хватать.

Не упоминаю тут свои бизнес-процессы — это большая тема для отдельной статьи, и даже не одной.

Временная шкала моих проектов

Приводя в порядок архивы и вспоминая прошлое, решил зафиксировать информацию о моих 3D-движках и других OpenSource-разработках в виде интерактивной временной шкалы при помощи time.graphics — возможно, кому-то будет интересно:

Dagon

Прошу читателей этого блога извинить меня за редкие посты. Более полугода я не отчитывался по работе над Atrium — спешу заверить, что проект не похоронен, прогресс понемногу идет, просто чуть медленнее, чем хотелось бы. Последние несколько месяцев я был занят дипломной работой в институте, поэтому свободного времени для геймдева было крайне мало.

Те, кто следит за моей активностью на GitHub, могли заметить, что у меня появился новый репозиторий dagon — проект, который позиционируется как новая эволюционная ступень DGL. В процессе работы над Atrium я пришел к выводу, что в движке не хватает средств автоматизации некоторых рутинных задач. Например, управление памятью в типичном игровом приложении может быть почти полностью автоматизировано, поскольку выделение и высвобождение памяти происходит в специально задуманных паузах, таких как переключение между локациями (это не относится к играм с открытым миром с фоновой подгрузкой, но это уже специфический случай). Кроме того, в обсуждении на Reddit звучал вопрос, почему DGL не использует SDL2. Так родилась новая ветка движка, которую я решил сделать отдельным экспериментальным проектом.

На сегодняшний день Dagon включает следующие возможности:

  • Использование SDL2
  • Новая модель памяти на основе концепции владельца (owner), позаимствованной из Delphi
  • Модель образцов и компонентов (entity-component), позволяющая расширять функциональность объектов динамически, без наследования классов
  • Динамическая перезагрузка ресурсов при их модификации сторонним приложением без перезапуска игры
  • Поддержка форматов OBJ и IQM
  • Поддержка текстур PNG, JPG, TGA, BMP
  • Поддержка контейнера Box для ресурсов
  • Новая система материалов с разделением на фронтенд (набор параметров) и бэкенд (передатчик параметров графическому конвейеру — фиксированному или шейдерному, в зависимости от выбранной реализации бэкенда). Система позволяет использовать как стандартные материалы с известным набором параметров, так и создавать свои, специализированные
  • Некоторые компоненты, напрямую портированные из DGL — например, система событий, система освещения, рендеринг текста.
Как только будет дописана система материалов и поддержка формата DGL3, я начну портировать Atrium на новый движок.