スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

OBSEのイベントハンドラに関して(その2)

(その1)の続きです。
 
2つめは(個人的には)ちょっと致命的だなぁと思っている内容なのですが…
「ゲームの途中でセーブデータをロードする際、セーブデータを読み込んでいる途中でまだロードが完了していない状態にもかかわらず、イベントハンドラが呼び出されてしまう」

サンプルMODをアップロードさせて頂きました。
http://www.4shared.com/file/2oiNJcBA/EventHandlerTest_2.html

MODのスクリプト部分を以下にコピペします。



scn aaaOnMagicEffectHitEventHandler

ref target
int magicEffectCode

Begin Function { target magicEffectCode }

 ;MODの設計としてflagを1にしてからイベントハンドラを登録している
 ;さらにflagの値はセーブデータに保存されているので
 ;ロード後でもこのイベントハンドラがflag==0の状態で呼ばれることは絶対にあり得ない

 if aaaOnMagicEffectHitQuest.flag == 0
  ;エラーをコンソールに出力
  printC "aaaOnMagicEffectHitEventHandler: ERROR!! (flag==0)"
 endif

End



scn aaaOnMagicEffectHitQuestScript

short flag ;フラグ

Begin GameMode

 ;イベントハンドラ登録の前に必ずフラグを立てる
 set flag to 1
 Message "flagは%.0fです。このflagはセーブデータに保存されます。" flag

 ;イベントハンドラを登録する
 if 1 == SetEventHandler "OnMagicEffectHit" aaaOnMagicEffectHitEventHandler
  MessageBoxEx "OnMagicEffectHitイベントハンドラの登録に成功しました。"
 endif

End



どのような処理を行うMODかというと
・5秒ごとに呼ばれるクエストスクリプト内で以下の処理を行い続ける
 クエスト変数flag(セーブデータに保存される)を1に設定する
 クエスト変数flagの内容を画面左上メッセージに表示して知らせる
 イベントハンドラを登録して成功したらメッセージボックスで知らせる
これを繰り返しているだけのMODです。

今回の実験を行うに当たり
・flagが1の状態になっているセーブデータを「遠くのセル」で2つ作る
を行う必要があります。
さらにセーブを行うセルに「魔法効果を受けているアクターがいる」ことが必須となるので、できれば「From2ch Lives」などを導入してそのアクターが街にいる状態だとベストです。
(VanillaのDarkElfの氷耐性などでもOKだと思います)

まずEventHandlerTest_2.espをアクティベートしてオブリを起動し、新規開始でもロードでも構いませんのでゲームを開始させてください。
自動的に「OnMagicEffectHitイベントハンドラの登録に成功しました。」というメッセージボックスが出てくるはずです。
次に「遠くのセルで2つのセーブデータを作る」必要があるのでわかりやすく「コロルの街」へ移動してください。

「コロルの街」に着いたら画面の左上に「flagは1です。このflagはセーブデータに保存されます。」と表示されていることを確認して、セーブを行ってください。
このセーブデータを「セーブデータ:コロル」とします。

今度はわかりやすくとても遠い「レーヤウィンの街」へ移動してください。
「レーヤウィンの街」に着いたら画面の左上に「flagは1です。このflagはセーブデータに保存されます。」と表示されていることを確認して、セーブを行ってください。
このセーブデータを「セーブデータ:レーヤウィン」とします。

「クエスト変数flagが1の状態で、コロルで保存したセーブデータ」と
「クエスト変数flagが1の状態で、レーヤウィンで保存したセーブデータ」が出来ました。
しつこいようですがクエスト変数の内容はセーブデータに保存されるので、このflagは絶対に1であり続けます。

では今現在レーヤウィンの街にいると思いますので、「セーブデータ:コロル」をロードしてください。
ロードが完了したらすぐに「コンソールを開いて」ください。
(すぐに開かないと別MODの初期化完了メッセージなどが出てきてログが流れて消えてしまう場合があるので)

コンソールを開いて「PageUp」キーを押すとバックログを辿れます。
そこに以下のようなメッセージが出力されているはずです。

aaaOnMagicEffectHitEventHandler: ERROR!! (flag==0)
aaaOnMagicEffectHitEventHandler: ERROR!! (flag==0)
aaaOnMagicEffectHitEventHandler: ERROR!! (flag==0)
aaaOnMagicEffectHitEventHandler: ERROR!! (flag==0)

クエスト変数flagが1の状態のセーブデータをロードしたはずなのに、なぜかflagが0の状態でイベントハンドラが呼び出されています。
もしロードの前に呼び出されたとしてもflagは常に1となっていますので、根本的に「誰がflagを0にしたのか?いつOnMagicEffectHitイベントハンドラが呼びだされたのか?」が謎となります。

では今現在コロルに居るはずですので「セーブデータ:レーヤウィン」をロードし、すぐにコンソールを開いて確認してみてください。

aaaOnMagicEffectHitEventHandler: ERROR!! (flag==0)
aaaOnMagicEffectHitEventHandler: ERROR!! (flag==0)
aaaOnMagicEffectHitEventHandler: ERROR!! (flag==0)
aaaOnMagicEffectHitEventHandler: ERROR!! (flag==0)

再びこのようなログが残っているはずです。

これが「ゲームの途中でセーブデータをロードする際、セーブデータを読み込んでいる途中でまだロードが完了していない状態にもかかわらず、イベントハンドラが呼び出されてしまう現象」です。

全てのイベントで発生するわけではないようですが、少なくとも「OnMagicEffectHit」イベントでは確実にこの現象が発生することを確認済みです。

ConScribeのログを確認してみると…

aaaOnMagicEffectHitEventHandler: ERROR!! (flag==0)
aaaOnMagicEffectHitEventHandler: ERROR!! (flag==0)
aaaOnMagicEffectHitEventHandler: ERROR!! (flag==0)
aaaOnMagicEffectHitEventHandler: ERROR!! (flag==0)
===============================================
Game Instance : 2 | Time : 2011/02/16 6:22:18
===============================================

こんな感じになっており、イベントハンドラが呼び出されたのは「ロードが完了して新しいインスタンスが開始されるよりも」であることが確認できます。

以上の情報から推測してみると、あくまで「個人が勝手に妄想レベルで想像しただけ」の推測ですが…

・ゲームの途中でセーブデータをロードすると…
・まず全データの初期化が行われる
 (恐らく全てのクエスト変数が0で初期化されている?)
・セーブデータの情報を参照して「アクターの配置」が行われる
・セーブデータの情報を参照して「アクターにかかっていた魔法効果」を復元する
 (=アクターに魔法効果をかける)
魔法効果がかけられたのでOnMagicEffectHitイベントが発生してイベントハンドラが呼び出されてしまう
 ※この時点ではまだクエスト変数の復元が行われていないと推測します
・次にセーブデータの情報を参照して全てのクエスト変数を復元する
・ロードが完了したので新しいインスタンスとしてゲームが開始される

こんな感じになっているんじゃないかなぁと思っています。

もし本当に「ロードの初期段階で全てのクエスト変数が0で初期化されている」のであれば、今回のサンプルMODのようにflag的なものを作っておき、それが0なら「不正な呼び出し(セーブデータのロード途中で呼び出された)」と判定すれば、もしかしたら不具合を回避することができるかもしれません。

しかし「本当に全てのクエスト変数が0で初期化されている」かは謎ですので、その辺を推測で断定して「対処は完了しました!」と言ってまた不具合持ちMODをリリースしてしまうのは恐いので「IgnoreFriendlyFireDamage」は公開中止にしました。

この問題は「セーブデータのロード途中にイベントハンドラが呼び出されてしまう」ことが直接の原因ではないかと推測していますが、そもそも「一度登録されたイベントハンドラが別のセーブデータをロードしても残って動き続けている」のが根本的な原因ではないかというのが率直な感想です。
オブリを起動して最初にセーブデータをロードする際にはイベントハンドラが残存していませんので正常に動作します。

OBSEシステムの方で「セーブデータのロードを開始する際、まずは登録されている全てのイベントハンドラを解除する」という仕組みを備えてもらえれば、ロードの途中でイベントハンドラが呼ばれてしまうこと自体が起きなくなると思うのですが…

私のEnglishPowerはVeryLowなので技術的な内容を英文に訳してOBSE開発チームに報告することはできません(´・ω・`)
なのであくまで「イベントハンドラを用いたMODを作っている日本語を読める方へのアドバイス」となっています…

次回は「この動作仕様に対する完璧な対策を取ることはできないのか?」を模索したいと思います。

(その3へ続く)
スポンサーサイト

コメント

非公開コメント

No title

問題があるのはともかくとして、イベントハンドラ用のイベントとして
LoadGame/SaveGame/OnNewGame 等々があることを考えると
「一度登録されたイベントハンドラが別のセーブデータをロードしても
残って動き続けている」は仕様という気がします。
プロフィール

r_basilico

バジリコ風味 (r_basilico)
Twitter: r_basilico
Steam: r_basilico
艦これ: 嫁艦は祥鳳

リンク
最新記事
最新コメント
月別アーカイブ
カテゴリ
検索フォーム
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。