YKTのどっちがいいかな?

実世界連動地域貢献型すごろくゲームのアプリ開発状況を書いていきます。

後付け開発

今日はcapaさんとnonstyleさんとnukamisoさん家に遊びに行く予定です。
ですので予約投稿なるものを仕掛けてみました。

なんとなく4時に仕掛けましたが,起きているわけじゃないですよ?

本日の話題

さて前回は皆さんにカードを投稿してもらうためのwebアプリの話をしました。

実はこれ完全に見切り発車で,肝心のwebアプリ→Unityへ持っていく部分が全くの手つかずでした。
できなかったら(お互いに)ただの作り損っていうね。

そんなことでは怒られてしまうので、今週はそこの部分の実装をしていきます。

WebアプリとUnityの連携

例えばこんなカードを作ったとして、Unityで利用できるようにしてみましょう。

あくまで"例えば"ですよ?

Webアプリ側の変更

はてさて何はともあれ、まずは、「カードのデータ頂戴!」という要求に対して「あげまーす。うそでーす。」と応答するwebページを作る必要があります。

@csrf_exempt
def getlist(request):
    if request.method == "POST":
        if request.POST["password"] == "みせられないよ":
            cards = Bcard.objects.all()
            str = ""
            for c in cards:
                dict = {
                    "cardname":c.cardname,
                    "madeby":c.madeby,
                    "target":c.target,
                    "content":c.content,
                    "message":c.message,
                }
                js_str = json.dumps(dict,ensure_ascii=False)
                str += js_str + "/"
            return HttpResponse(str[:-1])
    return HttpResponse("error")

こんな感じで、あるURLにPOSTでアクセスしてきて、かつ、パスワードが一致する通信があったら、全カード情報をスラッシュ区切りのJSON形式で返すような関数を用意しましょう(dict周りでダサいことしてますがそこはご愛嬌)。

Unity側の変更

そしてUnity側からは、

public class wwwtest : MonoBehaviour
{
    private string json_strs;
    private string[] jsons;
    void testbtn()
    {
        StartCoroutine(GetText());
    }
    IEnumerator GetText()
    {
        string url = "http://127.0.0.1:8000/card/みせられないぜ/";
        WWWForm form = new WWWForm();
        form.AddField("password","みせられないよ");
        UnityWebRequest request = UnityWebRequest.Post(url,form);
        yield return request.Send();
        if (!request.isNetworkError && !request.isHttpError)
        {
            json_strs = request.downloadHandler.text;
            jsons = json_strs.Split('/');
            for(int i = 0; i < jsons.Length; i++)
            {
                Bcard.Add(JsonUtility.FromJson<Batsucard>(jsons[i]));
            }
        }
        else
        {
            //エラー処理
        }
}

こんな感じで、該当のURLにパスワードを送って、JSONデータが降ってくるのを待ちます。
ちなみにさも当然かのように書いてますが、POSTだとかJSONだとか実はあんまりよくわかってないです。

完成

そんなこんなで下のデバッグプリントのようにunityでカード情報が見れるようになりました。

意外とあっさりでしたね。皆さんの投稿も無駄にならずに済みそうです。

ちなみにわかる人にはわかると思いますが、今回はローカル環境でテストしていますので、みんなが作ってくれたカードは一切見ていません。ご安心を。

余談がメインになるやつ

さて、上記のコード内の「みせられないぜ」と「みせられないよ」にはそれぞれ小文字・大文字・数字がランダムに並んだ30桁の文字列を入れてあります。
つまり、この2つの文字列を当てることができれば、事前にカードの内容を知ることができちゃいます(太っ腹!)

では、どのくらいの時間があれば文字列を当てることができるか考えてみましょう。
もちろん人間が手入力していくのは到底無理なのでコンピュータの力を借りるとしましょう。

試しに[a-z][A-Z][0-9]の全62種類のなかから4個選んで(重複可)並べることを考えましょう。
このようにしてできる全ての文字列を出力するプログラム(一応速さを意識してc言語)を書いてみると,私の環境では112秒かかりました。

さて、1桁増えるごとに計算時間は62倍になりますので,30桁だと,112×6230-4秒 = 4.48極秒 = 14.2正年かかります。
今回は該当urlを見つけた後にパスワードの探索をすればいいので最大で2倍の28.4正年かかることになります。

ちなみに正は1040の接頭語です。初めて使ったわ。

実際には単なる出力ではなくwebサーバーにアクセスする処理が入るのでもっと時間がかかりそうですね。

もちろんこれは一番最後の候補が正解だったときの話ですので,運が良ければMOMOの開催に間に合うかもしれません


余談ですが,ランダム文字列が7桁だと112秒 × 627-4 = 0.85年で総当たりできるのに対し、 8桁になると52年になって一気に現実的でなくなります。

最近8文字以上で大文字・小文字・数字全部入れろというサイトが多いですが,理にかなっているもんですね。

※全部入れろという制約を逆手にとれば,条件を満たしていないものは探索から除外できます。
大文字なし(368)・小文字なし(368)・数字なし(528)の文字列は,総数(628)の27%を占めるので38年くらいでクラックできそうですね。


余談の余談ですが,総当たり攻撃を行った場合は、不正アクセス禁止法に違反するそうです。

3年以下の懲役又は100万円以下の罰金が科せられますので注意してね。


さらに余談の余談の余談ですが(どんだけ喋るねん),プログラマーを自称している男性諸君,こういう総当たりの処理はforループで実装しますよね?

3桁ぐらいであればforループを3重にネストして書けばいいですが, これが今回みたいに大きな桁数になる場合,または桁数を容易に変更できるよう汎用性をもたせたい場合, あなたならどうやってかきますか?(知っていれば簡単ですが。解答をコメントにどうぞ!)

おわりに

ゲーム内での罰ゲームカード取得時のテキストはこんな具合()になる予定です。

カードを作成する方は参考にしてください。

さて、開催まで残すところ3週間となってきました。

ここからは細かなテキストの調整などを最優先にして、まずはいつでもプレイできる環境に整えたいと思います。

ただ調整に注力するとブログのネタがなくなるんよな・・・

参考文献

dackdive.hateblo.jp

qiita.com

kan-kikuchi.hatenablog.com