CASSELL ACADEMY · NIGHTWATCH
我把「守夜人讨论区」做成了真的
一个粉丝论坛的开发日志 —— 你正在读的这个站,是怎么来的
缘起:一个「要是它真的存在」的念头
《龙族》里有个我一直很喜欢的设定:卡塞尔学院的学生们上的是一个叫守夜人讨论区的校内 BBS。最高管理员是副校长「守夜人」,日常由留级八年的芬格尔打理 —— 可以发悬赏、开博彩、讨论时事,发帖还要被学院主机诺玛盯着。
读书的时候我总在想:如果这个论坛真的存在,它会是什么样子?
后来没忍住,动手做了出来。现在它是一个能注册、能发帖、能评血统、能觉醒言灵的完整论坛。
第一条原则:设定必须落成功能
同人项目最容易犯的错,是把设定当装饰 —— 到处贴 logo 和台词,用起来却是个普通论坛。所以我给自己定了一条规矩:
每一条原著设定,要么落成一个真的能用的功能,要么就不做。
于是有了这些:
学籍认证(注册) 注册不叫注册,叫「新生入学登记」。填完档案信息,必须完成 3E 考试:从 30 道题库里随机抽 5 道单选题。题目全部出自原著 ——「镰鼬的序列号是多少」「自由一日胜者的奖励不包括哪一项」「哪句铭文刻在七宗罪剑匣上」。答完,诺玛在服务端当场评分。
血统等级(权限) 3E 成绩直接换算成血统:答对 5 题 S 级、4 题 A 级……全错 E 级。血统决定你能看到哪些版块:低血统点进「黑太子档案」,看到的不是隐藏、不是空白,而是诺玛的权限拒绝播报 —— 原著里卡塞尔学院就是这么分层的。
顺便说,F 级「待评」不是考试结果,是「尚未评定」的状态 —— 留级八年的芬格尔就停在这一档。
言灵(能力) 登记完成后,系统按你的血统档位,从言灵周期表里随机觉醒一个言灵,本人无法指定。S 级的池子里是莱茵、烛龙这些绝密级;F 级没有言灵。档案页提供「重新觉醒」,但只在同一档位的序列号区间内换。
诺玛(学院主机) 它是这个论坛的灵魂。当前版本的诺玛是个规则引擎:你的每一条发言都会过一遍风纪审查(校外联络、代考、账号交易的暗语会被拦下),提到「言灵」「血之哀」「3e 考试」「楚子航」会触发对应回复。每帖自动回复上限 4 条,免得它刷屏。
芬格尔(彩蛋) 水帖会被他逮到。正文 ≤ 6 个字,或者纯粹一句「顶 / 沙发 / 打卡」,他会亲自下场吐槽。
守夜人小屋(管理后台) 学院的暗面中枢:发赏金、扣余额、封学籍、升降版主,都在这儿。
视觉:诺玛的全息界面
我想要的不是「深色主题」,而是诺玛的屏幕。
主色定成「全息青」—— 冷、精确、无处不在,代表机器;金色留给秘党和学院,作为次要强调(悬赏、公告、身份标识)—— 那是「人」的一极。背景铺一层很淡的技术网格,面板四角做 HUD 角标(远看是界面,近看才发现的小细节),数据一律走等宽字体。
标题用衬线 —— 学院该有的古典感;正文无衬线。这套视觉没有引任何外部字体,全靠 CSS。
技术选型
- ·Next.js 16(App Router / Server Components / Server Actions)
- ·TypeScript + Tailwind CSS v4(用 @theme 定义午夜配色)
- ·Prisma 6 + SQLite(单机论坛够用,备份就是拷一个文件)
- ·Caddy 反代 + PM2 常驻,一台轻量云服务器跑完
认证没有上 NextAuth,自建了一张 session 表 + HttpOnly cookie。理由是:这个站的权限判定全是自定义的(血统档位 + 角色 + 版块),套框架反而绕。
安全上的三条铁律,分享一下思路:
- ·答案不下发:3E 的题库、正确答案、解析只存在于服务端代码里;下发给浏览器的题目一律先剔除答案,客户端包里翻不出正确选项。
- ·评分只在服务端:注册接口完全忽略客户端提交的血统字段 —— 改表单、伪造请求都拿不到高等级。
- ·抽题集要校验:既然改成随机抽 5 题,就要防住「客户端只回传自己会的题」。服务端会检查回传的题目集:必须恰好 5 道、互不重复、都在题库里,否则一律按 0 分处理。
踩坑记(四个真实的坑)
都是部署上线之后才现形的。
一、被自己家的门禁拦下的背景视频
给登录页做底层轮播时,我上传了压缩好的 mp4,本地一切正常;部署上线后,背景黑屏。
排查了半天:视频文件明明 200 可访问。最后发现 —— 站点的全站门禁(middleware)有一张静态资源白名单,png、svg、webp 都在,唯独没有 mp4。于是访客请求背景视频时,被门禁当成「未登录访问内页」,307 重定向走了,视频自然加载不出来。
修起来是一行:把 mp4 / webm / avif 加进白名单。教训是:门禁的白名单要用扩展名表驱动,别手写。
二、4K 60fps 的视频不能直接当背景
素材是两段 4K 60fps 的动画,原片每段 10MB 上下、码率 15Mbps —— 直接挂上去,手机流量党会想打我。
处理成:1080p / 30fps / 无音轨 / CRF26 / faststart,每段压到 1.5~2MB;两张插画转成 webp(每张 230~280KB);再用 ffmpeg 抽首帧做 poster,切换时不会有黑场。
轮播逻辑也是纯 CSS + 一个定时器:插画停 7 秒,视频播完即切,交叉淡入淡出 1.2 秒;提前预热下一段素材。
三、自动播放会失败,「减少动态效果」是硬需求
背景视频必须 muted + playsInline 才给自动播放 —— 这是硬性规则。但即使用了,在某些省电模式下依然会被浏览器拦下。
所以做了三层降级:
- ·播不动就显示 poster 静态图,定时器照常轮换;
- ·用户系统开了「减少动态效果」(prefers-reduced-motion),直接停在第一张插画,不轮播、不播视频;
- ·整段背景是纯装饰(aria-hidden),不影响「能不能登进来」—— 这条是底线。
四、随机抽题后,「重新渲染」会偷偷换题
抽题改版后踩了个隐蔽的坑:表单提交失败时(比如邮箱验证码填错),页面会重新渲染,服务端又抽了一次题—— 用户刚答完的 5 道题全换了,答案作废。
解法是把抽中的题目在客户端「钉住」:首屏拿到哪 5 道就一直显示哪 5 道,只有手动刷新才换一批。
它现在的样子
打开本站,未登录访客直接落在登录页:屏幕底层是四段素材(两段视频 + 两张插画)的轮换背景,右半边浮着一块半透明玻璃卡片—— 学籍认证表单,卡片下面透着流动的画面。
这条「门户长什么样」的路,我改了三版:一开始是全屏开场动画(点一下才进入);然后给登录页做了右半边半透明卡片 + 底层轮播;最后干脆把门户页下线,让访客直接落在登录页 —— 少一跳,也少一处要维护的界面。
部署流程也走顺了:本地构建通过 → 上传前先在服务器整目录备份 → 上传校验哈希 → 服务器构建 → 重启进程 → 线上验证。每一步都有回滚点,改错了不至于熬通宵。
写在最后
这个项目让我确认了一件事:做同人项目,最爽的不是还原度,而是「设定逼着你做技术决策」。
诺玛要盯所有发言,我就得写规则引擎;血统要分级权限,我就得设计一整套可见性判断;3E 要随机抽题,我就得把评分做成「防翻车」的。这些技术问题,反过来又让那个虚构的学院变得真实。
原著里说,卡塞尔学院的学生都带着「血之哀」—— 龙血纯度越高,越难融入人群。
这个论坛就是给同类留的一张桌子。
本文为个人非商业粉丝项目开发日志,《龙族》相关设定版权归原作者及出版方所有。