0%

通过ipfs+onsyuri在浏览器上玩ons游戏

经过初步测试,通过ipfs索引文件,在浏览器上玩ons游戏貌似是可行的,虽然还存在一些问题。

大致原理

  1. onsyuri的web版本可以通过文件索引和url懒加载游戏资源文件。
  2. ipfs可以通过网关加文件的cid获取文件

结合上面两点,能够有一种神奇的效果,将游戏的资源文件上传到ipfs,将每个文件的cid写到onsyuri_index.json内对应的url上,就可以通过ipfs索引游戏文件,并且能够分布式的扩散到其他节点,能够自然的完成游戏的传播与分享。

前置条件

在开始之前先准备好以下东西:

  • ipfs desktop
  • onsyuri.wasm
  • onsyuri.js
  • onsyuri.html
  • onsyuri_index.py
  • 一个ons的游戏

前往ipfs的github发布页上下载对应系统的安装包并安装,windows的是exe结尾,具体使用可以在网上多搜索,这不是本文的重点,不详细说明。

onsyuri同理,发布页下载web版本之后解压,或者自行编译。

明确目标

网页运行的流程大概是,先在html文件里面加载了js胶水代码,js胶水代码加载了wasm文件,之后加载json索引文件,在游戏运行的时候通过索引加载游戏的资源文件。

我们要把这些移植到ipfs上,那就要把所有通过url获取文件的方式全部改成通过cid获取。

值得注意的是,cid是根据文件内容计算出来的,在生成cid之后就不要修改文件的内容,如果修改了文件那就要重新上传并使用新的cid,以及一定要注意修改的顺序。

修改html用get方式传入index.json

首先打开onsyuri.html在文件开头找到

1
2
<meta onsyuri_js="onsyuri.js">
<meta onsyuri_index="onsyuri_index.json">

这是通过在meta设置文件名之后在这个位置


在这里确定的文件url。

因为每个游戏的index都不一样,如果为每个游戏都上传一个html,我认为是很没有必要的,所以我决定把这个变成一个通用的模拟器页面,通过get传参的方式传入index。我js不太会,以下是网上找的获取get参数的方法,如果有更好的方法也可以更改:

1
2
3
4
5
function GetQueryString(name){
var reg=new RegExp("(^|&)"+name+"=([^&]*)(&|$)");
var r=window.location.search.substr(1).match(reg) ;
if(r!=null)return unescape(r[2]);return null;
}

之后更改获取index的代码如下:

之后在进入模拟器页面时加上?index=onsyuri_index.json或者自己的json的url即可。

修改js

onsyuri.js文件主要是用来加载onsyuri.wasm的,而wasm没有任何依赖,可以直接上传ipfs,然后复制cid

之后打开js,这个生成的胶水代码完全没有格式化,直接用查找替换将onsyuri.wasm替换成上面的cid

这时候这个胶水文件就完成了,可以上传了

最终修改html

胶水代码的url也是在meta里面设置的,修改为cid

1
<meta onsyuri_js="QmQq3UxfKDBsD5gGMy3NK7yZFEjSmxPG2KaxPKKH3imHFF">

这时候,html和js和wasm已经完全耦合在一起了,除了html,修改其中任何一个文件,都要按照上面的顺序修改并重新上传这3个文件,如果要修改,请确保自己神志清醒,知道自己在干什么。

游戏文件上传和生成索引

建议拆包并开启懒加载,以获取最佳体验。不过应该是我多虑了,游戏资源拆包之后可能会生成上万个文件,大多是语音和序列帧动画等数量极多的文件,不知道ipfs能够索引的最大文件数量是多大,不过这么长的哈希,应该是不会冲突了吧即使我上传100G大合集应该也不会吧

拆包需要自行编译nsadec或者用其他软件拆包,这里不多介绍。计算cid的方法,我也不太懂哈希,之前调库瞎搞搞了一晚上都没算出跟上传的一样的,所以不要管怎么计算cid了,从调库变成系统调用ipfs,如果是使用的ipfs desktop,在设置勾上“控制台”

修改onsyuri_index.py,注意,以下并非规范操作,且不知道会有什么其他影响,谨慎使用

1
2
3
4
5
6
7
8
9
10
11
def make_filelist(inpath, urlbase):
files = []
for _root, _dirs, _files in os.walk(inpath):
for file in _files:
path = os.path.join(_root, file)
relpath = os.path.relpath(path, inpath).replace('\\', '/')
#url = urlbase + relpath if urlbase!="" else ""
url=os.popen(f"ipfs add -nq \’{path}\’").read()[:-1]
if url=="": files.append({"path": relpath})
else :files.append({"path": relpath, "url": url})
return files

path的必须要用引号包围,有一些文件命名不规范,名字里面带有空格。

-n的参数是不上传只计算哈希,如果要顺便上传就去掉-n,只保留-q

[:-1]是去掉行末换行。

最后使用ipfs add 上传游戏文件和onsyuri_index.json就完成了,记得记下onsyuri_index.json的cid。

测试使用

ipfs公共网关(如https://ipfs.io)都位于国外,因为一些网络原因,我们的节点一般是不能和公共网关连接的,所以我们的游戏资源可能无法同步到公共网关(其实也可以,但是需要很久),所以最好使用自己节点的网关来访问。使用方法为:

1
(ipfs网关)/ipfs/(onsyuri.html的cid)?index=(对应游戏的onsyuri_index.json的cid)

用我自己的举个例子:

  • 本地ipfs网关为127.0.0.1:8080
  • onsyuri.html的cid为QmX6UfTaWxiKfaPh9PQ59ExACboGr9TUvCs3pVtMcExsJj
  • 游戏的onsyuri_index.json的cid为Qmf9rq18AMbgsBiCwmrCNxxsnbsk3BLBwT47i3h1UfC6UR

那这个使用的链接应为

1
http://127.0.0.1:8080/ipfs/QmX6UfTaWxiKfaPh9PQ59ExACboGr9TUvCs3pVtMcExsJj?index=Qmf9rq18AMbgsBiCwmrCNxxsnbsk3BLBwT47i3h1UfC6UR

效果如下:

用公共网关也可以但是卡得几乎不能玩

如果要分享给别人玩,目前没有什么好的方法,只能让对方也装上ipfs desktop,使用自己的节点来访问,国内节点之间连接的速度还是可以接受的。

如果将来使用这种方式的人多了,资源终于缓存到公共网关了,那全世界的人就都能够通过公共网关流畅的运行了。