華和梨と栞ロードとゴーストキャッシュ

アリス「おかしいわ。おかしいわ。何だかとってもおかしいわ。」
ボブ「HI! どうしたんだい、海老名グリーンみたいなことを口走って!」
アリス「誰が海老名パーキングエリアよ。
 それより、ゴーストを作ってるんだけど、ゴーストを切り替えるたびにセーブデータがぽこぽこ増殖していくのよ。
 いったい、何がおこってるの?」
ボブ「HAHAHA! そいつはゴーストキャッシュという名のSSPの仕掛けた恐るべきトラップなのさ。」
アリス「どーにかする方法はないわけ?」
ボブ「HAHAHA! まぁ、落ち着けよアリス。
 この問題を理解するにはゴーストキャッシュの挙動と栞ロード/アンロードのタイミングの理解が必要なのさ!」

目次

栞ロード/アンロードとゴーストキャッシュ

通常、ゴーストが起動/終了するとき、栞ロードと栞アンロードが発生します。
ところが、SSPのゴーストキャッシュが有効である場合、ゴーストキャッシュからゴーストが起動したり、ゴーストキャッシュに入ったりする場合には栞ロードや栞アンロードが発生しません。
これが華和梨的には由々しき問題なのです。

栞ロード時とゴーストキャッシュからの起動時での挙動の差

栞ロードが発生したとき、華和梨は「エントリの初期化」と「スクリプト記述ゾーンのKISの実行」を実施します。
ところが、ゴーストキャッシュからの起動ではこれらの処理が実施されず、「各エントリの前回終了時の状態の復元」が実施されます。
この挙動の差が問題なのです。

どういうこと?

ちょっとテスト

kawarirc.kis に以下のようにだけ書いてゴーストを起動します。

ほげ: 0
System.Callback.OnGET: $(.entry ev.${System.Request.ID})
ev.OnBoot: $(inc ほげ)\1\s[10]\0\s[0]${ほげ}\e
ev.OnClose        : \-
ev.OnGhostChanging: \e

まずはゴーストキャッシュを無効にして、このゴーストを起動します。
普通にゴーストが起動すれば起動時に「1」と喋ります。起動時に「ほげ」エントリの内容を話していますが、この「ほげ」エントリの初期値は「0」でOnBoot?イベント受信時に1増やしているので。
もちろん、このゴーストを他のゴーストに切り替えて戻したり、他のゴーストから呼び出してみても起動時に「1」と話します。

ところがゴーストキャッシュを有効にしてこのゴーストを起動してみると……。

最初の起動時こそ「1」と話しますが、他のゴーストに切り替えてすぐにもどしてみると「2」と喋り、もう一度やってみると「3」になりと、起動すればするほど喋る値、つまり、「ほげ」エントリの値が増えていきます。

どうしてこうなった?(AA略)

ゴーストキャッシュからゴーストが起動される時、「各エントリの前回終了時の状態の復元」が実施されるので、「ほげ」エントリは「0」で初期化するのではなく、前回終了時の「1」という状態を復元されてしまっているのです。
そのため、OnBoot?受信の契機で「1」である「ほげ」エントリは$(inc ほげ)により更に1増えて「2」となるのです。

倍増するエントリ

次はこんな感じで。

System.Callback.OnGET: $(.entry ev.${System.Request.ID})
ev.OnBoot: $(
  load sav.txt;
  inc 起動回数;
)\1\s[10]\0\s[0]起動回数:$(join 起動回数 ",")\e
ev.OnClose        : $(save sav.txt 起動回数)\-
ev.OnGhostChanging: $(save sav.txt 起動回数)\e

まずはゴーストキャッシュを無効にして、このゴーストを起動します。
ゴーストが起動すれば起動時に「1」と喋り、起動するごとに「2」、「3」と増えていきます。
ゴースト終了時(OnClose?OnGhostChanging?)の契機で「起動回数」エントリをセーブし、ゴースト起動時(OnBoot?)でそれをロードして1増やしているのですから当然ですね。

ところがゴーストキャッシュを有効にすると……。
ゴーストを切り替えたりするごとに、起動時のトーク、つまり、「起動回数」エントリの中身が「1」→「2/1」→「3/1/2/1」とカオスなことに。

どうしてこうなった?(AA略)

ゴーストキャッシュからゴーストが起動される時、「各エントリの前回終了時の状態の復元」が実施されるのはさっき述べた通り。
そのため、OnBoot?イベントを受信したときには既に「起動回数」エントリには「1」という値が設定されています。
ところが、OnBoot?イベントでさらにセーブデータのロードを実行することに「ほげ」エントリに「1」という単語がもうひとつ追加されて、「1」が「1,1」という2つの単語を持った状態になってしまうのです。
で、OnBoot?イベントでの$(inc 起動回数)により、コレが「2,1」に。

もちろん、この状態でゴーストを終了すればセーブデータには「2,1」という2つの単語を持った状態でセーブされ、再びゴーストキャッシュから起動されたときに「起動回数」は「2,1」の状態が復元された上にOnBoot?イベントでさらにセーブデータの内容を追加され「2,1,2,1」となり、$(inc 起動回数)されるので「3,1,2,1」と倍増していくのです。

ゴーストキャッシュ対策

セーブデータのロードはスクリプト記述ゾーンで

一番簡単な対策はスクリプト記述ゾーンにセーブデータのロード処理を書いてしまうこと。
ゴーストキャッシュから起動するときにはエントリの内容は前回終了時の状態が復元されるのだから有難くそれを使わさせてもらうことにして、通常の栞ロードが発生した時だけセーブデータから値を読み込んで使いましょう。

これはこんな感じ。

=kis
load sav.txt;
=end
System.Callback.OnGET: $(.entry ev.${System.Request.ID})
ev.OnBoot: $(inc 起動回数;)\1\s[10]\0\s[0]起動回数:$(join 起動回数 ",")\e
ev.OnClose: $(save sav.txt 起動回数)\-
ev.OnGhostChanging: $(save sav.txt 起動回数)\e

エントリの状態の初期化はOnCacheRestore?にも

セーブデータが増えるのはこれでいいとして、前の方に出てきた「ほげ」エントリのようにセーブ対象ではないけれど起動時に初期化したいエントリはどうしましょう。
実は、ゴーストキャッシュからゴーストが起動されるとき、SSPはOnCacheRestore?というNOTIFYイベントを投げてきています。この中で値の初期化を実施しましょう。

こんな感じ。

ほげ: 0
System.Callback.OnGET: $(.entry ev.${System.Request.ID})
System.Callback.OnNOTIFY: $(.entry ev.${System.Request.ID})
ev.OnBoot: $(inc ほげ)\1\s[10]\0\s[0]${ほげ}\e
ev.OnClose: \-
ev.OnGhostChanging: \e
ev.OnCacheRestore: $(setstr ほげ 0)

どうせなら処理を共通化。

=kis
function 初期化処理 $(.setstr ほげ 0);
初期化処理;
=end
System.Callback.OnGET, System.Callback.OnNOTIFY: $(get ev.${System.Request.ID})
ev.OnBoot: $(inc ほげ)\1\s[10]\0\s[0]${ほげ}\e
ev.OnClose: \-
ev.OnGhostChanging: \e
ev.OnCacheRestore: $(初期化処理)

これでめでたしめでたし……と思いきや。

続きはそずべねぐ/セーブとロードのおはなしで。

コメント



トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2009-12-27 (日) 10:02:47