PWAゲームを開発しネイティブアプリ化までした中での課題と対策
https://gyazo.com/3eab0788e33bf11469cf548643510250
https://builderscon.io/builderscon/tokyo/2019/session/c2119a5b-c215-45aa-b95f-3c3fcd0f4b4f
goccy
https://speakerdeck.com/goccy/pwagemuwokai-fa-sineiteibuapurihua-madesitazhong-defalseke-ti-todui-ce
通信量を減らすアセット戦略
4万アセット、1GBくらいある
オフライン戦略
ブラウザ都合で少量しかキャッシュできない
LRUで消える
AndroidとChromeであれば?
CacheStorege
Persistent Storage
ストレージ永続化
成立する条件が複雑で厳しい
現実的な対応
Lighthouseで数値確認
Cache-Control対応
すべてをmax-age=31536000(1年)
アセットバージョンの取得例
静的の場合
バージョン付きで埋め込む
動的に組み立てる場合
1. APIレスポンスにアセットバージョンを含める
2. アセットパスとバージョンの対応表を作成して最初に取得
CDNの自動圧縮ではないファイルの場合はgzip圧縮
Content-Encoding: gzip
m4aやwasmは圧縮効率が良い
lighthouse推奨
マスターデータの管理方法
サーバ・クライアントのどちらで管理すべきか?
クライアント
サーバのレスポンスを早く返せる
容量制限あり
保存データの漏洩リスク
サーバー
レスポンスに必要な情報が含まれる
紐付け処理で応答性能が悪くなるかも
レスポンスが肥大化しやすい
レスポンスからの漏洩のリスク
ノックノートはサーバ管理を選択
IndexedDBに収まるサイズではない
高速検索方法
アプリケーションサーバ上のメモリにのせる・キャッシュさせる
https://github.com/knocknote/rapidash GitHub.icon
B+Tree構造で展開
依存データはEager Loadingで取得する
マスターデータの間引き
APIの設計をyamlに書く
依存するデータや必要なカラムをうけとってAPIを自動生成させる
APIドキュメントも生成できる
ソースコードや通信内容が見れる中でのチート対策
暗号化処理
アセットの秘匿化
アセットパスの難読化
類推可能だと抜かれるリスクあり
パスの途中にハッシュ値を含める
ローカルに保存したあとだと推測されやすい
パス全体をハッシュ化
ハッシュ化されたあとのパスを使ってデバッグが困難
実際の対策
全体をハッシュ化
C++ロジック、Goのアセットアップローダ
S3にアップロード
参照時はEmscriptenでWebAssembly化したモジュール経由
https://github.com/knocknote/libhtml5 GitHub.icon
https://github.com/knocknote/wasm GitHub.icon
1年間本番環境で WebAssembly ( by Emscripten )を使ってきた中で生じた問題とその解決策 - Qiita
全体のハッシュ化はステージング以上の環境に限定
テキストファイルの暗号化
共通鍵暗号方式
暗号化はアセットアップローダ経由
復号はEmscriptenでWebAssembly化したモジュール経由
画像の暗号化
スクショやcanvasのキャプチャは許容
オリジナルの画像を抜かれたくないとき
Data URI Schemeでも表示されるのであまり効果なし
HTMLImageElementを経由するため
WebGLRenderingContext.tex(Sub)Image2DにはImageBitmapを渡せる
XHRで画像データを取得
復号
ImageBitmapに変換
メモリをかなり食うので複数表示時は注意
gl.texImage2D()にImageBitmapを渡す
APIの秘匿化
HTTPリクエストの暗号化
POST /のbodyに入れて送る
HTTPレスポンスの暗号化
バイナリを圧縮して返すときは圧縮効率が悪い
アプリケーションコードの秘匿化
共通鍵暗号の鍵やロジックを守る
アプリケーションコードを隠蔽する
sourceURLを付与してevalするとSourceMapも使える
ソースコード自体を守る
window.JSONなどのglobalなAPIフックから守る
Chromeのローカルオーバライド
任意のファイルをローカル上に保存しているのに置換できる機能
簡単に処理を書き換えたり任意の処理を追加できる
アプリケーションコード評価前に差し替えられてないかチェック
その後ローカルのJSONを取得
code:js
const json = {};
json.parse = window.JSON.parse;
window.JSON = json;
インゲームロジックを守る
インゲーム
遊びのコアな部分
ロジックはサーバで動かしたほうがいいが…
サーバのアプリケーションコードを記述するハードルが高い
ゲームによっては応答速度でUX悪化
ターン制のゲームなので毎ターンサーバと通信して結果を突き合わせる
進行状況をRedisに保存
学び
cgo内で発生したSIGNALはGoからキャッチできない
処理系依存、JSとGoで挙動が違う