一个基于 Node.js、Express 和 WebSocket 的多人在线诈唬出牌小游戏。玩家可以创建房间、邀请朋友加入,并在浏览器中实时同步对局状态。
- 创建房间并生成房间码
- 输入昵称后加入多人对局
- 大厅阶段允许修改昵称,开局后锁定
- 基于 WebSocket 的实时同步
- 支持部署到公网服务器后进行多人联机
- 支持
2-8名玩家同局 - 使用一副
54张牌:- 四种花色的
A-K - 大王和小王各一张
- 四种花色的
- 大小王属于万能牌,在质疑结算时可视为任意声明点数
- 房主创建房间时设置最大人数
- 所有玩家加入前都必须输入昵称
- 至少
2名玩家后,房主才可以开始游戏 - 开始后系统洗牌并尽量平均发牌
- 开局后昵称不能再修改
- 每一轮由当前首家先出牌,并声明一个点数作为“本轮牌面”
- 首家可以打出任意自己手里的牌,但其他玩家只能看到你“声明出了几张、声明的点数是什么”,看不到真实牌面
- 本轮后续玩家如果继续出牌,声明点数必须和本轮牌面一致
- 玩家轮到自己时有三种选择:
- 出牌
- 不出
- 质疑上一手
- 每次至少出
1张牌 - 只能打出自己当前真实持有的牌
- 实际打出的牌可以和声明不一致,因此可以诈唬
- 如果你打出的是真牌,也就是所有牌都符合声明点数,或者是大小王,那么这手牌在结算时会被判定为真实
- 只有在桌上已经存在上一手时,后续玩家才能选择“不出”
- 当前这一轮的首家不能直接“不出”
- 如果轮到你响应上一手,而你在
20秒内没有操作,系统会自动判定为“不出” - 如果其余玩家依次都选择“不出”,最后一次成功出牌的玩家赢得这一轮:
- 桌面上的牌进入弃牌堆
- 该玩家成为下一轮首家
- 下一轮重新声明新的牌面点数
- 只有当前轮到你、且桌上有可响应的上一手时,才能发起质疑
- 不能质疑自己刚打出的牌
- 发起质疑后,系统会短暂公开上一手的真实牌面,然后自动结算
- 若上一手的所有牌都满足以下条件,则质疑失败:
- 牌点数与声明一致
- 或该牌是大王 / 小王
- 质疑失败时:
- 质疑者收走桌面上的所有牌
- 上一手出牌者成为下一轮首家
- 若上一手存在任意一张既不是声明点数、也不是王的牌,则质疑成功
- 质疑成功时:
- 上一手出牌者收走桌面上的所有牌
- 质疑者成为下一轮首家
- 玩家手牌出完后,会先进入待确认完成状态
- 如果这名玩家在后续结算中没有重新收回牌,就会正式进入完成顺序
- 当场上只剩
1名仍持有手牌的玩家时,对局结束 - 排名按出完手牌的先后顺序确定,界面会显示完整名次
本项目是一个普通的 Node.js 应用,不需要额外的构建工具或数据库。请先确认本机已经安装:
- Node.js
18或更高版本 - npm,通常会随 Node.js 一起安装
可以在终端中检查版本:
node -v
npm -v在项目根目录执行:
npm install项目依赖只有 express 和 ws,安装完成后会生成或更新 node_modules/。
继续在项目根目录执行:
npm start该命令等价于:
node server.js启动成功后,终端会输出类似:
Hu Pai server running at http://0.0.0.0:3000
然后在浏览器打开:
http://localhost:3000
服务默认使用:
HOST=0.0.0.0PORT=3000
如果需要改端口或显式指定监听地址,可以设置环境变量后启动。
macOS / Linux / Git Bash:
HOST=0.0.0.0 PORT=3000 npm startWindows PowerShell:
$env:HOST="0.0.0.0"
$env:PORT="3000"
npm startWindows CMD:
set HOST=0.0.0.0
set PORT=3000
npm start如果 3000 端口已被占用,可以换一个端口,例如:
$env:PORT="3001"
npm start此时浏览器访问:
http://localhost:3001
本地测试多人对局时,可以打开多个浏览器标签页访问同一个地址:
http://localhost:3000
建议流程:
- 第一个标签页创建房间
- 复制房间码
- 其他标签页输入不同昵称并加入同一个房间
- 房主点击开始游戏
- 分别在不同标签页中出牌、不出、质疑、退出房间或投降
如果只是自己在同一台电脑上多开浏览器测试,所有标签页都打开:
http://localhost:3000
如果想让手机、平板或其他电脑一起加入,通常需要这些设备和主机在同一个局域网里。最常见的情况是:
- 所有设备连接同一个 Wi-Fi / WLAN
- 或者所有设备连接到同一个手机热点
- 或者用网线和 Wi-Fi 接在同一个路由器下面
这里的“主机”就是运行 npm start 的那台电脑。其他玩家不需要运行代码,只需要用浏览器访问主机给出的局域网地址。
启动服务后,终端可能会输出类似:
Open one of these URLs:
- [本机] http://localhost:3000
- [以太网 2] http://192.168.56.1:3000
- [WLAN] http://172.26.71.105:3000
这些地址的含义是:
[本机] http://localhost:3000:只给主机自己用。localhost永远表示“当前这台设备自己”,所以朋友在自己的手机或电脑上打开这个地址,会访问到朋友自己的设备,而不是你的主机。[WLAN] http://172.26.71.105:3000:通常是 Wi-Fi / 无线网卡地址。如果大家连的是同一个 Wi-Fi,一般让其他玩家打开这个地址。[以太网 2] http://192.168.56.1:3000:通常是某个有线网卡、虚拟网卡或虚拟机网卡地址。除非其他玩家也在这个网络里,否则他们大概率打不开。像192.168.56.1常见于 VirtualBox / 虚拟网卡,不一定适合联机。
简单判断方法:如果大家都连同一个 Wi-Fi,优先试 [WLAN] 那一行;如果主机用网线连接路由器,优先试 [以太网] 那一行;localhost 只用于主机自己。
联机步骤:
-
主机连接 Wi-Fi 或热点。
-
主机在项目目录运行:
npm start
-
主机自己可以打开
http://localhost:3000。 -
其他玩家打开终端里显示的
[WLAN]或正确网卡地址,例如:http://172.26.71.105:3000 -
主机创建房间,把房间码发给其他玩家。
-
其他玩家输入昵称和房间码加入。
如果主机的 IP 变化了,例如换了 Wi-Fi、重连热点或重启电脑,需要重新看终端输出的新地址。
在 Windows 上查看本机 IP:
ipconfig找到当前网络下的 IPv4 地址,例如 192.168.1.23。其他设备访问:
http://192.168.1.23:3000
如果无法访问,通常需要检查:
- 服务器终端是否还在运行
- 其他设备是否和主机连接同一个 Wi-Fi / WLAN / 热点
- 其他玩家打开的是主机的
[WLAN]或正确网卡地址,而不是localhost - Windows 防火墙是否允许 Node.js 或当前端口通过
- 路由器是否开启了客户端隔离
- 学校、公司、酒店 Wi-Fi 可能禁止设备互相访问,这种情况下同一 Wi-Fi 下也可能打不开
- 看到
EADDRINUSE:端口已被占用,换一个PORT后重新启动。 - 浏览器能打开页面但房间不同步:确认页面地址使用同一个主机和端口,反向代理或网络环境需要支持 WebSocket。
- 修改代码后没有生效:当前没有热重载,需要停止服务后重新执行
npm start。 - 关闭终端后房间消失:房间和对局状态保存在服务进程内存中,服务停止后会清空。
当前没有自动化测试。修改后建议启动服务,并至少用多个浏览器标签页手动验证以下场景:
- 创建房间并邀请其他玩家加入
- 房主开局并确认玩家收到手牌
- 首家声明点数后,其他玩家执行出牌、不出、质疑
- 验证质疑成功和质疑失败两种结算
- 验证全员不出后进入新一轮
- 刷新页面后确认可恢复为原玩家身份
- 断开玩家连接后确认离线玩家不会阻塞后续回合
服务端是单实例内存房间模型,部署时要求所有玩家连接到同一个实例。
HOST: 监听地址,默认0.0.0.0PORT: 监听端口,默认3000
- 需要把 HTTP 请求转发到 Node 服务
- 需要保留 WebSocket Upgrade 头,否则实时同步无法建立
- 如果由 HTTPS 终止,前端会根据当前页面协议自动使用
wss://
- 房间和对局状态保存在进程内存中,服务重启后会丢失
- 不支持多实例横向扩展
- 页面刷新会尝试基于本地保存的房间码和玩家身份恢复连接;服务重启、清理浏览器存储或换设备后无法恢复
- 不包含持久化存储或 P2P 直连能力