怎样从头制作一个实时 pokemon go 地图?

物联网

  “Go catch em all! ”

  前言

  在一个月前, Pokemon Go 成了新一轮现象级手游。基于 LBS (Location Based Service) 的设计给社交带来了更多可能。

  从最早玩游戏的时候,我就不甘于受限于游戏的世界,探索各种 “作弊” 的方式。既然是基于位置的游戏,那么制作一个 “地图” 也成了顺其自然的想法。

  这篇文章就记录了我从反向工程解构 Pokemon Go 架构,到建立一个实时 Pokemon 地图的过程。

  成品: mypokemon.io

  探索

  对网络安全有一些了解的同学大概都知道,要 hack 某个游戏或者 app ,你需要做的第一件事就是了解它是怎样运作的。对于 Pokemon Go 也是一样的。

  监听网络流量

  首先,我需要架设一个网络流量监听的工具。由于近年来 REST API 的流行,我假设 Pokemon Go 也是用 REST API 来通信的,那么假设一个 http proxy 便能够进行 MITM ( man in the middle attack ) 攻击,并截取通信记录了。在这里,我用的工具是 burp

  由于我的 macbook 和我的手机接入到了同一个 wifi ,我在电脑上设置的 proxy ,在 iphone 上也能连上。在 burp configure 界面有很详细的设置过程,在设置好电脑跟手机之后,我们就可以在 burp 上看到 Pokemon Go 的网络流量了。

物联网

  解码

  在 burp 里,我们可以看到 Pokemon Go 的 通信记录 ,其中大部分都是毫无意义的二进制码。联想一下 Google 的技术栈,不难想到,这是用 protocol buffer 编码过的,那么用 protobuf 反向解码,就可以看到原请求了。

  我写了一个 小脚本 来批量解码, 解码的结果 是可以 json 格式的 enum 集合。由于没有 schema ,下一步我们需要做的就是搞清楚每个 enum 对应的意思。这个过程就比较枯燥繁琐了,在 github 上效率最高的 project 算是 AeonLucid/POGOProtos ,我们就不要重新造轮子了。

  同样的,在这步之后,把 protocol buffer 转化成可用的 API 也有开源的项目 tejado/pgoapi ,我们就可以跳过这两步,直接开始设计地图了。

  架构设计

  我最终的目标是提供一个 Pokemon Map as a Service,那么所有的数据储存跟采集都必然发生在云端。我决定的架构是这样的:

物联网

  整个系统分为 3 个部分:

  网页前端

  数据查询层

  数据采集层

  网页前端

  由于地图的本身的特性,网页前端是非常轻量级的,基本不需要怎么修改,只需要显示后端发回的数据即可。所以前端我就直接用 github pages 来托管。

  在最新的 chrome 浏览器里,实时定位信息只能在 https 显示的网页上使用,所以为了能够使用用户的位置信息,我用 cloudflare 做 DNS 提供商。用 cloudflare 不光可以一键增加 SSL 证书,还可以提供 CDN 服务,这样也解决了一部分前端加载速度的问题。

  数据查询层

  将数据查询跟数据采集分开也是很自然的想法。地图上的数据在短时间内并不会有巨大的改变,所以查询这个动作是很轻量级的,但是数据采集则需要从服务器爬取信息,是需要很多集群资源的。

  在这里,我用 AWS 的 API Gateway 来给网页端提供接口,API Gateway 再把请求转发给一个 AWS Elastic Beanstalk 的 Django 后端,最后转化成一个简单的 SQL。由于目前并没有很大的流量,所以即使直接 query 数据库延迟也很低。如果今后流量增大了,也许会考虑再加一个 Elastic Search 来提高效率。

  数据采集层

  数据采集才是这个地图中最核心的部分。要设计数据采集层,我们就要先看看有哪些可以用的工具。

  在前面我们已经得到了可以模拟 Pokemon Go 客户端的 python api,在 Pokemon Go 中,每 5 秒,客户端就会发送一个 GET_MAP_OBJECTS 请求给服务器,包含了用户所在的位置,服务器则会返回用户可以抓到的小精灵地址,以及离用户很近的小精灵地址。这些小精灵大概是在距离用户 100 米内的。这就意味着,每一个用户请求,只能覆盖半径 100 米的圆形区域,要搜索 1 平方公里的区域,就需要发送约 100 个请求到 Pokemon Go 服务器。 例子