LoginSignup
1
0

More than 1 year has passed since last update.

Screeps:ArenaをTinyGoのWebAssemblyで書こうとして断念した話

Posted at

あらすじ

  • Screeps:Arena チュートリアルやったけどおもろそうやん
  • CtFやろうと思ったけどクラスないとしんどいなぁ
  • WebAssemblyでほかの言語も行けるらしいし勉強がてらGoでやるか
  • 環境整えるのめんどいからDockerで作るか
  • ビルドできたけどwasm_exec.js動かんやんけ!
  • 断念

目次

環境

  • Windows10 Home
  • TinyGo 0.22.0
  • Docker Desktop 4.7.0

Dockerの準備

ビルド

適当なサンプルコードを用意。
//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ファイルインポートするためのコード

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用コード

コンテナを起動し続けるように実行する

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ぽいスクリプトなだけなのかな。
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0