概要
やりたいことは半年前から変わらず、キューブの世界のデータをバイナリキャッシュサーバーに置き
PlayFab ログインユーザーが Unity クライアントから CDN 経由で最新の世界データを引いてくることです。
できれば圧縮したファイルをユーザーに届けたい(いや必須で)
現在の進捗は次の記事の通り
golang 自作バイナリキャッシュサーバーが S3 に定期バックアップを保存している状況
調査結果とアイディア
公式ブログによれば?
blog.playfab.com
次の GetContentUploadUrl api をオブジェクトキーとシークレット情報で実行すると url が得られるのでこれにファイルアップロードリクエストを投げれば良いらしい
api.playfab.com
アイディアはここから
であるならば CloudScript 側でシークレットキーを使って(いらないかもだけど)アップロード url を作りこれを golang サーバーにリクエストボディで伝える
その url に golang から圧縮したファイルアップロードをしてもらう感じ いけそうですよね?
やってみます。
アップロード url を CloudScript から作ってバイナリキャッシュサーバーに渡す
CloudScript の説明を読むと、次のサーバーAPIフルアクセスになってて、クライアントから切り離されているからゲームコード書いていいよとある
api.playfab.com
残念ながら GetContentUploadUrl はこの Server リストに載っていなくて、次のように CloudScript からも http リクエストを作らなければならなかった
CloudScript の実装(動作確認済み)
handlers.updateContentFileCubedata = function (args, context) { // get upload url var headers = { "X-SecretKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" }; var body = { Key: "hoge.zip", ContentType: "application/zip" }; var url = "https://XXXXX.playfabapi.com/Admin/GetContentUploadUrl"; var content = JSON.stringify(body); var httpMethod = "post"; var contentType = "application/json"; var response = http.request(url, httpMethod, content, contentType, headers); response = JSON.parse( response ); // request zip upload headers = { "Authorization": "Basic dXNlcjpwYXNzd29yZA==" }; body = { dataUrl: response.data.URL }; url = "https://xxx.your.domain.net/upload"; content = JSON.stringify(body); httpMethod = "post"; contentType = "application/json"; response = http.request(url, httpMethod, content, contentType, headers); return { responseContent: response }; };
※Authorization の値は user:password の値なので、本番では使われていません
golang で zip アーカイブ
これでいけました。(イケてません)
// data をアーカイブ func compress(data []byte) (resData *bytes.Buffer, err error) { b := new(bytes.Buffer) zipWriter := zip.NewWriter(b) defer zipWriter.Close() writer, err := zipWriter.Create("filename") if err != nil { log.Fatal("zip input file create error.", err) return nil, err } writer.Write(data) return b, nil }
golang で 圧縮と解凍
byte 配列を圧縮して byte 配列にしたいなら gZip ですよ。
zip はアーカイバなのでファイル名が中に必要 でこれは不要と
実装例はこちらを見つけました。
gist.github.com
テストのため byte 配列をファイル保存して 内容をチェックしてみます。
参考は昔の自分の記事
simplestar-tech.hatenablog.com
関数化しておくとこんな感じ
// data書き出し func writeFileData(data []byte, filepath string) (err error) { file, err := os.Create(filepath) if err != nil { return } defer file.Close() file.Write(data) return }
次のテストコードで圧縮前と圧縮後、解凍後で全部問題ないデータになっていることを確認できました。
resData, err := gZipData(cubedata) if err != nil { log.Fatal("gZipData error.", err) } resdata2, err := gUnzipData(resData) if err != nil { log.Fatal("gUnzipData error.", err) } writeFileData(cubedata, "cubedata.bin") writeFileData(resdata2, "resData.gz") writeFileData(resdata2, "unzip.bin")
バイト配列を url にファイルとしてアップロード
ローカルにファイルを作らずにアップロードできるんじゃないかなと思っていますが、どうでしょう?
できました。
実装は次の通り(はじめてのことなのでいろいろ間違えて疎通確認取るまで大変でした!)
// make gz data compressedData, err := gZipData(cubedata) if err != nil { log.Fatal("gZipData error.", err) } body := new(bytes.Buffer) body.Write(compressedData) // create request request, err := http.NewRequest("PUT", requestJson.DataUrl, body) if err != nil { log.Fatal(err) } request.Header.Set("Content-Type", "application/gzip") // send request client := &http.Client{} response, err := client.Do(request) if err != nil { log.Fatal(err) } defer response.Body.Close() // check result result, err := ioutil.ReadAll(response.Body) if err != nil { log.Fatal("ioutil.ReadAll(response.Body)", err) } resultMessage := string(result) if resultMessage != "" { log.Fatal("PlayFab upload error.", resultMessage) }
これで全世界何万人のユーザーが来ても、問題なくファイルを配信する機能が確認できました。
Unity からファイルをダウンロードする方法は Qiita に昔記事を書いていたので
こちら
qiita.com
クライアント実装時に参照しようと思います。
まとめ
半年間調査を続けた大目標が達成されました!
キューブの世界のデータを考え得る限り実装が楽で高速なバイナリキャッシュサーバーに置くことができ
PlayFab ログインユーザーだけが Unity クライアントから CDN 経由で定期的に最新のキャッシュサーバーのデータで更新される世界データを引いてこれること
圧縮したファイルをユーザーに届けられている
ここから Unity クライアントに作業を戻していきます。