ロバメモ - 素人のUnity覚書と奮闘記

素人のUnity覚書と奮闘記

現在日時を取得するときの不正対策

※2019/06/30 コード修正

やりたいこと

端末から日時を取得する場合、端末の時刻を操作されると狂ってしまう。ということで、端末以外から日時を取得したい。 その方法を叶えてくれるのが NICT(日本標準時(JST)グループ)様
なんでも、json形式で日時を配信してくれているらしいです。個人利用も無償でOKらしいですが、制限があるそうです。

Q&Aより
1時間平均で20回(1日の合計が480回)を超えないようにしてください。 それ以上が必要な場合は事前にご連絡ください。

参考サイト様

blog.anzfactory.xyz
概ね、杏z学習帳様の方法を真似させていただいてるのですが、自分用にカスタマイズしたものをメモ。

コード

(1)NTP(https://ntp-a1.nict.go.jp/cgi-bin/json)から時刻を取得 Request()
(2)PCとの時刻を比較して不正がないかをチェック CheckPCTime()
   修正:差を求めて誤差5秒未満ならOKとした。
(3)(1)(2)をStart時に行う
(4)実行メソッドはGetDateTime();
   不正がなければ、PCから現在日時を取得し、不正があればNTPから取得する。
(5)Start時のチェックを何度も行わないようにシングルトンにする。
   (NTPへのアクセス数を減らすため)
   新規でゲームオブジェクトを作成してこのクラスをアタッチしておくこと。

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

public class TimeStamp : MonoBehaviour
{

    static readonly string NTP_URL = "https://ntp-a1.nict.go.jp/cgi-bin/json";
    static public TimeStamp instance;

    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
            DontDestroyOnLoad(this.gameObject);
        }
        else
        {
            Destroy(this.gameObject);
        }
    }

    void Start()
    {
        StartCoroutine(CheckPCTime());
    }

    IEnumerator Request(Action<DateTime> callback)
    {
        var request = UnityWebRequest.Get(NTP_URL);

        yield return request.SendWebRequest();

        if (!request.isNetworkError)
        {
            var json = JsonUtility.FromJson<NTPDate>(request.downloadHandler.text);
            var startDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            var unixDate = startDate.AddSeconds(json.st);
            callback(unixDate.ToLocalTime());
            Debug.Log(unixDate.ToLocalTime());
        }
    }

    public class NTPDate
    {
        public string id;
        public double it;
        public double st;
        public int leap;
        public long next;
        public int step;
    }

    bool isMatch;
    IEnumerator CheckPCTime()
    {

        var ntp = new DateTime();
        yield return StartCoroutine(Request(r => ntp = r));

        var pc = DateTime.Now.ToLocalTime();
        if ((ntp - pc).TotalSeconds < 5)
        {
            isMatch = true;
        }
        else
        {
            isMatch = false;
        }

        Debug.Log(pc + " : " + ntp + "=" + isMatch);
    }

    public DateTime GetDateTime()
    {
        var value = DateTime.Now.ToLocalTime();
        if (!isMatch)
        {
            StartCoroutine(Request(r => value = r));
        }

        return value;
    }
}

呼び出し方法

TimeStamp.instance.GetDateTime();

以上。