リモート開発メインのソフトウェア開発企業のエンジニアブログです

AWS Lambdaを使ってスプラトゥーン2の戦績をstat.inkに定期保存できるようにした

皆さん新年あけましておめでとうございます。今年もよろしくお願いします。
新年と言うわけで、1発目の投稿は少しカジュアルな感じで行こうと思い、表題のネタを取り上げるに至りました。

スプラトゥーン2とは?

多分名前くらいは皆さん聞いた事があるのであまり詳しくは説明しませんが、任天堂が提供しているNintendo Switch専用のゲームソフトで、インターネットを通じてオンラインで対戦が可能なシューティングゲーム(?)です。
ゲームモードの1つであるガチマッチでは、オンラインでランダムに集めた8人を4人ずつの2チームに分け、対抗戦を行う事ができます。

一口にオンライン対戦と言っても、使用できるブキやステージ、ルールは多岐に渡り、非常にバラエティに富んだゲームとなっています。

戦績管理について

ゲームとはいえ、上位のガチ勢は訓練に訓練を重ねた猛者たちが揃っており、そこまでは目指さないまでも、コンスタントに勝ち上がっていく上で戦績の管理は重要だと考えています。(持論)
何故なら、苦手なステージやブキ、ルールの傾向を統計的に把握し、分析をする事が可能だからです。

実は、任天堂が提供している専用のスマホアプリ(イカリング2)を使うと、直近の50試合の戦績を細かく(ルールとステージ、勝敗、自身と対戦者の装備、キル/デス数等)見る事が可能です。
しかし、50件ですと戦績を分析するには不十分です。そこで、イカリング2が使用しているHTTP APIに対して、直接HTTP通信を行う等を行い、定期的に戦績を記録しておく必要があります。

その中の一つに stat.ink と言うOSSがあります。このソフトウェアではこれまでの戦績を管理し、統計的な分析を行う事が可能で、同URLでは作者がセルフホスティングしているサービスを使う事ができます。
ただ、このソフトウェアは戦績の(任天堂のAPIからの)取得自体はセキュリティ上の都合により行っておらず、自前で取得する必要があり、取得したデータをstat.inkに送信する事で蓄積が可能となっています。

死ぬ程雑な図

肝心のデータを取得する方法ですが、これまたいくつかツールがあるのですが、その中の1つにPython製のCLIで動く splatnet2netstatink と言うツールが有ります。CLIと言う特性上、今回のAWS Lambdaで定期実行するのには良さそうなので、(前置きが長くなりましたが)今回はこれをServerless Frameworkを使って定期実行を実現する方法を紹介します。

splatnet2netstatinkの仕様把握とアーキテクチャの設計

まずはCLIの使い方を見てみます。
(※このツールを使うには、イカリング2APIとstat.inkの2サービスのセッションが必要となるのですが、その辺りはドキュメントに書いてあるので割愛します。)

$ ./splatnet2statink.py --help
splatnet2statink v1.3.4
usage: splatnet2statink.py [-h] [-M [N]] [-r] [-s] [-t] [--salmon]

optional arguments:
  -h, --help  show this help message and exit
  -M [N]      monitoring mode; pull data every N secs (default: 300)
  -r          retroactively post unuploaded battles
  -s          don't post scoreboard result image
  -t          dry run for testing (won't post to stat.ink)
  --salmon    uploads salmon run shifts

-r オプションを付けて実行すると、任天堂が提供している「直近の50件」から、stat.inkにまだ未送信の物を自動で判別して送ってくれます。試しに2試合プレイした状態で実行してみましょう:

$ ./splatnet2statink.py -r
splatnet2statink v1.3.4
Checking if there are previously-unuploaded battles...
Previously-unuploaded battles detected. Uploading now...
Battle uploaded to https://stat.ink/@***/spl2/168****
Battle uploaded to https://stat.ink/@***/spl2/168****

もちろん、もう一度実行しても送信される試合はありません:

$ ./splatnet2statink.py -r
splatnet2statink v1.3.4
Checking if there are previously-unuploaded battles...
No previously-unuploaded battles found.

つまり、このスクリプト(+セッション情報)を定期的に実行すれば良さそうです。

AWS Lambda側のアーキテクチャですが、普通にスクリプトと依存パッケージ一式をアーカイブしてアップロードし、CloudWatch Eventsを使って定期実行するように設定すれば良さそうです。(下記参照)

死ぬ程雑な図2

いずれもServerless Frameworkを使って簡単に設定が可能です。

Serverless Frameworkの設定

OSSとして公開していますのでそちらをご確認下さい。(ライセンスはsplatnet2statink同様GPLv3)

要所のみ解説します。

# serverless.yml

# ~~

plugins:
  - serverless-python-requirements

custom:
  pythonRequirements:
    dockerizePip: non-linux

# ~~

functions:
  post_unuploaded_battles:
    handler: handler.main
    events:
      - name: at-every-schedule-rotation
      - description: At every schedule rotation.
      - schedule: cron(0 1/2 * * ? *)

# ~~

1. serverless-python-requirements の利用

Cコンパイルが必要なパッケージのコンパイル作業を、Docker経由で行う事ができます。
これにより、デプロイ(コンパイル)元が非Linuxであっても問題がありません。
※以前同じ様な内容の記事を公開していますので詳しくはそちらをご参照下さい。

2. functions.events.scheduleでCloudWatch Eventsを設定

cron記法により、2時間毎の奇数時に実行されるようにしています。これは、スプラトゥーン2のルール及びステージのローテーションのタイミングと同様となっております。

後はこれを sls deploy するだけです。その際、先述の保存したセッション情報を記録しているconfig.txt(詳しくはsplatnet2statinkのドキュメントを参照)を忘れずに設置しておいて下さい。

動作確認

CloudWatch Logsのストリームを確認してみました:

$ aws logs get-log-events --log-group-name my-logs --log-group-name /aws/lambda/splatnet2statink-prod-post_unuploaded_battles --log-stream-name '2019/01/08/[$LATEST]*****************************' | jq '.events[].message'

"START RequestId: 7d08fcaa-12f1-11e9-a61a-*************** Version: $LATEST\n"
"Previously-unuploaded battles detected. Uploading now...\n"
"Battle uploaded to https://stat.ink/@***/spl2/168****\n"
"Battle uploaded to https://stat.ink/@***/spl2/168****\n"
"Battle uploaded to https://stat.ink/@***/spl2/168****\n"
"Battle uploaded to https://stat.ink/@***/spl2/168****\n"
"END RequestId: 7d08fcaa-12f1-11e9-a61a-***************\n"
"REPORT RequestId: 7d08fcaa-12f1-11e9-a61a-***************\tDuration: 29432.69 ms\tBilled Duration: 29500 ms \tMemory Size: 512 MB\tMax Memory Used: 51 MB\t\n"

見た感じ問題無さそうですね。

まとめ

今回はsplatnet2statinkとServerless Frameworkを使って簡単にstat.inkに戦績を溜め込めるようになりました。
定期実行スケジュールについてですが、お好みで調整(例えばよくプレイするゴールデンタイムでは実行感覚を短くする等)したり、手動で更新するためのbotを作ってみても面白いかもしれませんね。

それでは皆さん良いイカライフを!

← 前の投稿

機械学習の学習済みモデルを使用して API サーバーを構築する

次の投稿 →

RSpecのletの遅延評価を利用してよりコンテクスチュアルなSpecを書く

コメントを残す