TL;DR
配置Drawpile
到drawpile官网下载并安装最新的drawpile。然后联系我开服。
开服之后选择菜单中的Session
,选择Join
,在最下方输入服务端地址,然后连接。
抽牌器的使用
访问抽牌器页面。(不要过度使用,免费版的heroku有时长限制)
输入名字和游戏创建者给的
game code
加入游戏。如果game code
放空,将建立新游戏点击加入或创建之后,最下方的在线列表将显示所有在线的玩家(
如果显示「offline」,请联系我修bug~)如果你是创建者,
draw code
框中应该有一份draw code
,你可以在初始土地块摆放好之后立即抽牌。如果你不是创建者,等轮到你的时候向上家讨要draw code
。抽完牌之后记得把draw code
告诉下家。
具体的界面如下:
- 玩家名,允许重名(
但是你不觉得重名会很麻烦吗) game code
,一般是6位十六进制数,也就是只包含0~9
、A~F
这几个字符。如果在game code
前面添加一个:
字符,那么将进入调试模式,WebSocket连接将不使用SSL(便于本地调试)。- 如果
game code
为空,那么按钮内容是New
,否则是Join
。前者代表将新建游戏。 - 土地块代码。遵循「上-右-下-左-备注」的顺序进行编码,
F
表示草地,R
表示道路,C
表示城市,K
表示修道院。备注表明土地块四边中的哪几个边内容是相联通的。如果备注是K
,那么表示中间是修道院。如果备注以a
,结尾,说明城市带有盾牌。 - 上一个抽牌的人和剩余牌数。「base」代表「没人抽牌,现在这张牌是初始块」
- 土地块图片。图片加载可能比较缓慢,因此需要时刻检查图片和代码是否匹配。
draw code
,一般是4位十六进制数- 为什么没有8?
- 抽牌按钮
- 在线玩家列表
Tip:如果忘了上一张牌是什么,可以按F12,然后打开浏览器的Console查看。
所有人离开之后,游戏将被清除掉。
进行游戏
drawpile和抽牌器都准备就绪之后,就可以打起语音电话开始游戏了。具体方法就是把抽牌器抽出的牌复制到drawpile里面摆放,简单粗暴,但是有效。
前因后果
上个月某日,某人在某群里提到想要在线上玩卡卡颂〔Carcasonne〕。然而卡卡颂官方电子版在steam上价格不低,我又不想上某宝买steam上的桌游模拟器,于是想着不如用点低级的方法「组装」出一个卡卡颂来。
游玩卡卡颂时,大概只有四种动作是必要的:抽取土地块,摆放土地块,收放随从,计分。只要有了牌,后三种都可以借由在线画板/白板实现,比方说drawpile。因此关键的问题就在于如何抽牌。
如何抽牌?
抽牌不仅得有序,抽牌结果还得同步到所有客户端,并且抽牌得是随机的。drawpile的确勉勉强强可以实现,但是体验会很差(提前把所有牌有序摆放到一个图层,然后再建一个图层盖住,所有玩家各自生成随机数进行抽取),手撸一个抽牌器几乎是不可避免的。
抽牌器不仅仅只是一个多端共享的随机数生成器,我还要求它有以下功能:
支持多局游戏同时在服务端上进行
尽可能确保抽牌有序,减小误操作的影响
掉线可以重连,可以看到有那些人在线
界面不丑
引用的外部依赖越少越好,页面加载越快越好
代码简短
最后我用tornado写了一个几百行的服务端,并且除了tornado之外,没有任何其他依赖。因为要支持多个游戏同时进行,就需要有一个游戏的唯一标识,所以引入了game code
。为了保证抽牌有序,我决定阻止一个人连续抽两张牌,以防止网络延迟高的时候玩家狂点抽牌按钮,带来意想不到的结果。另外还引入draw code
,每次抽牌都需要凭证,刚刚抽完牌的人不能再抽牌,但是手上会获得一个draw code
,他需要把这个draw code
发给下一个行动的玩家,这样就最大限度地确保抽牌有序。
为了精简页面,只用了原生的HTML+CSS+JS来写前端,目前拖慢页面加载速度的是两个webfont(为了美观,我最终还是用了webfont)。
抽牌程序已经挂上github了。
把服务端挂到heroku上,抽牌器就算是完成了(当然,还是有些bug要修)。
Drawpile
确保已经安装了drawpile-srv
。执行drawpile-srv
,如果使用默认配置,它就会开始监听本地的27750端口,把这个端口用frp转发出去,外界就可以正常访问了。
也可以选择在服务器上直接部署drawpile服务器,可以参考drawpile的文档。但是因为我服务器上的系统是远古版本的CentOS(服务商的锅),搞起来有点麻烦,所以我最后还是选择比较简单的内网穿透。
开发插曲
一开始我打算把抽牌器部署到腾讯云服务器上,但是因为我没有备案,所以腾讯会限制IP访问(最后变成只有我能连接)。我原以为只有HTTP协议会受到限制,但是实测WebSocket也会(可能是因为WebSocket连接也是从HTTP请求开始的)。把服务端改挂到heroku上就完美解决了,为了这个小东西备案实在是不值得。
转到heroku上之后,发现还是无法抽牌,这导致我们第一次游戏只能由我抽牌,然后由我把牌复制到drawpile里面,其他玩家再进行移动。排查原因,发现是连接被heroku切断了,55秒没有数据交互,heroku就认为连接被限制,然后掐断连接,但偏偏实践中两次抽牌间隔往往要超过55秒。查了一下tornado的文档,设置websocket_ping_interval=40
之后就解决了这个问题。