
开发者用SQL Server数据库成功运行经典游戏Doom
来源标题:Running Doom Inside SQL Server
开发者将经典游戏Doom移植到SQL Server数据库中运行,实现了真实WAD文件、BSP渲染和E1M1关卡,而非简单的射线投射模拟。这是继其此前在SQL Server中构建LLM项目后的又一次非常规尝试,原作者表示用数据库跑游戏虽看似无意义但颇具趣味。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
不是光线投射器。不是致敬作品。是真正的 Doom——真正的 WAD、真正的 BSP、真正的 E1M1——由一个关系型数据库渲染出来。
这个想法是我第一篇文章《在 SQL Server 里构建一个 LLM》的延续,那篇文章用 SQL 数据库做了一些不寻常、甚至可以说是「毫无意义」的练习。但这一次对我意义特殊,因为我记得 Doom 发布时自己就在玩,惊叹于它在公司网络上运行得如此流畅,也惊叹于这家软件公司——Id Software——凭借他们的游戏引擎技术、数学功底以及把游戏做成 Shareware 的商业模式,把其他所有人都远远甩在了后面。
我还记得教我妈玩 Doom,后来又教她玩 Quake。她始终坚持只用键盘玩 Quake,尽管我为她做了一个 cfg 文件,让她可以用鼠标或手柄。我给她起了个网名「Quakemum」,但她从不喜欢在线玩——对她来说太快了。[那就用鼠标啊!!] 她会在厨房里待上几个小时,尖叫声和游戏噪音吵得隔壁房间的我爸不得安宁。你知道吗,她居然把两款游戏都通关了。我觉得我自己都没能做到。
关键画面:E1M1 起始房间,完整状态栏,手枪已拔出,运行在 SQL Server 里
互联网上有一个由来已久的传统:让 Doom 运行在那些本来不该运行任何东西的硬件上。验孕棒。洗衣机控制面板。ATM。数码相机。计算器。约翰迪尔拖拉机。跑步机。有人把它弄到了 Peloton 的小 LCD 屏幕上。还有人弄到了麦当劳的收银机上。规则很宽松,但大体上有共识:设备必须自己完成实际计算,把视频流对准屏幕可不算数。
数据库一直悄悄缺席这份名单,这一直让我觉得是个奇怪的空白。数据库就是一台计算机。它有内存、有存储、有一个能做算术的查询引擎,而在 SQL Server 的情况下,还有一个基于成本的优化器,会在你没要求的情况下乐于把工作并行分配到多个核心上。如果验孕棒都能做到,关系型引擎当然也能。
那么:SQL Server 能运行 Doom 吗?不是「渲染出某种看起来有点像 Doom 的东西」——而是真正加载 id Software 的数据并把它画出来。
有两个人已经接近了,都值得致谢。
Patrick Trainer 构建了 DuckDB-DOOM,它把光线投射、敌人行为、碰撞和渲染实现为 SQL,在浏览器中对 DuckDB 运行。CedarDB 的 Lukas Vogel 走得更远,做了 DOOMQL,一个完全用 SQL 写的多人射击游戏——游戏状态放在表里,渲染是一叠视图,一个 shell 脚本每秒大约三十次地执行一个 SQL 文件。
两者都确实很聪明。但两者都是基于网格的光线投射器,Trainer 帖子下面的评论立刻指出了显而易见的一点:它看起来像 Wolfenstein 3D,而不是 Doom。网格光线投射器只有单一高度的方形房间,谈不上地板或天花板,也没有任意几何形状。那是 1992 年的引擎,不是 1993 年的。
更重要的是,两者都没有加载 WAD。关卡是为演示手工搭建的。而这正是我想填补的空白,因为填补它原来依赖于一个我认为真正有趣、而不仅仅是傻乎乎的观察。
这就是让整个项目运转起来的关键。
1993 年 John Carmack 设计 Doom 的关卡格式时,他需要紧凑地存储一个 3D 世界,并在 486 上非常快地读回来。他产出的东西——大概他自己都没用这些术语去想过——是一个规范化的关系型模式。定宽记录。整数主键。表之间的外键。没有空值。大体上是第三范式。
Doom 把它叫做 它是 VERTEXES vertex(id, x, y) LINEDEFS linedef(id, v1, v2, flags, special, right_sidedef, left_sidedef) SIDEDEFS sidedef(id, x_off, y_off, upper_tex, lower_tex, middle_tex, sector_id) SECTORS sector(id, floor_h, ceil_h, floor_tex, ceil_tex, light) SEGS/SSECTORS/NODES 预先计算好的 BSP 树,作为另外三张表 THINGS thing(id, x, y, angle, type, flags)
一个 LINEDEF 持有两个指向 VERTEXES 的整数和两个指向 SIDEDEFS 的整数。一个 SIDEDEF 持有指向 SECTORS 的整数。这些就是外键。没人把它们写成约束,因为 C 没有约束,但它们就在那里,而且成立——我检查了 FreeDoom 两个 IWAD 中全部 68 张地图的每一个外键,没有发现任何违反。
就连光照也是关系型的。Doom 的 COLORMAP 是一张 34 × 256 的查找表,把(光照等级,调色板索引)映射到调色板索引,而 PLAYPAL 把调色板索引映射到 RGB。把这两者组合一次,Doom 的整个光照模型就变成了一个两列连接。纹理映射是一个连接。距离着色是一个连接。
所以项目的第一阶段根本不是什么移植。而是一个 BULK INSERT。一个 Python 脚本读取二进制 lump,输出 T-SQL——一个关卡生成了 3.7 MB 的 INSERT 语句,而 WAD 本来就是这么个东西:一堆压缩过的表行。
SELECT layer = 'wall', shape = geometry::STGeomFromText('LINESTRING('
- CONVERT(varchar(12), a.x) + ' ' + CONVERT(varchar(12), a.y) + ','
- CONVERT(varchar(12), b.x) + ' ' + CONVERT(varchar(12), b.y) + ')', 0) FROM dbo.linedef l JOIN dbo.vertex a ON a.id = l.v1 JOIN dbo.vertex b ON b.id = l.v2 WHERE l.left_sidedef IS NULL UNION ALL SELECT 'portal', geometry::STGeomFromText('LINESTRING('
- CONVERT(varchar(12), a.x) + ' ' + CONVERT(varchar(12), a.y) + ','
- CONVERT(varchar(12), b.x) + ' ' + CONVERT(varchar(12), b.y) + ')', 0) FROM dbo.linedef l JOIN dbo.vertex a ON a.id = l.v1 JOIN dbo.vertex b ON b.id = l.v2 WHERE l.left_sidedef IS NOT NULL AND NOT EXISTS (SELECT 1 FROM dbo.door_special d WHERE d.special = l.special) UNION ALL SELECT 'door', geometry::STGeomFromText('LINESTRING('
- CONVERT(varchar(12), a.x) + ' ' + CONVERT(varchar(12), a.y) + ','
- CONVERT(varchar(12), b.x) + ' ' + CONVERT(varchar(12), b.y) + ')', 0) FROM dbo.linedef l JOIN dbo.vertex a ON a.id = l.v1 JOIN dbo.vertex b ON b.id = l.v2 JOIN dbo.door_special d ON d.special = l.special UNION ALL SELECT 'monster',
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。