SQLDoom: SQL 데이터베이스 내부에서 원본 1993년 Doom 엔진을 완전히 실행하기
TL;DR
SQLDoom은 CedarDB에서 SQL 쿼리만을 사용하여 원본 1993년 Doom의 게임 로직과 렌더러를 완전히 내부에서 실행하며, 고전적인 35 Hz 타이크 레이트를 유지하고 최대 60 FPS로 320×200 프레임버퍼를 제공합니다. 미세한 Python 클라이언트는 입력, 타이밍, 비트맵 표시를 담당하며, 동일한 엔진은 4인 사망전 서버를 구동합니다.
SQLDoom이란 무엇인가
SQLDoom은 Doom의 공개 에피소드 1을 충실하게 포팅한 것으로, 모든 게임 로직, 상태, 렌더링을 CedarDB 데이터베이스 엔진이 실행하는 SQL 문장으로 이동시킵니다. 외부 코드는 최소한의 Python 스크립트뿐이며, 다음과 같은 기능을 수행합니다:
pygame을 통해 키보드/마우스 입력을 읽습니다.- 1/35초마다
SELECT doom_run_game_tic(...)를 호출하여 세계를 한 타이크씩 진행합니다. - 단일 SQL 쿼리로 새 프레임을 요청하고 반환된 비트맵을 그립니다.
나머지 모든 것—몬스터 AI, 플레이어 이동, 무기 처리, BSP 탐색, 벽/시야 평면 래스터화—는 테이블, 저장 프로시저, CTE가 풍부한 쿼리 안에 존재합니다.
아키텍처 개요
시스템은 두 개의 독립된 파이프라인으로 나뉩니다:
- 게임 로직 파이프라인 – 고정된 35 Hz로 실행되며, 관계형 게임 상태 테이블을 업데이트하고, 업데이트된 상태 외에는 아무것도 반환하지 않습니다.
- 렌더링 파이프라인 – 순수 함수형 SQL 쿼리로, 현재 상태 테이블을 읽고 각 픽셀에 대한
(x, y, rgb)를 포함하는 64,000행의 결과를 생성합니다.
Python (pygame) ↔️ SQL 로직 테이블 ↔️ SQL 렌더러 → 비트맵 → 표시
이 분리 덕분에 클라이언트는 원하는 만큼 빠르게 프레임을 요청할 수 있습니다(작성자는 최대 60 FPS를 보고함) 하지만 로직은 원래의 28.6 ms 타이크 예산에 고정되어 있습니다.
Doom 데이터를 관계형 모델로 로딩하기
Doom의 WAD 형식은 이미 관계형 테이블로 매핑하기에 적합합니다:
VERTEXES→vertexesLINEDEFS→linedefsSIDEDEFS→sidedefsSECTORS→sectorsTHINGS→things
1000줄의 Python 임포터는 WAD를 파싱하고 랩톱에서 약 18초 만에 약 110개의 테이블을 채웁니다. 관계형 스키마 덕분에 레벨 기하학은 직접 쿼리 가능하며, 블로그 포스트에 포함된 조망형 ASCII 렌더링 쿼리로 이를 입증했습니다.
게임 타이크 구현
단일 저장 프로시저 doom_run_game_tic이 전체 타이크를 캡슐화합니다. 내부에서 CedarScript(PL/pgSQL 유사 언어)는 이 타이크에 실행해야 할 하위 시스템을 결정하는 비트마스크 계획을 생성합니다. 그 후 다음과 같은 함수의 연쇄 호출을 수행합니다:
doom_cs_move– 플레이어 및 몬스터 이동doom_cs_doors– 문 열기/닫기doom_cs_weapon– 무기 상태, 히트스캔, 피해doom_cs_monsters– 몬스터 AI 상태 머신doom_cs_sector_fx– 섹터 효과(바닥, 천장)
몬스터 AI 예제는 시선, 거리 계산을 수행하는 재귀 CTE를 보여주며, CASE 표현식을 사용해 다음 상태를 결정한 후 단일 UPDATE로 monster_ai를 업데이트합니다.
성능: 최악의 타이크(E4M1, 46마리 각성 몬스터)는 10.45 ms(28.6 ms 예산의 약 37 %) 소요. 일반적인 타이크(6마리 몬스터)는 평균 2.15 ms(약 8 %) 소요. 전체 로직은 약 5,900줄의 SQL로 구성되며, 원본 C 소스의 약 9,000줄보다 적습니다.
SQL에서의 렌더링 파이프라인
단일 SQL 쿼리와 89개의 CTE가 전체 프레임버퍼를 생성합니다. 파이프라인은 Doom의 고전적인 단계를 그대로 반영합니다:
- BSP 탐색 – 사전 계산된
node_path_steps물리적 뷰는 각 루트에서 서브섹터까지의 경로를 40비트sort_key로 인코딩합니다. 이 키로 정렬하면 재귀 없이 앞에서 뒤로의 서브섹터 순서를 얻을 수 있습니다. - 벽 래스터화 –
generate_series는 각 벽 세그먼트를 화면 열로 확장한 후 픽셀 단위 조각으로 나눕니다. - 시야 평면(바닥/천장) 채우기 – 윈도우 함수는 각 열에 대한 남은 클리핑되지 않은 밴드를 계산한 후 천장/바닥 구간을 생성합니다.
- 스프라이트 렌더링 – 벽과 유사한 조각 생성을 수행합니다.
- 심도 해결 – 모든 후보 조각은 64비트
winner_key에 패킹되며, 가장 상위 비트는 심도, 표면 우선순위, 안정적인 동점 처리기를 포함합니다. 픽셀당MIN(winner_key)는 보이는 조각을 선택합니다.
시간 분할:
- 벽 래스터화 – 약 1.7 ms
- 시야 평면 채우기 – 약 3 ms
- 심도 해결 및 픽셀 단위 작업 – 약 8.2 ms(가장 큰 비용)
- 전체 평균 프레임 – 약 16 ms, Ryzen 7 7840U에서 약 60 FPS를 제공; 복잡한 장면에서는 원래의 35 FPS로 떨어집니다.
데이터베이스를 통한 멀티플레이어
전체 세계가 트랜잭셔널 데이터베이스에 존재하기 때문에 멀티플레이어는 사실상 무료입니다:
- 단일 ‘심판’ 프로세스가 모든 플레이어의 35 Hz 시계를 조정합니다.
- 플레이어 입력은
SECURITY DEFINER함수api_input를 통해 제출되며, 값은 제한되고mp_inputs에 행이 삽입됩니다. - 각 타이크는
BEGIN … COMMIT블록 내에서 실행되어, 모든 클라이언트가 타이크 전 또는 타이크 후 스냅샷을 보게 보장합니다—부분 업데이트 없음. - 역할 기반 접근 제어는 플레이어가 API 함수만 사용하도록 제한하여, 임의의 테이블 조작을 방지합니다.
공개 서버는 EU 또는 미국 서버에 연결된 4인 사망전을 호스팅하며, 클라이언트당 3코어 구성에서 안정적인 35 FPS를 제공합니다. 16코어 머신은 추가 매치를 수용할 수 있습니다.
왜 데이터베이스가 좋은 선택일 수 있는가
- 데이터 중심 설계 – 무기, 몬스터, 애니메이션 프레임은 테이블의 행으로, 검사 및 실시간 조정이 매우 간단합니다.
- 원자성 및 일관성 – 각 타이크는 트랜잭션으로, 전통적인 비동기화 버그를 제거합니다.
- 접근 제어 – 세밀한 권한 설정을 통해 플레이어에게 의도된 API만 노출할 수 있습니다.
- ECS 매핑 – 엔티티는 행이 되고, 컴포넌트는 테이블이 되며, 시스템은 집합 기반
UPDATE/INSERT문장이 되어 데이터베이스의 병렬 실행 엔진을 활용합니다.
컴파일 오버헤드
CedarDB는 복잡한 쿼리를 실행하기 전에 LLVM IR로 컴파일합니다. 측면 비교 어셈블리 분석 결과, SQL 생성 코드는 원본 C의 48명령어 대비 117명령어를 사용하며, 추가 명령어는 주로 결과를 테이블에 쓰기 때문입니다. 여러 추상화 계층이 존재하더라도 이 격차는 적당합니다.
커뮤니티 반응 (Hacker News 요약)
"SQL로 꽤 복잡한 게임 로직을 표현하는 것이 얼마나 쉬운지 놀랐습니다. 게임 로직은 단지 약 5900줄의 SQL입니다." – bob1029
"원본 C보다 줄 수가 적으면서도 쿼리 계획을 상태 머신처럼 악용하는 것은 정점의 공학적 부도덕입니다. 저는 그것을 사랑합니다." – soltanov
"게임 상태가 SQL 테이블이라는 사실이 제게 일 년간의 최적화에 대한 기억을 불러일으켰습니다… 원자성 보장은 일관된 상태를 보장합니다… 액션 게임을 위한 읽기/쓰기 루프를 그렇게 하다니? 순전히 어리석은 짓이지만, 저는 그것이 정말 재미있다고 생각합니다." – noduerme
이러한 댓글들은 SQL의 표현력, 쿼리 계획기를 상태 머신으로 사용하는 우아함, 그리고 실시간 액션 게임에 대한 확장성에 대한 트레이드오프에 대한 놀라움을 강조합니다.
시작하기
- CedarDB Community Edition을 설치합니다.
- Python 종속성 설치:
psycopg2및pygame. - 공개 에피소드
doom1.wad를 확보합니다(예:apt install doom-wad-shareware). https://github.com/cedardb/sqldoom리포지토리를 클론하고, README를 따라 WAD를 로드하고 Python 드라이버를 시작합니다.
또는 원본 블로그 포스트에 링크된 EU 또는 미국 서버에서 공개 매치에 참여하세요.
결론
SQLDoom은 관계형 데이터베이스가 전체 기능을 갖춘 실시간 3D 엔진을 호스팅할 수 있음을 보여줍니다: 게임 로직은 집합 기반 업데이트에 자연스럽게 맞으며, BSP 기반 렌더러는 CTE와 윈도우 함수로 표현 가능하고, 트랜잭션 보장은 멀티플레이어를 간단하게 만듭니다. 데이터베이스는 실용적인 그래픽 엔진으로서의 생산 게임에는 적합하지 않지만, SQL이 표현할 수 있는 가능성을 극한까지 밀어붙이는 매력적인 개념 증명입니다.
Sources
관련
- 프로젝트
- 프로젝트
- 프로젝트
- Dispatch
- 프로젝트