Google

2012年9月13日木曜日

Unityでコインゲー開発:保存その2

余計な記事で間があいてしまいましたが。
保存とセキュリティ話の続きです。

前回のUnity記事では時間フラグを書きました。
あれは基本のひとつなので、当然防げないものは幾らでもあります。
(時間を細かく履歴で見る等の応用を掛ければさらに効果的ではありますが)
そもそも時間基準で不正監視という仕組みは、以下を前提としているものです。

 「スマホの時計はその国の標準時に合わせて使うもの」

不正も含めスマホの時計が狂うと、中に入ってるスケジュールとかアラームとか他のアプリに多大な影響を出します。
そのため、基本的に時計をほぼ合わせた状態で使うであろう、、、という暗黙の了解的なものを織り込んでしまっている訳です。
日時履歴を細かく取ったり、サーバ通信を強制にすることで回避できなくもないですが。

じゃあ既に使わなくなったスマホやSIMを入れずに使うゲーム専用機での対策はどうするか?という話はよく出るのですが、結局いつもこうなります。

 「8割のユーザが不便になりすぎないようにする」

、、、ま、話が進まないのもアレなんで、サーバと同期した際の話を少々。

まずアプリ内課金を予定していたので自前サーバはありました。
そのサーバを使って何をするかですが、ざっくり要求はこう。

 ・共通のフォーマットで低コスト処理
 ・初回プレイ時に内部的に認証
 ・不正が発見されたら強制的に初期状態へ

なんていうか、8割のユーザがこれで問題なく遊べるのか?は疑問ですが、まあこういう感じだった訳です。
iOSでアプリ内課金しているので、サーバ連携のフォーマットは自ずと決まりました。
当然JSONです。
UnityでJSONを扱う場合、処理を全ていれこんだクラスが既に存在します。

 JsonObject
 http://wiki.unity3d.com/index.php?title=JSONObject

詳細については上記を御覧ください。
1クラスが入れ子になって階層を表現する仕組みなのでフットプリントは大きくなる傾向にありますが、簡単に生成、パーズが実現できます。

生成時のサンプルはこんな感じ。(上記サイト抜粋)


JSONObject j = new JSONObject(JSONObject.Type.OBJECT);
j.AddField("field1", 0.5);
j.AddField("field2", "sampletext");
JSONObject arr = new JSONObject(JSONObject.Type.ARRAY);
j.AddField("field3", arr);
arr.Add(1);
arr.Add(2);
arr.Add(3);
string encodedString = j.print();
インスタンス生成し、フィールドを入れるだけ。
入れ子になるならそれを繰り返し、子供にするだけです。
パーズも簡単。
string encodedString = "{\"field1\":0.5,\"field2\":\"sampletext\",\"field3\":[1,2,3]}";
JSONObject j = new JSONObject(encodedString);
accessData(j);
//access data (and print it)
void accessData(JSONObject obj){
 switch(obj.type){
  case JSONObject.Type.OBJECT:
   for(int i = 0; i < obj.list.Count; i++){
    string key = (string)obj.keys[i];
    JSONObject j = (JSONObject)obj.list[i];
    Debug.Log(key);
    accessData(j);
   }
   break;
  case JSONObject.Type.ARRAY:
   foreach(JSONObject j in obj.list){
    accessData(j);
   }
   break;
  case JSONObject.Type.STRING:
   Debug.Log(obj.str);
   break;
  case JSONObject.Type.NUMBER:
   Debug.Log(obj.n);
   break;
  case JSONObject.Type.BOOL:
   Debug.Log(obj.b);
   break;
  case JSONObject.Type.NULL:
   Debug.Log("NULL");
   break;
 
 }
}
こちらは先ほどよりは複雑ですが概念としては簡単。
もらったJSONデータをインスタンスに入れると自動的にパーズします。
入れ子があればその多段階層ができるので、それらを再帰的に処理しながら、プロパティの種類を見て取り出していくだけです。
そもそも貰うフォーマットもある程度決め打ちであるはずなので、その階層にそって存在するであろうデータを想定するだけです。
性能も悪くないですが、何度かバージョンアップしていますので古いVerを使わないようにご注意を。
続いてユーザ認証ですが、この為だけにユーザに負担をかけちゃいけません。
今までは自動認証に使えた仕組みがUDID。
これはデバイス識別IDなのですが、AppleはiOS5からもう使うなという指示を出してきました。
代わりに何を使うかといったらUUIDという、どうしようもない物。
要は被らないようにおそるおそる出した乱数なので保証はありません。
困った末に私が取った対策はこれ。
 ・UUIDと一緒に長い乱数を生成
 ・それらからハッシュキーを生成しセーブデータに追加
 ・この2つをもって認証IDとして扱う
 ・ユーザが端末からセーブデータを消したらまた新規別垢扱い
、、、、対策になってないかも知れませんね、はい。
しかしログインを明示的にさせないという大前提を崩さないためにはこうするしかありませんでした。
キモとしてはUUIDと乱数のハッシュで、まかり間違って全iOSユーザがダウンしてくれる超大ヒットアプリwになった場合にIDが被るという事を防ぐ、という点です。
UUIDの長さを見ればまず被らないとは思うんですがw、なんか心配すぎてこうなってしまいました。
続きます。

0 件のコメント:

コメントを投稿