English version below.
ΠΠ°ΠΏΡΡΡΠΈΡΡ Doom Π½Π° PostgreSQL.
ΠΠ»Ρ ΡΠ΄ΠΎΠ±ΡΡΠ²Π° Π²Π΅ΡΡ ΠΏΡΠΎΡΠ΅ΡΡ ΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½ Π² Π²ΠΈΠ΄Π΅ 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
ΠΡΠΎΡ ΡΠ°ΠΉΠ» ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ PostgreSQL Π΄Π»Ρ ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΡ ΡΠΎΡΡΠ°Π²Π° ΡΠ°ΡΡΠΈΡΠ΅Π½ΠΈΡ ΠΈ ΠΊΡΠ΄Π° ΠΈ ΠΊΠ°ΠΊ Π΅Π³ΠΎ Π·Π°Π³ΡΡΠΆΠ°ΡΡ.
comment = 'Provides ability to play the game.'
default_version = '1.0'
relocatable = false
module_pathname = '$libdir/pg_doom'
schema = doom
ΠΠ· ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠ½ΠΎΠ³ΠΎ Π·Π΄Π΅ΡΡ ΡΡΠΎ module_pathname - ΠΏΡΡΡ, ΡΠΊΠ°Π·ΡΠ²Π°ΡΡΠΈΠΉ Π½Π° ΡΠΎΠ±ΡΠ°Π½Π½ΡΠΉ Π±ΠΈΠ½Π°ΡΠ½ΡΠΉ ΠΌΠΎΠ΄ΡΠ»Ρ.
ΠΡΠΎΡ ΡΠ°ΠΉΠ» Π²ΡΠΏΠΎΠ»Π½ΡΠ΅ΡΡΡ ΠΏΡΠΈ Π·Π°Π³ΡΡΠ·ΠΊΠ΅ ΡΠ°ΡΡΠΈΡΠ΅Π½ΠΈΡ Π² Π±Π°Π·Ρ Π΄Π°Π½Π½ΡΡ . ΠΡΠΈ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎΡΡΠΈ Π² ΡΠ°ΠΊΠΈΡ ΡΠ°ΠΉΠ»Π°Ρ ΡΠΎΠ·Π΄Π°ΡΡ ΡΠ°Π±Π»ΠΈΡΡ, ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ΠΈΡ, ΡΡΠΈΠ³Π³Π΅ΡΡ, ΡΡΠ½ΠΊΡΠΈΠΈ ΠΈ Π΄ΡΡΠ³ΠΈΠ΅ ΡΡΡΡΠΊΡΡΡΡ, Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΡΠ΅ Π΄Π»Ρ ΡΠ°Π±ΠΎΡΡ ΡΠ°ΡΡΠΈΡΠ΅Π½ΠΈΡ. ΠΠ°ΠΌ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎ ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²ΠΈΡΡ Π² ΡΡ Π΅ΠΌΠ΅ Π±Π°Π·Ρ Π΄Π°Π½Π½ΡΡ ΡΠΎΠ»ΡΠΊΠΎ Π΄Π²Π΅ ΡΡΠ½ΠΊΡΠΈΠΈ - Π²Π²ΠΎΠ΄Π° ΠΈ Π²ΡΠ²ΠΎΠ΄Π° Π΄Π°Π½Π½ΡΡ :
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 ΡΠ°ΠΉΠ»Π΅.
Π€Π°ΠΉΠ» ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ Π΄Π»Ρ ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡΠΈΠΈ ΠΈ ΡΡΡΠ°Π½ΠΎΠ²ΠΊΠΈ ΡΠ°ΡΡΠΈΡΠ΅Π½ΠΈΡ. Π Π½Π°ΡΠ°Π»Π΅ ΡΠ°ΠΉΠ»Π° Π·Π°Π΄Π°ΡΡΡΡ ΠΈΠΌΡ ΠΈ ΠΎΠΏΠΈΡΠ°Π½ΠΈΠ΅ ΡΠ°ΡΡΠΈΡΠ΅Π½ΠΈΡ:
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)
Π ΡΠ°ΠΉΠ»Π°Ρ ΡΠ°Π·ΠΌΠ΅ΡΠ°Π΅ΡΡΡ ΠΈΡΡ ΠΎΠ΄Π½ΡΠΉ ΠΊΠΎΠ΄ ΡΡΠ½ΠΊΡΠΈΠΉ, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΌΡ ΠΎΠ±ΡΡΠ²ΠΈΠ»ΠΈ Π² 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
log_filename = 'server.log'
log_min_messages = 'warning'
shared_preload_libraries = ''
listen_addresses = '0.0.0.0'
EOF
cat >> $PGDATA/pg_hba.conf <<-EOF
host all postgres 127.0.0.1/32 trust
host doomdb slayer 0.0.0.0/0 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 ΡΠΈΠ»ΡΠ½ΠΎ Π·Π°Π²ΠΈΡΠΈΡ ΠΎΡ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠ΅ΠΉ ΠΊΠΎΠΌΠΏΡΡΡΠ΅ΡΠ° ΠΈ ΠΈΠ³ΡΠΎΠΊΠ°.
Run Doom on PostgreSQL.
For convenience, the whole process is implemented as a Docker image, which you can try.
You only need to manually place the doom.wad
file, which is copyright protected and not freely distributable.
git clone https://github.com/DreamNik/pg_doom
cd pg_doom
<manually place your doom.wad file in the pg_doom directory>
docker build --tag pg_doom --file docker/Dockerfile .
docker run --rm --interactive --tty pg_doom
Controls - using the A, S, D, W, F, E keys.
The solution will consist of:
- the "pg_doom" extension, which will work inside the DBMS;
- a bash script, which will act as an input-output interface.
The extension will provide two new functions in the SQL language. The first will transmit the pressed keys; the second will get the "picture" for display. The script will, in turn, read the pressed buttons, passing them as an argument to the first function, and then call the second function and display its result.
To write the extension, we will need:
- a computer with Debian OS;
- installed PostgreSQL with a development set;
- a C language compiler and a set of GNU Make utilities.
The article uses Debian OS, but you can use any other Linux family OS with appropriate adaptation of some steps. Windows will also work, but the preparation steps are entirely different. So, we open the console and install the necessary packages:
export DEBIAN_FRONTEND=noninteractive && \
apt-get update && \
apt-get install -y \
git \
build-essentials \
postgresql
The source code for the PostgreSQL extension will consist of:
- an extension metadata file - "pg_doom.control";
- files with SQL code for initializing the extension in the database - "pg_doom.sql";
- an extension build file - "Makefile";
- source code files - "pg_doom.c" and others.
The article does not provide all the source code. You can view the full source code in the pg_doom repository.
This file is used by PostgreSQL to define the composition of the extension and how and where to load it.
comment = 'Provides ability to play the game.'
default_version = '1.0'
relocatable = false
module_pathname = '$libdir/pg_doom'
schema = doom
The interesting part here is the module_pathname - the path indicating the compiled binary module.
This file is executed when loading the extension into the database. If necessary, such files create tables, views, triggers, functions, and other structures required for the extension to work. We only need to provide two functions in the database schema - for input and output:
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;
The key value MODULE_PATHNAME
is used in the file as the module's name for the functions. This value is replaced with the actual address of the loaded module (library), as specified in the control file.
The file is used for compiling and installing the extension. At the beginning of the file, the name and description of the extension are defined:
MODULE_big = pg_doom
EXTENSION = pg_doom
PGFILEDESC = pg_doom
Next, the list of data files that will be installed with the extension is defined:
DATA = pg_doom--1.0.sql pg_doom.control
Then, we define the list of object files that need to be compiled. That is, we're defining not the list of source files, but the list of build artifacts. The library will be assembled from the listed object files.
OBJS = pg_doom.c ...
The compiler call and build scripts are installed in the system and can be included using the PGXS mechanism. The utility pg_config is present in the system for obtaining paths.
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
bindir := $(shell $(PG_CONFIG) --bindir)
include $(PGXS)
In these files, the source code of the functions we declared in the SQL file is located.
Generally, in order for the compiled library to be loaded as an extension, you need to:
- call the macro
PG_MODULE_MAGIC
; - for each exported function, call the macro
PG_FUNCTION_INFO_V1(my_function_name)
; - all exported functions must have the signature
Datum my_function_name( FunctionCallInfo fcinfo )
; - define two functions -
void _PG_init(void)
andvoid _PG_fini(void)
.
A detailed description of the functions and their composition can be found in the repository with the source code of the extension.
To build the game's core, a patched source code is required, in which some language constructs that interfered with the original code's compilation and execution on modern 64-bit systems have been fixed. The original source code of the game's core can be found here.
To run the game, you need the file doom.wad. It contains all the game's media data, but unfortunately, unlike the game's core, it is not freely distributed. You can take it from the directory of the original game or obtain it by any other legal means.
Game integration is implemented in the doom.c
file. The first call creates a separate thread in which the function D_DoomMain
is called, which represents the main loop of the game.
During the game loop's operation, functions are called that control the game's input-output:
- I_InitGraphics;
- I_ShutdownGraphics;
- I_SetPalette;
- I_StartTic;
- I_ReadScreen;
- I_InitNetwork.
In a typical game run, these functions are implemented in the game's I/O drivers. But in our extension, we don't compile the drivers, and the functions are defined to interact with the structures available from the declared functions pg_doom_input
and pg_doom_screen
.
We initiate the build and installation into the system using standard make calls:
make -j$(nproc) && sudo make install
If PostgreSQL is not running on your system, you can create a temporary instance and start it:
export PGDATA=/tmp/pg_doom_data
mkdir -p $PGDATA
initdb --no-clean --no-sync
cat >> $PGDATA/postgresql.conf <<-EOF
log_filename = 'server.log'
log_min_messages = 'warning'
shared_preload_libraries = ''
listen_addresses = '0.0.0.0'
EOF
cat >> $PGDATA/pg_hba.conf <<-EOF
host all postgres 127.0.0.1/32 trust
host doomdb slayer 0.0.0.0/0 trust
EOF
pg_ctl start &> /dev/null
To start the game, create and configure the database:
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;
For a "comfortable" game, we need a wrapper script. It should handle input-output, similar to a regular game. For this, we need to read the pressed buttons and display the picture on the screen. Before starting, prepare the terminal:
stty -echo
clear
cols=$(expr $(tput cols || 281) - 1)
rows=$(expr $(tput lines || 92) - 2)
And then run the loop:
{
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'
In the loop, we dynamically form textual SQL commands and send them to stdin of the psql utility, which connects to the database. Its output is then formatted and displayed on the screen. The refresh rate and input lag depend heavily on the computer's and player's capabilities.