Dagon 0.25.0

Вышла очередная версия движка! Наиболее интересным нововведением является расширение dagon:video, о котором я писал ранее — теперь в играх можно воспроизводить видео при помощи библиотеки libVLC. Видеопоток декодируется в текстуру, которую можно применить к любому мешу. Для полноэкранного отображения видео предусмотрен виджет FullscreenMediaView.

Наконец-то добавил в упрощенный рендер поддержку точечных и конусных источников света. Поддерживается до 8 источников света на слой, их нужно добавлять методом SimpleRenderPass.addLight. Также в упрощенном рендере появилась поддержка cel-шейдинга — управляется свойствами Material.celShading и Material.rimLight. В deferred-рендере эти свойства ни на что не влияют.

Появилась поддежка uniform-блоков в шейдерах. Вы можете создать массив uniform-структур при помощи объекта UniformBlockParameter. Поля структуры должны соответствовать требованиям std140.

Реализован MaterialAsset — ресурс описания материала, основанный на том же синтаксисе, что и конфигурационные файлы. Кстати, в файлах *.conf теперь поддерживаются матрицы 4×4 и 3×3 (фактически они являются массивами из 16 или 9 числовых значений, которые можно интерпретировать в приложении как матрицы с построчным расположением элементов).

Из мелочей: поддержка Entity.opacity в HUD-проходе, поддержка анимации для свойства Entity.opacity, поддержка Material.emissionEnergy в шейдере Sky, свойство TextureAsset.loaded.

libVLC и видеотекстуры

Я давно искал удобное решение для воспроизведения видео в играх, и вот, наконец, оно найдено — libVLC, библиотека, на которой основан всеми любимый плеер VLC. В отличие от FFMPEG, это высокоуровневый API, который позволяет буквально в несколько строк добавить видеоплеер в любое окно по HWND (или X window ID под Linux). Но самое интересное, конечно, декодировать видео во внеэкранный буфер, чтобы потом передать его в OpenGL-текстуру — libVLC это также позволяет с минимальными телодвижениями. Я уже добавил в репозиторий Dagon экспериментальное расширение dagon:video, которое предоставляет класс Video, обертку над libvlc_media_t* и libvlc_media_player_t*. Использовать его очень просто — сначала создается VideoManager, общий для всего приложения (лучше хранить его в классе, наследующем от Game):

VideoManager videoManager = New!VideoManager(this);

Затем, уже в сцене, создается источник видео:

Video video = New!Video(videoManager, 1920, 1080, assetManager);
video.open("media/video.mp4");

auto videoMaterial = addMaterial();
videoMaterial.baseColorTexture = video.texture;
videoMaterial.alphaTestThreshold = 0.0f;

video.play();

Можно использовать видеотекстуру для любых целей — показать заставку в начале игры, натянув на screen-aligned плоскость, или применить к мешу для создания внутриигрового экрана. Можно даже воспроизводить 360-градусные ролики в равнопромежуточной проекции для отображения на панорамном фоне.

Dagon 0.24.0

Выпустил новую версию движка. Основное нововведение — рендеринг текста теперь является частью ядра движка в качестве модуля dagon.graphics.font (таким образом, Freetype становится зависимостью ядра). Расширение dagon:ftfont удалено. В связи с этим я решил добавить в Dagon встроенный простой GUI. В данный момент поддерживаются перетаскиваемые окна, вывод многострочного текста (TextView), строка ввода (TextInput) и консоль (Console). GUI-тулкит встроен в объект Scene — доступен как свойство Scene.ui.

Добавлен менеджер шрифтов (Application.fontManager). Он загружает шрифты по умолчанию (Liberation Sans и Liberation Mono, которые прилагаются к движку) и позволяет создавать новые.
Добавлена базовая поддержка библиотеки Assimp (расширение dagon:assimp). Поддерживаются меши, узлы и некоторые свойства материалов (диффузный цвет, текстуры).

Также реализована возможность изменить системный курсор мыши методом Application.setCursor. Добавлена поддержка логирования в буфер.

Ретроспектива: Bud, DSSS и другие старые системы сборки

До появления DUB в 2012 году в сообществе D-шников не было единой системы автоматизации сборки. Все использовали что-то свое — от классического Make до кастомных скриптов. Расскажу о нескольких таких проектах, которые застал лично.

Build/Bud

Согласно deadalnix, первой известной системой сборки для D была программа Build (впоследствии переименованная в Bud), которую написал Дерек Парнелл в середине нулевых. Является ли название DUB отсылкой к этому проекту, я точно сказать не могу, но вероятность такая есть) В 2011 году Bud 3 была портирована на D2, ее даже можно скачать по ссылке ниже.

Как и все ранние системы сборки, Bud использовала собственный формат конфигов, похожий на INI-файлы. Конфигурация в нем была до крайности громоздкой и неудобной, и альтернативы стали появляться, как грибы после дождя.

Страница Build/Bud на DSource: https://dsource.org/projects/build

D Shared Software System (DSSS)

Довольно мощная система сборки, написанная Грегором Ричардсом в качестве альтернативы Build. DSSS, строго говоря, был комплектом из сборщика Rebuild и пакетного менеджера DSSS. Для конфигурации он использовал файлы *.conf, концептуально тоже близкие к INI — наборы пар «ключ=значение». Интересной фичей DSSS была поддержка нескольких таргетов в одном конфиге: конфиг мог быть поделен на секции, каждая из которых описывала сборку отдельного таргета, ассоциированного с указанным главным модулем:

[example.d]
target=example_binary

Можно было собирать один и тот же модуль в разных конфигурациях:

[example.d+withfeature]
target=example_binary_withfeature
buildflags=-version=withfeature

Также поддерживались хуки (prebuild, postbuild, preinstall, postinstall и др.), которые впоследствии были реализованы и в DUB. Другая концепция родом из DSSS — возможность установки собранного ПО в систему, аналогично dub fetch/dub run.

DSSS был написан на D1, поэтому я его не использовал — на D1 я никогда не писал, сразу начал с D2, и было жутко неудобно ставить обе версии языка сразу. Позже появился проект по портированию DSSS на D2, но я с какого-то момента перестал за ним следить.

Страница оригинального DSSS на DSource: https://dsource.org/projects/dsss

Форк для D2 на GitHub: https://github.com/apriori/dsss

xfBuild

Тоже проект родом из нулевых, система сборки от легендарного h3r3tic. Одно время была перспективной альтернативой DSSS, но, к сожалению, тоже исключительно под D1, да еще под Tango — поэтому подробно о ней ничего сказать не могу, я ей никогда не пользовался.

Существует порт xfBuild на D2: https://github.com/SiegeLord/xfbuild

Cook

Был период в истории D, когда все пользователи поделились на два лагеря — сторонники Bud и любители DSSS, и эта ситуация создавала серьезные сложности для мейнтейнеров публичных проектов. Затем Парнелл и Ричардс пропали из сообщества, обе системы утратили активную поддержку, и вновь наступили «темные века». Именно в это время я открыл для себя D. Отсутствие сборщика под D2 побудило меня написать сначала простой скрипт для запуска через RDMD, а потом полноценную программу под названием Cook.

Программа автоматически сканировала структуру модулей проекта и кэшировала зависимости между модулями, так что циклы пересборки были очень быстрые. В отличие от DUB, Cook следовала идее инкрементальной сборки: все D-модули компилировались в отдельные объектные файлы, и пересобирались только те модули, которые были изменены (плюс все зависящие от них, так как в D нет деления на интерфейсные файлы и файлы реализации — если изменились шаблонные сущности, то нужно пересобирать код, который их использует).

В Cook я тоже использовал собственный формат conf-файлов — JSON в те времена был еще не так популярен, да и не очень он удобен для конфигов, если честно. Cook могла работать без конфига, если проект не использует специфичные настройки тулчейна. В конфиге можно было указать пути к компилятору и линкеру, указать флаги (cflags, lflags), переопределить команды компиляции и линковки и многое другое — громадным преимуществом Cook была возможность запускать разные версии компилятора. Вот как выглядел мой типичный кроссплатформенный конфиг:

windows.compiler_dir: "C:/dlang/dmd-2.067/windows";
linux.compiler_dir: "/opt/dlang/dmd-2.067/linux";
linux.compiler: "%compiler_dir%/bin32/dmd";
linux.linker: "%compiler_dir%/bin32/dmd";
windows.compiler: "%compiler_dir%/bin/dmd";
windows.linker: "%compiler_dir%/bin/dmd";
windows.obj.path: "o_windows";
linux.obj.path: "o_linux";

Формат конфигов представлял собой, по сути, мини-DSL с поддержкой любых кастомных переменных (не такой мощный, как в CMake, но все же получше, чем INI). Для переменных поддерживались платформ-префиксы. Подобно другим старым сборщикам, Cook поддерживала мультитаргетные проекты. Еще она могла загружать зависимости из git-репозиториев и подключать к проекту зависимости, хранящиеся локально. Ее можно было даже настроить для кросс-компиляции windows-программ под Linux при помощи Wine!

Получилась до того удобная штука, что я использовал Cook аж до конца десятых, предпочитая ее DUB, и прекратил разработку лишь в 2019 году, когда уже стало ясно, что DUB теперь полноправный стандарт. Cook прекрасно работает до сих пор — можете скачать, собрать и протестировать: https://github.com/gecko0307/cook2. Есть определенные задачи, где она, имхо, подходит лучше, чем DUB — например, при сборке в экзотические целевые платформы с использованием LDC.

Не могу сказать, что я доволен DUB на 100%, есть задачи, которые он не покрывает (впрочем, как и альтернативные сборщики). Например, до сих пор нет инструментария для автоматической интеграции в проект библиотек, написанных на других языках. Но эта проблема, судя по всему, в общем случае нерешаема.

Можно ли программировать при помощи нейросетей?

Мне, как мейнтейнеру значимых OpenSource-проектов, Microsoft предоставил бесплатный доступ к Copilot. Пользуюсь им теперь регулярно, как и ChatGPT, но в зависимость, как многие, не впадаю. Могу сказать вот что. В среднем нейронки хорошо справляются с чем-то типовым — например, запросами вроде «напиши функцию-хелпер на JS, которая принимает на вход массив и делает то-то и то-то». То есть, алгоритмами, которые я бы и сам написал, но иногда тупо лень это делать. Ситуация становится резко сложнее с программами, которые должны работать с определенными интерфейсами, библиотеками, API. Чем экзотичнее библиотека, тем хуже результат. Зачастую ИИ просто не знает, как выглядит та или иная функция API, и пытается угадать. Попадает пальцем в небо очень часто, хотя концептуально его решения могут быть правильными. Бывает, что код достаточно легко подчистить от ляпов, а бывает, что он в принципе не может работать.

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

Нейронки хорошо справляются с рутинными задачами. Переписать код с одного языка на другой, переименовать множество функций по заданному шаблону, сгенерировать какие-то однотипные конструкции — все, что не требует интеллекта вообще. Но для такого они не особо нужны, это решается несложной автоматикой: текстовыми фильтрами, регулярными выражениями и т.д. Чем меньше в проекте рутины и чем больше инженерного творчества, тем сложнее заменить живого автора. Утверждение, что якобы с нейронками даже непрограммисты могут создавать полноценные продукты — это миф. Как в рассказе Роберта Шекли «Верный вопрос»: Абсолютный Ответчик помогает только тем, кто правильно формулирует вопросы — а для этого, как резюмирует писатель, нужно знать большую часть ответа.

Встроенный UI в Dagon

Наконец-то дошли руки начать пилить свой уютный UI-тулкит! Убийцу ImGui я, конечно, не планирую — это будет набор базовых виджетов для добавления в игру простых отладочных инструментов. Уже готовы окна и TextView — на скриншоте ниже демонстрируется вывод лога приложения.

Об администрировании сервера

Сегодня, в эпоху облачных провайдеров и SaaS, управлять сервером самостоятельно — крайне нишевое занятие. Для этого нужно очень хорошо разбираться в Linux и его многочисленных системных компонентах, которые, к тому же, постоянно обновляются и меняются, из-за чего знания и опыт быстро устаревают. Я много лет пользуюсь Linux, но и то не знаю всех тонкостей, постоянно учусь. Сама система принципиально несложная и понятная, но в ней постоянно появляется что-то новое, какие-то дополнительные слои абстракции. Linux многолик, его можно использовать в самых разных контекстах — отсюда и сложность. Ну и, конечно, знать систему на уровне десктопа — это одно, а админить по SSH — слегка другое.

Не хочу никого пугать надуманными страшилками: в сервере нет ничего мистического, чаще всего это обычный Ubuntu, где все точно такое же, как и в десктопных версиях (только нет собственно десктопа). Умеешь пользоваться терминалом, можешь научиться и сервером управлять. Но сервер — это более специфическая среда, тут у вас не просто рут и «хомяк». Надо серьезно заботиться о правах доступа и постоянно думать о том, не под угрозой ли безопасность. На десктопе угрозы чаще всего условные — ваш комп никто извне не станет ломать, никому вы не нужны. А вот сервер попытаются запросто. Любой мало-мальски посещаемый сайт постоянно осаждают боты в попытках взломать или заспамить. Поэтому головным мозгом думать надо постоянно, взвешивая каждый свой шаг. chmod тут не для галочки.

Хорошая новость — Linux стабилен. Ядро падает крайне редко, я за всю жизнь ни разу не видел, чтобы система падала из-за проблемы именно в ядре. На десктопе все крахи из-за неправильно настроенных приложений. Чаще всего падает графическая подсистема (раньше это были «иксы» — X11, сейчас еще и Wayland), либо чудят драйверы. На сервере графики нет, и внешние устройства не подключаются, так что единожды запущенная система будет работать, пока какая-нибудь авария не случится. А сломать Linux нечаянно в процессе каких-то настроек — это надо иметь особую степень криворукости. Зато вот ломающиеся серверные приложения — это суровые будни админа! Софт под Linux встречается самого разного качества — Open Source же. Все всегда запускается на свой страх и риск. Можно очень легко попортить любые данные, если они не защищены от записи. Поэтому бэкапы надо делать регулярно. Никогда нельзя запускать никакие команды или скрипты, если не понимаешь, что они делают. На сервере все проблемы от бездумных действий. Если на десктопе обычно работаешь под непривилегированным пользователем, а sudo для административных действий — это исключение, то на сервере все наоборот: root — норма, работа под другими пользователями — исключение. Но постоянно приходится помнить: настраиваешь что-то из-под рута — готовься к ошибкам типа «permission denied». Создал из-под рута какой-нибудь файлик, забыл выставить нужные права — все, будешь полдня искать причину полома.

Скажу честно: я не большой фанат Docker, я предпочитаю все ставить самостоятельно прямо в систему. Докером пользуюсь только в тех случаях, когда надо что-то совсем уж экстраординарное и навороченное, в чем лень разбираться. Например, у меня в контейнере запущен Nextcloud. Да, лень — это вообще-то не самая уважительная причина в данном контексте, но и я не профессиональный админ, а просто юзер, которого жизнь заставила админить. Поэтому я и не адепт всей этой контейнерной религии — да и вообще не сторонник каких-либо определенных DevOps-практик. Мне кажется, чем проще, тем лучше. Если Docker упрощает жизнь — то хорошо, берем на вооружение, а если с ним все становится сложнее, то ну его.

Проще всего поставить классический комплект из веб-сервера, PHP и MySQL (или MariaDB). Справится абсолютный новичок. Даже сертификаты сегодня уже не обязательно самому ставить — Caddy делает это за вас. Нужно только немного понимать, как работает стек TCP/IP (впрочем, если вам только обычные сайты писать, то и полноценный сервер не нужен, достаточно shared-хостинга).

Наверное, у всех, кто что-то разрабатывал под веб, был период, когда хочется написать свою серверную программу. Так вот, это ровно до того момента, когда вы действительно сможете запустить ее на реальном сервере. Только тогда начинаешь понимать, что к чему в плане безопасности. Энтузиазм велосипедить отпадает, вырабатывается склонность ставить чужое, но надежное. Взять тот же WordPress — да, система неидеальная. Да, то и дело что-то там отваливается после обновлений. Да, плагины быстро устаревают. Но WP все равно лучше, чем самописный движок. Свое писать стоит, только если совсем уж нет никакого выхода, если у вас задача какая-то узкоспециализированная. Например, сложный CI/CD или игровой сервер.

Админство — совсем не творческое занятие, а жутко рутинное. Это постоянная правка всевозможных конфигов и написание скриптов, установка пакетов, управление Systemd, копание в логах, мониторинг портов и, конечно, лихорадочные поиски документации в Интернете. Запустили сервис, потестили, увидели проблему, остановили, фиксим. Потом еще раз, и еще, и еще… Главное, что я понял — лучше вообще не вкатываться во все это без особой насущной необходимости. Обычному человеку должно хватать бесплатных сервисов, предоставляемых корпорациями. Вот когда уже не хватает, и «хочется странного» — другое дело.

Например, мой путь в администрирование начался, когда понадобился личный/семейный VPN. Думал этим и ограничиться, но затем понеслось. Сервер — это мир больших соблазнов. Поставил одно, тут же хочется попробовать другое, ведь почти всем популярным сервисам есть self-hosted аналоги: можно поднять личные аналоги GitHub, Google Workspace, Spotify и бог знает чего еще. Настраивать и дорабатывать напильником все это можно бесконечно — есть маньяки, которые именно этим большую часть свободного времени и занимаются) Для меня идеал — один раз настроил и забыл, но, к сожалению, так получается далеко не всегда.

C3: неужели наконец-то нормальный C-подобный язык?

Современная замена C — это то, чего очень не хватает всем, кто пишет что-то для десктопа с нуля, не полагаясь на популярнные платформы типа веба, Java/.Net, скриптовых языков и т.д. До настоящего времени реального аналога C, можно сказать, не было вовсе (исключением является BetterC-режим D, но это все-таки не отдельный язык, а подмножество, и он не для «зашедших с улицы», а совсем наоборот — для самых опытных программистов на D). Go и Rust не предлагать! 🤣

И вот, захожу я как-то раз в ньюсгруппу d.D и вижу ссылку на C3: https://c3-lang.org. «Продать» мне что-то новомодное сложно, но язык зацепил! Я тестировал еще не все фичи, но вот что понравилось практически сразу:

  • Компилятор довольно легко установить. Вначале, правда, выкачивается майкрософтовский SDK, но после этого компилятор становится самостоятельным и Студию не требует;
  • Не нужна отдельная система сборки, компилятор сам выполняет ее функции;
  • Простая и вменяемая система типов, полностью совместимая с C без костылей;
  • Векторы, матрицы и кватернионы в стандартной библиотеке — бесценно! Сразу уйма работы отпадает при написании игрового движка;
  • Вообще, в целом, читаемая и красивая стандартная либа. Открываешь любой модуль, и все понятно;
  • Управление памятью, чем-то похожее на то, что я делаю в dlib (аллокаторы, универсальный new). Сборщика мусора нет;
  • Красивая обработка ошибок со встроенным Optional и проверкой на null на уровне синтаксиса. Optional — это просто модификатор типа: если его использовать, то компилятор тупо не дает прочитать такую переменную без предварительного null-чекинга. Можно его делать самостоятельно, а можно просто использовать оператор «!!», тогда компилятор вставит проверку сам. Все выглядит очень просто и читаемо.

Конечно, многого пока не хватает — например, нет менеджера пакетов. Биндинги к некоторым популярным библиотекам есть тут, но я пока не разобрался, как они линкуются, документации по этой теме нет. Нативных библиотек, ясное дело, тоже выбор пока совсем минимальный. Еще один жирный минус — не поддерживается компиляция в bare metal, а без этого язык нельзя назвать системным, и стопроцентной заменой C он считаться, строго говоря, не может.

Обновления

Dagon 0.23.1

Небольшое обновление Dagon 0.23. Добавлена поддержка переключения отдельных рендер-таргетов в PassTerrain — то есть, для материалов ландшафта работают свойства outputColor, outputNormal и др. Движок теперь использует dlib 1.3.2. Добавлены новые уроки — 9, 13 и 15.

К следующему релизу готовлю поддержку Assimp, о которой уже писал, а также перенос расширения dagon:ftfont в ядро движка, что позволит реализовать различные встроенные UI-компоненты.

BindbC-Assimp

Биндинг к Assimp 5 размещен на GitHub и доступен в качестве DUB-пакета. К сожалению, название bindbc-assimp занято заброшенным и несуществующим ныне проектом, поэтому пришлось зарегать как bindbc-assimp5.

Оптимизация блога

Я настроил объектный кэш на основе Redis, а также добавил HTML-кэш и заголовок Cache-Control, что заметно ускорило загрузку страниц блога. Было исправлено множество мелких проблем верстки. Все архивные игры теперь размещены на одном сервере с блогом.

Тема, которую я использую, теперь также доступна на GitHub. Это форк WPEX Blogger 1.2 от WPExplorer, в котором я внес исправления для совместимости с PHP 8, исправил поиск в мобильном меню, добавил новые переводы на русский, а также внес патч, исправляющий совместимость с плагином Code Syntax Block.

Поддержка Assimp

Библиотека Assimp — относительно тяжеловесное, но самое функциональное решение для загрузки 3D-моделей. У меня наконец-то дошли руки добавить поддержку Assimp 5+ в Dagon как расширение dagon:assimp, благодаря чему можно будет использовать в движке модели форматов FBX, Collada, 3DS и многих других. Пока загружаются только меши, но в планах добавить поддержку материалов, узлов и анимации.

Перед загрузкой модели можно задать кастомные флаги постпроцессора:

AssimpAsset aModel;

override void beforeLoad()
{
    aModel = this.addAssimpAsset("assets/cacodemon.fbx");
    aModel.loaderOption =
        aiPostProcessSteps.Triangulate | aiPostProcessSteps.FlipUVs;
}

После этого можно использовать меши из массива aModel.meshes для рендеринга:

auto e = addEntity();
e.drawable = aModel.meshes[0];