KedamaDiff开发日志其一
Overviewer历史地图抓图器模块 开发日志其一: 世界背景与单张图片
前言
制作 Kedamadiff 的初衷,是看到了玩家 SilentDepth 的毛线非官方地图。仅凭玩家在 QQ 群里主动通报建造进度,让 SD 来手动爬图,我想是一件很低效的事情。正好,毛线官方提供了一套基于 Overviewer 的官方地图,每日自动渲染,地图上的每一寸变化都能尽收眼底。通过这个项目,帮助SD更有效率地跑图,也持续记录毛线大陆的风貌变化,供玩家在若干岁月后,回顾当初的辛勤劳作,与公益服带给大家的欢乐。
本文作为项目的开发日志,始作于2018年。随着版本迭代,文中的很多内容都会被废弃/升级掉。可雁过留声,它们在这个项目里仍值得留下一席之地。
得名
即:
我眼中的 Overviewer 地图
既然分析的是 Overviewer 地图,关于毛线本身的设定就一笔带过吧:
目前正在运营的地图有 2 4个主世界—— v1, v2, v3, v4 ,玩家活动范围在方圆9600格内。以斜45°角向下俯瞰,整个地图呈现为扁形。
Overviewer对地图进行分块保存,我们看到的这张圆饼,其实是由无数图块拼接而成。每张图块有以下属性可供利用:
长宽:384*384像素
缩放等级:一个整型非正数,最大为0(Max)。在最大缩放等级下,可以清晰地看到每个Minecraft建筑方块。
- 为消歧义,本文规定:缩放越大,看到的地图细节最多(每一个方块都清清楚楚);缩放越小,视野越大(全地图)
ETag: 在官方网站储存时,为缓存机制提供服务的值,形如
"5a945d9f-10ab1"
(包含双引号)。虽然猜测横线前后分别为图片更新时的Unix时间戳和图块的文件长度(字节),但不能进一步利用这些值了。- ETag更新时不一定导致图片内容实质性更改,猜测是附近区域变动导致本图块被Overviewer波及到,被重渲染。但因图块内地面方块布局未实质更改,渲染出的图片内容(以及大小和hash)都不会变更。
Hash: 图片的摘要值,网站并不提供,下载到本地后再作计算。笔者使用sha1进行摘要计算。
考虑到一致性问题,本项目尽量避免缓存文件的SHA1和(最新)ETag。
毛线的地图被配置为每日更新,在北京时间上午11:00后可以进行爬取到新图。Overviewer图片的URL格式为:
“http[s]://{}/{}/{}{}.jpg?c={}”.format(主机名,项目名(例如kedama),地图名,path,Unix时间戳),path是形如 /1/2
的字符串,下详。
网站地图的四叉树图块坐标系统
理想情况下,无论地图扩张到了多大,在最小的缩放等级下,都会有且只有4个图块:
分别位于左上/右上/左下/右下四个方位。
增大缩放等级后,更细一级的图块将替换上述图块,以上述图块之一为根,/0 /1 /2 /3 分别位于新根的四个方位。
以此类推,就能得出每个图块的请求链接。
想要知道毛线世界在历史上的某一天是什么样子,只需要开动计划任务,在每天正午时分对官方图站进行全量抓取。听起来超级简单的,是不是?
理论上确实是这样,那我们就开始抓图吧~
理想与现实
拿到图片之是为了丢给电脑,让它对比两个时间点之间的变化。那电脑需要多细致的地图呢?像下面这样,每一根毛都看得清清楚楚吗?
答案显然是否定的,归根结底原因只有一个——
穷
让我们算一笔账,上面用来演示的大圆饼地图,抛去椭圆外侧的黑边,可以划分出 104,857.6 个 0 级图块。每张图块大约100KBytes,每次抓取这张地图,只需要花掉10GB硬盘空间(和流量)。毛线运营一年半有余,500天就是5TB,服务器维护得当,未来还能运行很长时间。
为了这样一个小项目投入上述成本,在金钱上显然是不值得的,这样的一套主机笔者目前还攒不出来。因此我们需要在一些地方偷工减料。
向现实低头
秉承图钉精神,在抓图层级的选择上我们以“又不是不能看”为纲,尽量抓取观感尚可,缩放比例较大的图块。经过实地测量,最终选择了比最大缩放小3级(-3)的层次来进行长期跟踪。其实如果条件允许,-2级 更佳。
开始抓图
选好了缩放级别,现在可以抓图了。编辑好用于构造URL的一系列值后,我们终于抓到了第一张-3级图块。
像这样子的,’前缀/0/3/3/3/3/3/3.jpg?后缀’(本文件名格式已弃用)
怎么保存它呢?一开始笔者将里面的‘/‘和数字部分(称为path组件/ path component)提取出来,用’_’替换了’/‘,得到了类似’0_3_3_3_3_3_3’的文件名。
一眼就能看到,在图站的坐标系统中,每个path组件都对应着指定的区域。而为了实现后续所需的增量更新、区域历史回顾等功能,需要记录区域的每次更新信息。
然而,这样的文件名,却无法用来对指定区域进行长期追踪,【构建自己的地图坐标系,后文】一节给出了原因和解决方案。
保存图片和部分辅助信息[已被迭代]
将图片保存在 images/地图名/更新日期
文件夹里,将网络相应的ETag(str)和图片文件的保存位置(str)存入更新历史里。数据库咱可是暂时不会的呢,简单试错(以字符文件的形式生成/读写dict)后,笔者选用了json来存储更新历史文件。这种序列化方式生成的文本文件分行很多,无论在文本编辑器还是git上看都很清晰,便于简单地查看和修改更新记录。反观前者(以dict文件的形式序列化)无论塞进多少信息都只是一行(一个dict对象),打开就是乱糟一坨。
下图展示了json保存的真实图块更新记录:
【升级去向:转为使用(sqlite)数据库,不再存储单个图块的保存位置】