Цель
Запустить Doom на PostgreSQL и познакомимся с основами написания нативных расширений для PostgreSQL.
Попробовать
Исходный код тут: https://github.com/DreamNik/pg_doom. Для удобства весь процесс реализован в виде Docker образа. Для работы придётся найти и вручную подложить файл doom.wad
, который защищён авторским правом и не является свободно распространяемым.
git clone https://github.com/DreamNik/pg_doom cd pg_doom <вручную поместите Ваш файл doom.wad в под-директорию pg_doom> docker build --tag pg_doom --file docker/Dockerfile . docker run --rm --interactive --tty pg_doom
Управление — кнопками A, S, D, W, F, E.
Архитектура решения
Решение будет состоять из:
-
расширения pg_doom, которое будет работать внутри СУБД;
-
bash-скрипта, который будет работать как интерфейс ввода вывода.
Расширение будет предоставлять две новые функции в языке SQL. Первая — будет передавать нажатые клавиши, вторая — получать «картинку» для отображения. А скрипт будет, в свою очередь, считывать нажатые кнопки, передавая их как аргумент первой функции, а после вызывать вторую функцию и отображать её результат.
Подготовка
Для того, чтобы написать расширение нам потребуются:
-
компьютер с ОС Debian;
-
установленный PostgreSQL с набором для разработки;
-
компилятор языка C и набор утилит GNU Make.
В статье используется ОС Debian, но можно использовать и любую другую ОС семейства Linux с соответствующей адаптацией некоторых шагов. Windows тоже подойдёт, но там шаги подготовки совсем другие.
Итак, открываем консоль и ставим необходимые пакеты:
export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ apt-get install -y \ git \ build-essentials \ postgresql
Создание расширения
Исходный код расширения для PostgreSQL будет состоять из:
-
файла с метаданными расширения —
pg_doom.control
. -
файлов с SQL кодом инициализации расширения в базе —
pg_doom.sql
; -
файла сборки расширения —
Makefile
; -
файлов с исходным кодом —
pg_doom.c
и другие.
В статье приведён далеко не весь исходный код. Весь исходный код можно посмотреть в репозитории pg_doom.
Файл pg_doom.control
Этот файл используется PostgreSQL для определения состава расширения и куда и как его загружать.
comment = 'Provides ability to play the game.' default_version = '1.0' relocatable = false module_pathname = '$libdir/pg_doom' schema = doom
Из интересного здесь это module_pathname
— путь, указывающий на собранный бинарный модуль.
Файл pg_doom—1.0.sql
Этот файл выполняется при загрузке расширения в базу данных. При необходимости в таких файлах создают таблицы, представления, триггеры, функции и другие структуры, необходимые для работы расширения. Нам необходимо предоставить в схеме базы данных только две функции — ввода и вывода данных:
CREATE PROCEDURE doom.input( IN chars TEXT, IN duration INTEGER) AS 'MODULE_PATHNAME', 'pg_doom_input' LANGUAGE C; CREATE FUNCTION doom.screen( IN width INTEGER DEFAULT 320, IN height INTEGER DEFAULT 200, OUT lineNumber INTEGER, OUT lineText TEXT) RETURNS SETOF RECORD AS 'MODULE_PATHNAME', 'pg_doom_screen' LANGUAGE C;
В файле используется ключевое значение MODULE_PATHNAME
в качестве имени модуля функции. Это значение подменяется на фактический адрес загружаемого модуля (библиотеки), которое указано в control файле.
Файл Makefile
Файл используется для компиляции и установки расширения. В начале файла задаются имя и описание расширения:
MODULE_big = pg_doom EXTENSION = pg_doom PGFILEDESC = pg_doom
Далее задаётся список файлов данных, которые будут установлены вместе с расширением
DATA = pg_doom--1.0.sql pg_doom.control
Далее, задаём список объектных файлов, которые необходимо собрать. То есть, задаётся не список исходных файлов, а список артефактов сборки. Из перечисленных объектных файлов и будет собрана библиотека.
OBJS = pg_doom.c ...
Вызов компилятора и скрипты сборки установлены в системе и могут быть подключены при помощи механизма PGXS. Для получения путей в системе присутствует утилита pg_config.
PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) bindir := $(shell $(PG_CONFIG) --bindir) include $(PGXS)
Файлы C
В файлах размещается исходный код функций, которые мы объявили в sql файле.
В общем случае, чтобы собранная библиотека загрузилась как расширение необходимо:
-
вызвать макрос
PG_MODULE_MAGIC
; -
для каждой экспортируемой функции вызвать макрос
PG_FUNCTION_INFO_V1(my_function_name)
; -
все экспортируемые функции должны иметь сигнатуру
Datum my_function_name( FunctionCallInfo fcinfo )
; -
определить две функции —
void _PG_init(void)
иvoid _PG_fini(void)
;
Подробное описание функций и их состав можно посмотреть в репозитории с исходным кодом расширения.
Интеграция игры
Для сборки ядра игры необходим пропатченный исходный код, в котором исправлены некоторые конструкции языка, которые мешали оригинальному коду компилироваться и запускаться под современными 64-битными системами. Исходный код пропатченного ядра можно найти тут.
Для запуска игры нужен файл doom.wad. Он содержит все медиаданные игры, но, к сожалению, не является свободно распространяемым в отличие от ядра игры. Его можете взять из директории оригинальной игры или получить любым другим легальным способом.
Для интеграции игры реализована в файле doom.c
. При первом вызове создаётся отдельный поток, в котором вызывается функция D_DoomMain
, которая представляет собой основной цикл игры.
В процессе работы цикла игры вызываются функции, которые управляют вводом-выводом игры:
-
I_InitGraphics;
-
I_ShutdownGraphics;
-
I_SetPalette;
-
I_StartTic;
-
I_ReadScreen;
-
I_InitNetwork.
При обычном запуске игры эти функции реализованы в драйверах ввода-вывода игры. Но в нашем расширении драйвера мы не компилируем, а функции определены на взаимодействие со структурами, которые доступны из объявленных функций pg_doom_input
и pg_doom_screen
.
Компиляция
Запускаем сборку и установку в систему при помощи типовых вызовов make:
make -j$(nproc) && sudo make install
Запуск сервера
Если в системе не запущен PostgreSQL, то можно создать временный экземпляр и запустить его:
export PGDATA=/tmp/pg_doom_data mkdir -p $PGDATA initdb --no-clean --no-sync cat >> $PGDATA/postgresql.conf <<-EOF listen_addresses = '127.0.0.1' EOF cat >> $PGDATA/pg_hba.conf <<-EOF host all postgres 127.0.0.1/32 trust host doomdb slayer 127.0.0.1/32 trust EOF pg_ctl start &> /dev/null
Загрузка расширения
Для запуска игры создаём и настраиваем базу данных:
CREATE DATABASE doomdb; CREATE EXTENSION IF NOT EXISTS pg_doom; CREATE ROLE slayer WITH LOGIN; GRANT USAGE ON SCHEMA doom TO slayer; GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA doom TO slayer;
Запуск игры
Для «комфортной» игры нам необходим скрипт-обёртка. Он должен заниматься вводом-выводом, аналогичным как при обычной игре. Для этого нам нужно считывать нажатые кнопки и отображать картинку на экране. Перед запуском необходимо подготовить терминал:
stty -echo clear cols=$(expr $(tput cols || 281) - 1) rows=$(expr $(tput lines || 92) - 2)
И далее запустить цикл:
{ while true; do while read -n 1 -s -t 0.02 k; do echo "CALL doom.input('$k',10);"; done; echo "SELECT '\\x1B[H';"; echo "SELECT linetext FROM doom.screen($cols,$rows);"; sleep 0.2; done; } | psql -h 127.0.0.1 -U slayer -d doomdb -P pager=off -t -q | sed 's|\\x1B|\x1B|g'
В цикле мы динамически формируем текстовые SQL команды и отправляем их в stdin утилиты psql, которая подключается к базе данных. Её вывод затем форматируется и выводится на экран. Скорость обновления и input-lag сильно зависит от возможностей компьютера и игрока.
Заключение
При помощи расширений PostgreSQL можно практически не ограничено расширять возможности СУБД PostgreSQL.
Спасибо за внимание!
ссылка на оригинал статьи https://habr.com/ru/articles/752620/
Добавить комментарий