appendix:エラーのお話

オダメで出力されるエラーについてのお話。
エラーを見れるようになる必要はないですがエラーに含まれている情報でどこが重要なのかを知れば、他者に聞くときに渡すべき情報がどれかのの判断の一助になるかもしれません。

まず大きく2種類のパターンがある。。

  • 個々の処理がエラーを捕まえて自分でエラーメッセージを出しているもの。
  • 個々のエラー処理が例外を捕まえておらず、システム1)が出しているもの。

 前者の「個々の処理がエラーを捕まえて自分でエラーメッセージを出している」のほうは、どういった動作をするかはその「個々の処理」次第であるため、有意義な情報を出してくれる場合もあれば、まったくなにも出さなかったり、誤った情報を出す場合などもありえます。

 後者のシステムが出すものは、決まった内容を出してきます。

役に立つ情報を出している場合もあれば、内容は出している処理次第なのでそもそも何が出しているのかわからないものも。
(おいらのプラグインはUnityで出力する際は2)プラグイン名とバージョンを付けるようにしている。)

正常に動作出来ない状況になった際に例外(Exception)が発生します。
これを捕まえ(catch)ずに放置すると見慣れたメッセージが出力されます。
こちらは決まったパターンで出力されます。

最初の行
TypeLoadException: A type load exception has occurred.
「TypeLoadException」は例外の種類、続いてその説明「A type load exception has occurred.」。
次の「Stack trace:」の後にスタックトレースが続きます。

エラー(例外)の種類

何がまずかったのかを表しているためヒントになることが多いですが、よくある例外だと「またいつものか・・・」ぐらいでしかない場合も。

よくある例外
  • NullReferenceException

いわゆる「ぬるぽ」本来あるべきもの、本来取得できるものなどがない状態でそれを操作した場合など。
nullが専門用語なので一般の人にはnullを参照した?どういう意味?ってなる。
この「ぬるぽ」は「ぬるぽ」した処理が問題ではなく3)あるべきものがない、取得できるはずのものが取得できてないないことが問題。
直前にファイルが読めてないとか出ている場合はおそらくそれが原因。
よくあるのはDLCの色変えMod等でDLCを持ってなくてModelが無いとか。

  • FileNotFoundException

名前通りでファイルが見つからない場合に発生する。
ただしデータファイルに限らずDLLなどがない場合もこれになる場合があるので要注意。
DLLの場合はAがBを参照していて、BがCを参照している用な状態で、Cが無いときもAの処理で出たりする。

  • TypeLoadException

本来あるはずのType(型)4)、がない場合。

  • MissingFieldException

これもあるはずのField(フィールド)がない場合。5)

非常によくあるのが“TBody.goSlot”がないといわれるもの、COM3D2とCOM3D2.5でこの部分の扱いが変わっており、COM3D2用のプラグインをCOM3D2.5に入れたりすると発生する。
これが出る場合は、COM3D2版とCOM3D2.5版の取り違えとか、オダメのバージョンがプラグインの想定するバージョン6)でないかあたりを確認する。

スタックトレース

例外が発生した箇所までにどういった処理が呼び出されているかを示したもの。
最初の行(一番上の行)が最後に処理された箇所、つまり例外が発生した箇所です。

ただ、ここに問題があると言うわけではなく、例えば渡されたメイドさんを××するという処理だとして、メイドさんが渡されなかった場合はここでメイドさんがいないんじゃぁ例外7)が発生しますが、その原因はメイドさんを渡さなかった直前の処理です。
その直前の処理もその前の処理が原因かもしれないのでどの処理が悪いかは単純に判断出来ません。
ただし、エラー発生に至るまでの処理がわかるので、その処理を行ったプラグインがぁゃιぃと言うような見当を付けることは出来ます。

例外発生時にスタックトレースを出力せずに「エラー」とかだけ出すプラグインとかがあると、ログからエラー箇所を特定するのが困難に。
(環境を考慮したスタックトレースプラグイン8)を入れると、Unityのログ出力の際のスタックトレースを出力するので原因箇所が特定出来るかも?)

バージョン不一致

プラグインインストール時になにかないと言うエラーが出る場合はバージョン不一致であることが多い。
  • COM3D2用のプラグインをCOM3D2.5に入れている
  • COM3D2.5用のプラグインをCOM3D2に入れている
  • プラグインの想定に対してオダメのバージョンが古すぎる
  • プラグインの想定に対してオダメがバージョンが新しすぎる

特に以下のものはCOM3D2とCOM3D2.5で異なっているので、COM3D2用とCOM3D2用.5用を間違えると出がち。

  • Maid.MaidProp
  • Maid.DelSubProp
  • CharacterMgr.PresetSet
  • ImportCM.LoadSkinMesh_R
  • ImportCM.ReadMaterial
  • TBody関連(典型的なのはTBody.goSlot)
  • COM3D2環境でIsCrcBodyがないと出たらCOM3D2.5用を入れている可能性が大。

オダメバージョンアップ後になにかないと言うエラーが出る様になった場合は、プラグインが新しいオダメに対応出来なくなった可能性があるのでプラグインの作者に連絡するのもあり。

プラグインが使えなくなるからオダメのバージョンを上げないという選択肢は、オダメの新しいDLCが使えないだけではなく、新しく公開されたプラグインも使えない可能性があることに注意。

プラグインがオダメのバージョンで使えなくなる理由の例

今まで呼び出していた処理に渡さないといけないものが変更される。

処理(引数 a, 引数 b)だったものが、処理(引数 a, 引数 b, 引数 c)に置き換えられた様な場合。
この場合処理の名前は同じでも渡さないいけないものが異なっているので別物として扱われる、そのため元のものがなくそんなものはないよエラーになる。

処理結果等渡される値、返される値に想定外のものが追加される。

これの典型的な例はOnLevelWasLoadedでOnLevelWasLoadedに渡されるレベルに負の値が渡されるようになって、trzr氏のプラグインは負の値が来ることを想定して居らずエラーになっている。

enumの値が変更される。

これの典型的例はSlotがずれる、カテゴリーがずれるというやつ。

enumは例えば

enum SlotId
{
  body,
  head,
  eye,
}

と定義するとbodyは0, headは1, eyeは2と言うように連番を振ってくれるのだが、間に追加すると

enum SlotId
{
  body,
  TNTN,
  head,
  eye,
}

bodyは0のままだがTNTNの1が追加され、headは2, eyeは3とずれてしまう。

例えばプラグインで頭を大きくしようと MakeBig(head)9) の様に呼び出していると、コンパイル時に MakeBig(1) と置き換えてしまう。
(コンパイラーは実行時に都度名前から数値に置き換えのは無駄だとし、コンパイル時に確定してしまう。)
これを新しい環境で実行すると、1は頭じゃなくTNTNなのでTNTNが大きなってしまう。

この問題への簡単な対応は新しい環境でコンパイルし直すことで、コンパイルし直すとMakeBig(head)はMakeBig(2)なり正常に動作するようになる。
(ただし、これを古い環境で実行すると、2はeyeなのでおめめぱっちりになる。)


1)
Unityとかmonoの例外処理機構
2)
PatcherやManagedなどでConsoleに出している場合はこの限りではない。
3)
エラーを適切に処理していないという意味では問題かもしれないが。
4)
専門用語「型(かた)」例えばMaid型なら、メイドさんを表すための定義
5)
Fieldも専門用語、型にある値を格納する箇所。例えばさっきのメイドさんの例であれば名前とか。(実際は入れ子になっていてMaid.m_Status.firstName_)
6)
バージョンアップで追加されたものが無いとか
7)
おそらくNullReferenceException、チェックして例外を起こしたならArgumentNullExceptionとか。
8)
メイドいぢりのUnityログを有効にしても同様の効果あり。
9)
ちなみにこんな処理はない、頭の大きさを変えたいならMaid.SetPropをHeadXとかHeadYで呼ぶ。

コメント

コメントを入力:
 
  • appendix/エラーのお話.txt
  • 最終更新: 2024/11/06 18:54
  • by fumble