Clotho の偶感録

個人用メモを世界に垂れ流す。

話題の詫びソースコードを眺めてみた

スマホゲームでのガチャの排出に関するトラブルなんてもう耳タコレベルの話題だとは思うんだけど,バンダイナムコゲームスの『ドラゴンボールZ ドッカンバトル』 で詫びソースコードが出てくる斬新な対応が目を引いたね。
(詫び石は配布済みみたいだよ)

当該ゲームには全く手を出してないんだけど,ソースコードの量がチラッと見てすぐに終わる感じなのでチラッと見て気になったことを記事にしてみた。

f:id:clotho_moirai:20171118103539p:plain

「ドラゴンボールZ ドッカンバトル」一部ガシャの「出現キャラ一覧」及び「出現キャラ提供割合」表示に関する不具合につきまして

というわけで今回はひたすら IT 系。

そもそもの問題

ソースコードをプレスリリースに出すとか

想定読者だれなんだよ,と。

そもそもプレスリリースにソースコードを出してくるってのが異常だよね。
プログラマー晒しかよって思うし *1C++ソースコードなんてゲームのユーザーに見せて何の説明した気になってるんだよと。

公開範囲が足りん

一部だけ晒すとか意味ないから全部 github の公開リポジトリにすればいいんでないかというね。
版権リソースだけ submodule に出して社内管理にしとけばいいっしょ。

上のリンク先,"「出現キャラ一覧」及び「出現キャラ提供割合」の表示に関する不具合の詳細" (以下『詳細プレス』とするよ) では,

ストレージから取得したカードIDリストは順序が保たれておらず

ってあるんだけど,どうせソース出すならストレージに順不同で保存しているロジックも曝さないとねえ。
とくに _cardDataCache.fetch()_cardDataCache.store()

このメソッドに対するユニットテストとカード ID リストの CRUD に関するテストも曝さないとねえ。

片手落ちでしょう。

というわけで

デベロッパーから受けたバグの説明メールをまんま詳細プレスに放り出したんでないかと感じたよ。

バンナムの広報が一番クソ。

詫びソースコード (前掲の記事から引用)

CardDatas CardModel::getMasterCardDatasByIds(
  const std::vector<uint32_t>& masterCardIds
) const
{
  vector<CardDataPtr> results;
  results.resize(masterCardIds.size());
  size_t exists = 0;

  for (int i = 0; i < masterCardIds.size(); i++) {
    auto p = results[i] = _cardDataCache.fetch(masterCardIds[i]);
    if (p != nullptr) {
      exists++;
    }
  }

  if (masterCardIds.size() == exists) {
    return results;
  }

  string sql = form(
    "SELECT * FROM cache.cards where id IN (%s);",
    join(masterCardIds, ",").c_str()
  );

  int i = 0;
  DatabaseManager::getInstance()->query(
    sql,
    [this, &results, &i](sqlite3_stmt* stmt) {
      if (results[i] == nullptr) {
        results[i] = _cardDataCache.store(make_shared<CardData>(stmt));
      }
      i++;
    }
  );

  return results;
}

横スクロール出ると読みにくいから改行入れたよ。

ダメなところ

基本的に全部個人的な意見や思い入れや思い込みだよ。

と保身しておきつつ。

まーコメント無い以外は致命的にダメなところないかなー。

コメントを付けるべし

型や引数やローカル変数が比較的まともな命名規約だから,このレベルならわたしもコメントあまり入れないけど,メソッドコメントは少なくとも欲しいな。

開発環境わからないけど Visual Studio なら XML ドキュメントで,それ以外なら JavaDoc か何かで。

命名規約を詰めるべし

CardDatas 型

Datas はあちこちで突っこまれてたね。

でも,そもそもプログラムで扱うのは全部データなんだから 型名に Data を入れるのは嫌い (わたしは)。

わたしなら CardDatas 型の命名は,

  • Cards

か,他の Card 系の型名との絡みがあって判りにくいなら

  • CardPtrs
  • CardsArray
  • CardsVector
  • CardPtrsArray
  • CardPtrsVector

のどれかかなあ。

複数形にした時点でコレクション型って読み取れるよね。

Vector 付けると std::vector やめて std::list とかに換えた時こまるね。

getMasterCardDatasByIds メソッド

get〜 は汎用的過ぎて良くないよねー。ぶっちゃけ何でも get って付けられるし。

この場合は CardModel::fetchMasterCardDatasByIds() でいいんでないかなあ。

exists 変数名

あと

size_t exists = 0;

exists は,この名前だと boolean 型と思ってしまう (わたしは)。

逆にこの人,「存在する/しないフラグ」 にはどんな変数名付けるんだろう。is_exist とかやっちゃうのかな。

わたしならどうすっかなー。

size_t cardInstancesCountOnCache = 0;

かなー。英語的に正しいかはともかくとして。

typedef したならそれ使うべし

たぶん CardDatas 形は vector<CardDataPtr> の typedef だと思うんだけど,なぜ

vector<CardDataPtr> results;

CardDatas results;

でないのか。

typedef すべし

std::vector<uint32_t>CardIds で typedef してありそう。

名前空間を付けるべし

std:: を付けるのか付けないのか,どっちでもいいから統一しなよ。

わたしは

using namespace std;

を書かない派なので std::vector とか std::make_shared と書くね。

たいした話じゃない
  vector<CardDataPtr> results;
  results.resize(masterCardIds.size());

って results 用にメモリを確保してると思うんだけど,
わたしなら

  CardDatas results(masterCardIds.size());

にするかなー。

いいところ

  • メソッドが短い
  • const 付けてる

気になること

バグの原因は _cardDataCache.store() 見ないとなんとも言えないよねー。

vector 舐めて nullptr 見つけて上書きするってのが期待された実装で,実際は push しちゃってるとか?

おわり。

まーソースコード自体はそんなヒドいコードでもなかったね。
でもこのソースだけじゃ原因判らないし,そもそもこの詳細プレスはソースコード無くても成り立つし。

というわけでバンナム何したいん? て印象。

ところで,『ドラゴンボールZ ドッカンバトル』,ニュース配信社によっては "Z" が抜けてるのが気になるんだよね。
詫び原稿マダー?

www.itmedia.co.jp

www.oricon.co.jp

*1:ソースコードをバージョン管理してるなら,当該行を誰が編集してきたか社内ではすぐ判る