あらすじ
- Screeps:Arena チュートリアルやったけどおもろそうやん
- CtFやろうと思ったけどクラスないとしんどいなぁ
- WebAssemblyでほかの言語も行けるらしいし勉強がてらGoでやるか
- 環境整えるのめんどいからDockerで作るか
- ビルドできたけどwasm_exec.js動かんやんけ!
- 断念
目次
環境
- Windows10 Home
- TinyGo 0.22.0
- Docker Desktop 4.7.0
Dockerの準備
- https://docs.docker.jp/docker-for-windows/install.html からダウンロード&インストール
- https://tinygo.org/getting-started/install/using-docker/ を参考にTinyGo環境のDockerイメージをPull
ビルド
適当なサンプルコードを用意。
//export [関数名]
を書くと関数として使用できるようになるらしい
package main
import "fmt"
func main() {
}
// export test
func test() {
fmt.Printf("Hello World\n")
}
Dockerでビルド
docker run --rm -v "$(pwd):/src" tinygo/tinygo:0.22.0 tinygo build -o wasm.wasm -target=wasm examples/wasm/export
※引っ掛かりポイント
- Powershell の pwd は
"($pwd)"
とダブルクォーテーションで囲わないと文字列で返してくれない -
examples/wasm/export
はそのままでないとダメだった-
go: go.mod file not found in current directory or any parent directory; see 'go help modules'
って言われる
-
- コマンド実行時のカレントディレクトリは Screeps:Areana で main.mjs が作られるディレクトリ
うまくいくとカレントディレクトリに wasm.wasm ファイルが作られる
他に必要なコードの取得
wasmファイルインポートするためのコード
-
https://text.baldanders.info/golang/webassembly-with-tinygo/ の方の initWASM() を
丸パクリ参考にさせていただきましたorz - 一部変更していますが動作確認できていません。
export function initWASM(url) {
let inst;
const go = new Go();
if ('instantiateStreaming' in WebAssembly) {
WebAssembly.instantiateStreaming(fetch(url), go.importObject).then(function (obj) {
inst = obj.instance; // 追記
go.run(obj.instance);
})
} else {
fetch(WASM_URL).then(resp =>
resp.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, go.importObject).then(function (obj) {
inst = obj.instance; // 追記
go.run(inst);
})
)
}
return inst; // 追記
}
TinyGoのWebAssembly用コード
-
wasmファイルインポートするためのコード の
new Go()
の定義 - Dockerコンテナからコピーしてくる
コンテナを起動し続けるように実行する
docker run -itd -v "$(pwd):/src" tinygo/tinygo:0.22.0 /bin/bash
起動したコンテナ名を確認
※CONTAINER 列の値がコンテナ名
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
起動したコンテナからファイルをコピーしてくる
docker cp [コンテナ名]:/usr/local/tinygo/targets/wasm_exec.js .
Screepsのメインのコード
import { } from '/game/utils';
import { } from '/game/prototypes';
import { } from '/game/constants';
import { } from '/arena';
import "./wasm_exec.js";
import { initWASM } from './wasm.js';
const wasm = initWASM('wasm.wasm');
export function loop() {
console.log(wasm.exports.test());
}
結果
直接的な原因
Screeps:Arena で実行したところ下記のエラー
ReferenceError: require is not defined
at file:///user/wasm_exec.js:104:22
at file:///user/wasm_exec.js:535:3
at (<isolated-vm boundary>)
at init (file:///opt/screeps/arena-engine/src/runner/runner-loop.mjs:244:22)
crpyto が必要だけど require() が存在しないのでエラーになる。
多分ブラウザだと global.crpyto の中身があるんだろうなぁ。
https://github.com/tinygo-org/tinygo/blob/a9ba6ebad9ad85cacd9674a1af9229489a811f68/targets/wasm_exec.js#L103-L110
試しにコメントアウトしてみたけど下記でエラー
https://github.com/tinygo-org/tinygo/blob/a9ba6ebad9ad85cacd9674a1af9229489a811f68/targets/wasm_exec.js#L121-L127
悪あがき
せめて html 上で動かないかなーと思って書き作ってみたけど fetch() が CORS で取れないからあきらめた…。
Webサーバー立てれば動くかもだけど力尽きました。。。
<html>
<head>
<meta charset="utf-8"/>
<script src="wasm_exec.js"></script>
<script src="wasm.js"></script>
<script>
var mod = initWASM('wasm.wasm');
console.log(mod.exports.test());
</script>
</head>
<body>
</body>
</html>
展望
https://github.com/screeps/screeps/issues/120 の話が進んで nodejs の utils ライブラリが使えるようになれば解消するかも?
いかんせん require が使えるようにならないと Tinygo WebAssembly は Screeps では厳しそう。
余談
- https://www.kabuku.co.jp/developers/annoying-go-wasm を呼んでTinyGoダメダメやん…って絶望したけど、最後まで読んだら希望が残っててほっとした。束の間だったけど。。。
- Screeps ってNodeJSで動いてるんじゃないのかぁ。 require() 使えてほしいんだけどなぁ。 Javascriptぽいスクリプトなだけなのかな。