ROのゲームサーバーの時刻がPCの時刻とずれているので、サーバーの時刻を取得して表示するページを作りました。
何をしているか
PHPでROの公式サーバー(ゲームサーバーではない)にHTTPリクエストを投げてレスポンスヘッダから時刻を取り出して表示しています。
Javascriptじゃないんかい! というツッコミはごもっともです。その理由と経緯は以下になります。
まずは検索
「サーバー 時刻 取得」などで検索すると、どうやらJSのajax通信では返ってきたレスポンスヘッダの中にサーバーが応答した時刻が入っており、それを取得すればOK! ということでした。
なーんだ結構簡単じゃん、jQueryでちょちょいとやればすぐできるなと思っていたのですが…
同一オリジンポリシー(SOP)が道を阻む
結果から言いますとJavascriptでは公式サーバーにアクセスすることができませんでした。
これは「同一オリジンポリシー(Same Origin Policy)」のせいでした。
コンテンツがブラウザに来る源泉(origin)に基づいて整理して、外部からの干渉を防ごうとする。
同一生成元ポリシー – Wikipedia
要するに他所様のサーバーにはJSで好き勝手させないよ! ってことですね。
HTTPリクエストを投げようとしてもエラーになってしまうので、どうにかこれを越える必要がありました。
解決方法は2つ。
まず1つ目はサーバー側でレスポンスヘッダにAccess-Control-Allow-Originを含める。
それができるんならこんな事しとらんわい!! 次!
2つ目はajax-cross-originというjQueryプラグインです。
これをjQueryとともに読み込むことでSOPを回避して別サーバーにアクセスできます。ただし返ってくるデータはJSON固定です。これで万事解決かのように見えたのですが…
AjaxではDATEヘッダを返してくれない
ウキウキしながらgetAllResponseHeaders()で中身を見てみると空っぽでした。
検索してみたところ、こんなことが書いてありました。
DATE
クロスドメインなAjaxリクエスト時に取得できないレスポンスヘッダーについて | mediba Creator × Engineer Blog
Server
ETag
Connection
Accept-Ranges
Keep-Alive
Content-Length
クロスドメインの場合には、上記ヘッダーを取得できていないことがわかります。
これについては解決方法はないようなので、JSで別サーバーの時刻を取得するのは不可能だとわかりました。
PHP の file_get_contents を使う
JSがダメならPHPで、と検索していたらfile_get_contentsを使えばHTTPリクエストが投げられるので使いました。
1 2 3 4 5 |
$context = stream_context_create(array( 'http' => array('ignore_errors' => true) )); $response = file_get_contents('https://ragnarokonline.gungho.jp/', false, $context); $servertime = str_replace('Date: ', '', $http_response_header[4]); |
レスポンスヘッダは $http_response_header に配列として格納されるので、[4]でdateの部分を取り出しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
let changeDate = (dateobj) => { document.getElementById('year').textContent = dateobj.getFullYear(); document.getElementById('month').textContent = dateobj.getMonth()+1; document.getElementById('day').textContent = dateobj.getDate(); document.getElementById('hour').textContent = ('0'+dateobj.getHours()).slice(-2); document.getElementById('minute').textContent = ('0'+dateobj.getMinutes()).slice(-2); document.getElementById('second').textContent = ('0'+dateobj.getSeconds()).slice(-2); dateobj.setSeconds(dateobj.getSeconds()+1); } let datedata = "<?php echo $date->format('Y,m,d,H,i,s');?>"; datedata = datedata.split(','); let date = new Date(...datedata); changeDate(date); setInterval("changeDate(date)", 1000); |
取り出した $servertime をJSに渡して、1秒ずつカウントアップする関数 changeDate() を setInterval で1秒毎に実行しています。
ここでハマったポイントが1つ、
setInterval に関数を渡すときは ” ” で囲わなければならない
囲わないと最初の1回だけ実行されて終わりになってしまいます。
setInterval の説明をしているページでもわりと” “なしで書いてあるところがあってハマってしまいました。
というわけで無事完成。やったね。
ゲームサーバーとHTTPサーバーの時刻が一緒である、というか細い前提のもと作りましたがたぶんちょっとは役に立つと思います。
実際自分のPCの時刻と比べると8秒ぐらいズレがでてますからね。
え、お前の時刻がズレてるだけだって? まあそうかも…