長時間実行しているPHPのグローバルタイマー

私のアプリケーションでは、リアルタイムではない複数のゲームがあります。各ゲームでは、各プレイヤーにスコアが与えられます。ゲームの進行中に、プレーヤーは何らかのアクションを交互に行うことができます。

ただし、カスタマイズ可能な時間内(1時間から1ヶ月まで)に何も行動が取られない場合、両方のプレイヤーはxポイントを失います。

私は、cronジョブによって呼び出されるグローバルな "タイマー"を持っていると思っていました。 1時間ごとに、タイマーは各ゲームの deductPoints()関数を呼び出します。

deductPoints()関数内で、私は次のようにします:

deductPoints()
{
   timeSinceLastDeduction++;
   if(timeSinceLastDeduction >= frequencyOfDeduction)
   {
       deduct();
       timeSinceLastDeduction = 0;
   }
}

このタスクを達成するためのより良い方法はありますか?

3
はい。これはまだ実装されていませんが、私の現在の設計では、状態永続性のためにSQLデータベースを使用することを考えていました。
追加された 著者 Razor Storm,
このデータをある種のリレーショナルデータベースに格納していますか?
追加された 著者 Conspicuous Compiler,
1回のメソッド呼び出しで潜在的に多数のオブジェクトをインスタンス化するのではなく、1回のクエリでポイントを更新する方法を検討することをお勧めします。
追加された 著者 Michael Mior,

2 答え

ターンが変わると、現在のタイムスタンプをデータベースに保存してから、cronを使って特定の間隔でチェックを実行し、必要な時間が経過したらゲーム機能を実行します。

基本的にはあなたの視線に似ていますが、時間値をどこかに保存する必要があります。データベースまたはファイルに保存します。

1
追加された
クール、これについてもっと効率的な方法があれば私は興味があります。このスキームでは、私は1000のゲームを持っている、つまり、1時間に1000人のゲームすべてを1時間に1つだけ設定しても
追加された 著者 Razor Storm,
同様のゲームが1000個ある場合は、cronからスクリプトを1つ実行します。このスクリプトは、注意が必要なゲームをチェックし、機能を処理します。
追加された 著者 budwiser,

より多くのデータを保存したい場合は、このデザインをお勧めします。

代わりに、毎回ポイントを差し引くのではなく(ゲームの「状態」に強く頼る)、プレイヤーが行ったすべてのアクションを保存します。例、このテーブルを取ることができます:

 id | playerid | action   |         stamp
----+----------+----------+------------------------
  1 |        1 | e2 to e4 | 2011-10-27 04:00:00-04
  2 |        1 |          | 2011-10-27 04:30:00-04
  3 |        1 |          | 2011-10-27 06:00:00-04
  4 |        1 |          | 2011-10-27 07:45:00-04
  5 |        1 |          | 2011-10-27 08:00:00-04

e2からe4は途中でチェスの動きであり、共通のオープニングの動きの1つです。あなたのゲームは、私は本当にそれを設計することはできません。 「アクション」はオプションですが、ペナルティを計算するためにアクションが発生したという事実を実際に保存する必要があります。 (もちろん、余分な情報を保存することもできますが、そうするためのスペースがある限り)。

この場合、 "id"はサロゲートキーです。 PostgreSQLでは、DDLは次のようになります。

CREATE TABLE actions(
  id BIGSERIAL PRIMARY KEY,
  playerid int,
  action text,
  stamp timestamptz,
  UNIQUE(playerid, stamp));

タプル(playerid、スタンプ)は「本当の」主キーになります。 "id"はサロゲートキーです。

タイムスタンプの違いを見るために、コードはウィンドウ関数を使って比較的シンプルです。 (PostgreSQLの使用を継続する)

select playerid, time_between, id FROM
        (SELECT playerid,
                stamp - lag(stamp) OVER() as time_between,
                id FROM actions
                ORDER BY stamp)
                as ss
        WHERE playerid = 1
                AND time_between > '1 hour';

サンプル出力。以前のテストデータを使用:

 playerid | time_between | id
----------+--------------+----
        1 | 01:30:00     |  3
        1 | 01:45:00     |  4

あなたはcronジョブをまったく持っていません。より多くのデータを保存しています(アクションを元に戻すのが簡単になります)。プレイヤーは過去の動きを見直すことができます。ペナルティを計算するのが非効率になる場合は、常に高いレベルでスコアをキャッシュすることができます。

ウィンドウ関数はMySQL btwでは利用できません。しかし、PostgreSQL、SQL Server、およびOracleで利用可能です。

編集:スペースが懸念されている場合は、常にこのテーブルを定期的に "ガベージコレクション"することができます。それでも、それはたくさんのゲームになるだろう。各行は〜48バイトを占有します(ヌルの「アクション」と仮定します)。 1000人のプレイヤーがいて、それぞれ40個のアクションで200個のゲームをプレイしている場合は、〜384MBのデータしかありません。

1
追加された
非常にクールな、私は前に窓関数に遭遇していないとは信じられない。私はこれを調べます。
追加された 著者 Razor Storm,
ええ、私はmysqlを使用する必要があるので、私は基本的にアプリケーションコードでこれをやっています。ありがとう!
追加された 著者 Razor Storm,
MySQLやウィンドウ関数を持たない他のデータベースでこれを行う必要がある場合は、特定のゲームからプレーヤーのすべてのアクションを返すのは簡単でしょう。次に、アプリケーションコードでそれを計算します。データ分析を行うアプリケーションコードを恥ずかしく思ってはいけません。そのすべてをSQLで達成できる素敵なトリックですが、そうしなければならない場合にはそれに集中することはありません。
追加された 著者 Dragontamer5788,
PHP - 日本のコミュニティ [ja]
PHP - 日本のコミュニティ [ja]
4 参加者の

このグループではPHPについて話します。 パートナー:kotaeta.com