TL;DR

配置Drawpile

drawpile官网下载并安装最新的drawpile。然后联系我开服

开服之后选择菜单中的Session,选择Join,在最下方输入服务端地址,然后连接。

抽牌器的使用

访问抽牌器页面。(不要过度使用,免费版的heroku有时长限制)

  1. 输入名字和游戏创建者给的game code加入游戏。如果game code放空,将建立新游戏

  2. 点击加入或创建之后,最下方的在线列表将显示所有在线的玩家(如果显示「offline」,请联系我修bug~)

  3. 如果你是创建者,draw code框中应该有一份draw code,你可以在初始土地块摆放好之后立即抽牌。如果你不是创建者,等轮到你的时候向上家讨要draw code。抽完牌之后记得把draw code告诉下家。

具体的界面如下:

界面
  1. 玩家名,允许重名(但是你不觉得重名会很麻烦吗
  2. game code,一般是6位十六进制数,也就是只包含0~9A~F这几个字符。如果在game code前面添加一个:字符,那么将进入调试模式,WebSocket连接将不使用SSL(便于本地调试)。
  3. 如果game code为空,那么按钮内容是New,否则是Join。前者代表将新建游戏。
  4. 土地块代码。遵循「上-右-下-左-备注」的顺序进行编码,F表示草地,R表示道路,C表示城市,K表示修道院。备注表明土地块四边中的哪几个边内容是相联通的。如果备注是K,那么表示中间是修道院。如果备注以a,结尾,说明城市带有盾牌。
  5. 上一个抽牌的人和剩余牌数。「base」代表「没人抽牌,现在这张牌是初始块」
  6. 土地块图片。图片加载可能比较缓慢,因此需要时刻检查图片和代码是否匹配。
  7. draw code,一般是4位十六进制数
  8. 为什么没有8?
  9. 抽牌按钮
  10. 在线玩家列表

Tip:如果忘了上一张牌是什么,可以按F12,然后打开浏览器的Console查看。

所有人离开之后,游戏将被清除掉。

进行游戏

drawpile和抽牌器都准备就绪之后,就可以打起语音电话开始游戏了。具体方法就是把抽牌器抽出的牌复制到drawpile里面摆放,简单粗暴,但是有效

前因后果

上个月某日,某人在某群里提到想要在线上玩卡卡颂〔Carcasonne〕。然而卡卡颂官方电子版在steam上价格不低,我又不想上某宝买steam上的桌游模拟器,于是想着不如用点低级的方法「组装」出一个卡卡颂来。

游玩卡卡颂时,大概只有四种动作是必要的:抽取土地块,摆放土地块,收放随从,计分。只要有了牌,后三种都可以借由在线画板/白板实现,比方说drawpile。因此关键的问题就在于如何抽牌。

如何抽牌?

抽牌不仅得有序,抽牌结果还得同步到所有客户端,并且抽牌得是随机的。drawpile的确勉勉强强可以实现,但是体验会很差(提前把所有牌有序摆放到一个图层,然后再建一个图层盖住,所有玩家各自生成随机数进行抽取),手撸一个抽牌器几乎是不可避免的。

抽牌器不仅仅只是一个多端共享的随机数生成器,我还要求它有以下功能:

  1. 支持多局游戏同时在服务端上进行

  2. 尽可能确保抽牌有序,减小误操作的影响

  3. 掉线可以重连,可以看到有那些人在线

  4. 界面不丑

  5. 引用的外部依赖越少越好,页面加载越快越好

  6. 代码简短

最后我用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之后就解决了这个问题。