makoto_ogaの日記

書ける時は技術系、それ以外は日常系・空想系のブログを書いていきます

ドッカンバトル不具合ソースコード解説

※11/17 9:30 追記
説明が不足しておりましたので、
『コインロッカーの0番目に悟空、1番目にクリリンが入っていて
2番目だけが空いている状態の場合』という文言を追記しました。


こんばんはogaです。

昨日公開された『ドッカンバトル』の不具合ソースコードの解説を私なりにしたいと思います。
なるべくプログラムを知らない方にも分かりやすいように書いていきます。

↓下記が公開された情報になります。

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


公開されたソースコードはある機能のプログラムの一部です。
C++という言語で書かれています。

1行目に書かれている『〜getMasterCardDataByIds〜』というものを関数といいます。
この関数を呼ぶ事によって指定したIDのキャラクターカード情報を取得できるというものです。

まず、処理が書かれている3行目から見ていきます。
3行目〜5行目のソースコードです。

vector<CardDataPtr> results;
results.resize(masterCardIds.size());
size_t exists = 0;

3行目4行目は指定されたキャラクターカードID数分の配列作成し、メモリを確保しています。
配列というのは扉がたくさんある入れ物になります。
駅にあるコインロッカーをイメージしてください。

仮にここでは3個のキャラクターカードIDを指定したと仮定します。
そうするとコインロッカーの個数は3個用意することになります。
このコインロッカーは番号が0番から始まる規則になっているので
0番、1番、2番があることになります。
それがこのソースコードになります。

5行目はキャラクターカードの情報取得数をカウントするために使用します。

次に⓵の赤枠部分です。
1.スマホ内のメモリからデータ読み込み、コインロッカーに0番から順番に入れていきます。
2.コインロッカーに入れた数をカウントアップします。(exists++)

次に⓵の赤枠と⓶の赤枠の間の3行です。
指定されたキャラクターカードID数(3個)とコインロッカーに入れた
キャラクターカード情報の数を比較します。
数が一致したらメモリの状態が最新なので、コインロッカー情報を返却(return)して
関数を終了します。

件数が合わなければスマホ内のメモリが最新でないため、⓶の赤枠へ移行します。
この場合はコインロッカーのどこかに空きがある状態になります。

②の赤枠処理
ストレージからキャラクターカード情報を取得します。
空いているコインロッカーにキャラクターカードデータを
入れていきます。
最後にコインロッカー情報を返却(return)して関数を終了します。

不具合があったのは⓶の赤枠、キャラクターカード情報を取得する順番の部分です。

例えばキャラクターカード情報を下記のように仮定します。

カードID カード情報
0 悟空
1 クリリン
2 ピッコロ

②の赤枠で取得した順番がソートされていない下記のように取れた場合
2:ピッコロ
0:悟空
1:クリリン

コインロッカーの0番目に悟空、1番目にクリリンが入っていて
2番目だけが空いている状態の場合

上から順番にコインロッカーに入れていくので、コインロッカーの2番目に
クリリンが入ってしまい、1番目と2番目がクリリンとなってしまいます。(0番目は悟空)
また、ピッコロがコインロッカーに入らなくなります。
これが表示バグの原因です。

修正内容としては下記のようになるかと思います。

[修正前]

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

[修正後]

string sql = from(SELECT * FROM cache.cards WHERE id IN (%s) ORDER BY id;", join(masterCardIds, ",").c_str());

修正後のソースコードが公開されていないのですが、おそらくこんな形だと思います。
これで昇順ソートされた順番で綺麗にコインロッカーに入れられます。

ただ、これだとソートされていないと不具合になるという危険を抱えたままなので
私がやる場合はIDがソートされていなくても問題無いように作ると思います。
(公開されていない部分の影響範囲は分かりませんが・・)

ソースコードの全文は割愛しますが、3行目のコインロッカーを作っている部分を変更します。

std::map<int, CardDataPtr> results;

これで配列が連想配列になります。
連想配列というのはラベル付きコインロッカーです。
普通のコインロッカーでは順番にキャラクターカード情報を入れていきましたが
このラベル付きロッカーにはラベルに対応したキャラクター情報しか入れられません。

0番目のロッカーにはID:0悟空、1番目のロッカーにはID:1クリリン、2番目のロッカーにはID:2ピッコロ
の情報しか入りません。
なので、取得したデータがソートされていなくても問題ないかと思います。

長くなってしまいましたが、
以上が公開されたソースコードの解説になります。

ガシャのソースコードは不具合が無いことを祈りつつ
詫び石300個でドッカンバトルを楽しんでいきましょう。

では、また。