今回の内容は、あくまで試験的なものとなります。実際にそのままの仕組みで導入するかは未定です。
※セーブ機能の実装自体は予定しております
また、今回はセーブするための仕組みの部分にのみにフォーカスを当てています。非公開ディレクトリやパーミッション等による最低限の対応は施していますが、試験的にアップした際のセキュリティの問題上、予めご了承くださいませ。
セーブ機能の試験的実装
β版の課題となっているセーブ機能の実装についてですが、セーブ段階でのセッションデータを保存、それを再度復元することができれば、理論上セーブは成立することになります。
セッションデータをどう保存するかはセキュリティ的な問題なども色々考慮しながらになりますが、まずは再スタートが可能かどうかを確認してみましょう。
データの保存
まずは「保存」からです。ポケモンでは記録することを「レポート」と言います。必要なデータを個別管理すると復元するのが手間になるので、今回はセッションファイルに記述されている内容(文字列)をそのままファイルに書き込むという形式を取ります。
しかし、セッションに記述されている内容を、直接ファイルから参照してくるのはあまりきれいな記述だとは言えません。なので、今回は「現在のセッションを、セッションと同じ形式でエンコードする」ための関数を用います。
session_encode(PHPマニュアル)
この関数を呼び出すことで、現在のセッションをセッションファイルに保存しているのと同じ状態にエンコードした文字列を返り値としてくれます。
こちらをPHPのファイル操作でお馴染みのfopen、fwrite、fcloseを使って記録していきます。
// 書き込みモードでファイルを開く
$file = fopen(保存するファイル, 'w');
// ファイルに書き込む
fwrite($file, session_encode());
// ファイルを閉じる
fclose($file);
ユーザーID
では、どのファイルにセーブデータを保存するか考えていきましょう。同じファイルに複数のセッションデータを保存することはあまりに危険です。上書きが発生してしまうとそれだけで1名分どころか、全てのユーザーのデータが失われてしまうことになります。
なので、プレイヤーにユーザーIDを割り振ることで、それぞれのセーブデータファイルを用意、そこに書き込んでいくことにしましょう。
プレイヤーIDは作成時に重複回避しながら割り振りました。この文字列をわざわざ控えて置くのも面倒なので、クリックするとクリップボードへコピーできるようにjsを記述しました。
内容は以下の通りです。
/**
* クリップボードへのコピー
* @function on:click
*/
var copyToClipboard = function() {
$('[data-clipboard="true"]').on('click', function(){
var text;
var target = $(this).data('target');
// コピーする文章の取得(もし対象が見つからなければ自身を選択)
if(target){
text = $(target).text();
}else{
text = $(this).text();
}
// テキストエリアに対象テキストをセット
var textarea = $('<textarea>' + text + '</textarea>');
$(this).append(textarea);
// テキストエリアを選択してコピー
textarea.select();
document.execCommand('copy');
// 不要になったテキストエリアを削除
textarea.remove();
toastr.success('プレイヤーIDをコピーしました');
});
}
指定の要素がクリックされたら、ターゲットを取得、なければ自身を対象としてテキストを変数へ格納。その変数をテキストエリアに突っ込んでDOM生成、selectで文字列を選択状態にしてからexecCommandのcopyを使ってクリップボードへ貼り付け、不要になったテキストエリアは速攻で削除という流れです。
最後には、コピー完了メッセージとしてトーストを用いています。
ざっくりとした説明ですが、上記コピペで読み込み時に起動、コピーボタンとしたい要素にdata-clipboad=”true”を付与すればコピー可能なので、ぜひ参考にしてください。
重複回避したユーザーIDの割当てができれば、後はユーザーID名のファイルにセーブデータを書き込むだけです。初めてのレポートではセーブデータ自体が存在しないため、ファイルが存在しなければ作成するという分岐を追加します。
// ファイルの存在確認(なければ作成)
if(!file_exists(ユーザーID)){
touch(ユーザーID);
}
// 書き込みモードでファイルを開く
$file = fopen(ユーザーID, 'w');
// ファイルに書き込む
fwrite($file, session_encode());
// ファイルを閉じる
fclose($file);
これでレポート時のデータを保存するための仕組みは完了です。
レポートの読み込み
次に、レポートの読み込みについてです。ポケモンでは起動時の「つづきから始める」が該当します。ユーザーIDを使って保存しているため、そのIDを使うことでレポートの存在有無を確認します。
セッションを再生成
対象のレポートからfile関数を使ってレポートデータを読み込み、それをセッションへ再セットします。file関数では行ごとに配列として取得するため、添字0番の行がセッションデータとなっています。
エンコードされた文字列をセッションに再セットする際にはsession_decodeを使用します。
session_decode(PHPマニュアル)
// レポートの存在確認
if(!file_exists(ユーザーID)){
throw new Exception;
}
// セーブデータを現在のセッションへ読み込み
session_decode(
file(ユーザーID)[0]
);
// セッションを保存
session_write_close();
session_decodeをすることでセッションへの書き込みは出来ているのですが、そこからリダイレクトすると、最初のキー以外のセッションが引き継がれないという現象に陥ったので、こちらもリダイレクト時の処理と同じく、session_write_closeを使って保存処理を加えています。
では、実際にセーブデータが引き継ぎされるかどうかを確認してみましょう。
問題なくセーブデータは引き継ぎされましたね。
これで試験的なセーブ機能については実装ができました。
課題
ここからはセーブ機能についての課題です。試験的に記録できるようにはなりましたが、いくつか問題点が残っていますので、挙げておきます。
再発行
ユーザーIDを覚えていればいつでもセーブデータを呼び出すことができますが、もし記録を忘れてしまったり、紛失してしまった場合はどうでしょうか?
残念ながら、セーブはされていても読み込むことはできなくなってしまいます。
これを防ぐためにも、多くのサービスで標準搭載されているのが「再発行」です。メールアドレスとパスワード、どちらかを忘れてしまった際に、他の登録情報を使って再登録したり、メールへ通知するシステムです。
これを実現するためには、ユーザーID以外に最低でもメールアドレスが必要になります。
パスワード認証
再発行のためにメールアドレスを用意するとしても、1つ問題が残ります。それは不正アクセスによる個人情報の流出です。
現段階ではPHPポケモンには現状個人情報は含まれておらず、最悪他人のセーブデータを呼び出せたとしてもさほど問題はありません。ですが、再発行のためにメールアドレスのような個人情報を扱うとなれば、流石にユーザーIDだけでの保管は危険度が跳ね上がります。そのためにも、ユーザーIDとパスワードを使った認証を導入する必要は最低限必要になります。
もし再発行システムを導入するのであれば、レポート自体はおとなしくDBを使って管理するほうが良さそうな気がしてきました。
まとめ
いかがだったでしょうか。
今回のPHPポケモンでは「試験的なセーブ機能の実装方法」についてご紹介しました。
色々と問題が残る中、日々奮闘中です。
プログラミング学習に取り組んでいる方や、ゲーム開発に興味を持たれている方は、ぜひ参考にしてみてくださいね。