NScripterがクラッシュした!エラーが出てくれない!謎エラーが出た!という事態の原因リスト
(書いた日:2018/11/22 最終更新:2020/09/22)
■明らかに変なエラーが出た!
謎の「文字列が来るべきところに文字列がありません。」
■エラーが出ない!
NScripterが落ちた!固まった!
症例1:nscr.exeは応答していません
ウェイトなき無限ループ
NSSleep()/NSDoEvents()関数なき無限ループ(Lua)
NSSleep()関数に負の値を渡した(Lua)
症例2:nscr.exeは動作を停止しました
strsp命令における文字の領域はみ出し
異常なバックログを開こうとした
壊れたセーブポイントの使用(Lua)
error()関数に長い文字列が渡された
NSDClear()なきNSDLoad()時のメモリリーク(Lua/ver3.03以前)
スプライトのセル指定を間違えた
os.date()関数に誤った引数を渡した(Lua)
select系命令における文字のテキストウィンドウはみ出し
NSPopStr()関数で512バイト以上の文字列を読み込もうとした(Lua)
プラグイン内部でエラーが生じた
本来は引数ミスで出るエラーだが……
「NSExec()関数に投げた命令が引数を余らせてしまった」際にもなぜかこのエラーが出る。
--------
NSExec('bg "white",0,"あいうえお"')
--------
NSExec内部でエラーが発生した場合、NScripterはデタラメなスクリプト位置を吐いてくる。加えてLua側に処理が戻る事なく本体が落ちる(endコールバックのみ機能する)ため、標準の環境で一度やらかすと原因(記述ミスしている箇所)の特定が難しい。debugライブラリで無理矢理状況を探る事も出来るが要注意。
自作luasub命令の引数を弄った際に修正漏れが無いか念入りに確かめよう(出来ればエディタの検索機能に頼ろう)。
(余談)
制作環境にも左右されるが、「文字がウィンドウをはみ出しました」エラーも同じ原因(引数が多すぎる)由来で発生する事がある。
余った引数がテキストと見なされた結果、奇数バイトの半角文字を表示しようとしてしまい文字化けが発生し……と連鎖的にエラーを起こしてゆくケース。
落ち着け
NScripterは大抵の場合懇切丁寧にエラーの原因を教えてくれる。
が、少ないながらも特定条件下でエラーに辿り着かずに落ちる場合がある。
知る限り示しておく。
関係ないけど:エラーは出るのになんでそうなるのか分からん的な事態に出くわした場合は、不等号の向きとか配列に渡す変数とかしっかり確認しようね。約束だよ。
ウェイトなき無限ループ
誰もが一度は通る道。
--------
;無限ループ
for %0=1 TO 4
mov %0,%0-1
;こいつが無いとWindowsはプログラムが応答なしと見なす
wait 1
next
--------
ウェイト無しで計算処理を回し続けると、Windows君に「あ、こいつ俺のCPU手放さねえ。なんかやらかしたか?」と検出される。その通りだよ。
そもそも無限ループからしてNScripterの扱うジャンルを考えれば9割がたミスによるものであろう……
NSSleep()/NSDoEvents()なき無限ループ(Lua)
Luaで無限ループしたり長い処理作ったりする場合も
--------
while(true)do
NSSleep(0)
NSDoEvents()
end
--------
忘れないようにしましょう。
NSSleep()関数に負の値を渡した(Lua)
--------
do
local oNSSleep=NSSleep
function NSSleep(num)
local time=tonumber(num) or 0
--値が負だとフリーズする対策
time=time<0 and 0 or time
oNSSleep(time)
end
end
--------
このように対策可能。
strsp命令における文字の領域はみ出し
「動作を停止しました」の原因は大抵これであると思われる。
--------
;クラッシュする例
strsp 1,"あい\うえおか\きk\kk\kkk",0,0,3,1,20,20,0,0,0,0
--------
指定したウインドウを文字がはみ出ると(かつ、\を含むと?)無言でクラッシュする。
命令乗っ取ってしっかりエラー出すようにした方がいいと思う。
異常なバックログを開こうとした
標準バックログが壊れるシチュエーションはいろいろあるが、NScripterには珍しくログ周りは何も考慮してくれない。
locate命令(存在知ってた?)で変な値を指定したり、ルビ回りで変な事したり、tateyoko命令を安易に使おうとしたり……
壊れたセーブポイントの使用(Lua)
00.txt無しで(Luaのみで)起動した場合、何らかの条件下でセーブポイントが正常に作成されない(壊れたセーブポイントが作られる)場合がある。
その状態(外からは検知不可)でsavegameするとめでたくクラッシュ。
00.txt無しでの起動はいくつかの不安定な動作を誘発する。
NSLaがほとんど飾りである00.txtを置いている理由の一つ。
error()関数に長い文字列が渡された
みんな大嫌いな環境依存。僕の環境では1000バイト前後でクラッシュ!
内部の受け渡しにおいてメモリ確保失敗したりしているものと思われる。
error関数を乗っ取り、NSOkBox()やNSYesNoBox()にすり替えて画面表示すれば対応可能。
ただし長い文字列をボックスで表示しようとすると相当深刻に重い。恐らくユーザー側はフリーズと区別付かない。
あまりに長いエラー文字列はそれ自体がエラー的であり、エラーログとしてテキストに出力すべきなのだろう。
NSDClearなきNSDLoad時のメモリリーク(Lua/ver3.03以前)
これが正しいあり方。
--------
NSDLoad(1,〜)
NSDClear(1)
NSDLoad(1,〜)
--------
前のテクスチャを明示的に消さずに上書きすると、旧いテクスチャのメモリが解放されずにどんどん使用メモリが上がっていく……というバグが3.03まであった。
--------
NSDLoad(1,〜)
NSDLoad(1,〜)
--------
スプライトのセル指定を間違えた
「存在するファイルを読み込んだが、セル指定を間違えていた」という条件でクラッシュ。
--------
lsp 999,":a/;button\\selfileBG.png",0,0
--------
こういう事をすると(「/」の直後が「1以上の数字」でないと)無言で落ちる。
出会う頻度と危険度(分かりづらさ)では一番かも?
★確認:アニメーションとセルの指定★
アニメあり |
◆セル毎の表示時間は一定 | ◆セル毎の時間を個別指定 |
「セル数,コマ毎の表示時間,ループ形式」 lsp 0,":a/3,100,0;ファイル名.png",0,0 | 「セル数,<1コマ目表示時間,…,nコマ目表示時間>,ループ形式」 lsp 0,":a/3,<10,30,50>,2;ファイル名.png",0,0 |
※ループ形式 0…末尾から最初のセルに戻る 1…末尾で停止する 2…末尾から逆向きに折り返す |
アニメなし |
◆セルは複数 | ◆セルも無し |
「セル数」 lsp 0,":a/2;ファイル名.png",0,0 | lsp 0,":a;ファイル名.png",0,0 |
事実上pngに統一された現代では、
transmode alpha(αチャンネル参照)を定義節で指定する。
つまり以下のようになる。
アニメあり |
◆セル毎の表示時間は一定 | ◆セル毎の時間を個別指定 |
「セル数,コマ毎の表示時間,ループ形式」 lsp 0,":/3,100,0;ファイル名.png",0,0 | 「セル数,<1コマ目表示時間,…,nコマ目表示時間>,ループ形式」 lsp 0,":/3,<10,30,50>,2;ファイル名.png",0,0 |
※ループ形式 0…末尾から最初のセルに戻る 1…末尾で停止する 2…末尾から逆向きに折り返す |
アニメなし |
◆セルは複数 | ◆セルも無し |
「セル数」 lsp 0,":/2;ファイル名.png",0,0 | lsp 0,"ファイル名.png",0,0 |
アニメ無し複数セルの指定(大抵はボタンに使いたい場合だろう)のみ、やりたい内容に対して記述が直感的ではない。lsp/lsph命令をluasubで乗っ取って"2;ファイル名"で済むように改造した方が分かりやすいかもしれない。
os.date()関数に誤った引数を渡した
仕様に存在しないパラメータが含まれているとクラッシュしてしまう。
select系命令における文字のテキストウィンドウはみ出し
限界まで表示されてからクラッシュするので、慌てているとselect系命令が原因と気付きにくいかもしれない。
NSPopStr()関数で512バイト以上の文字列を読み込もうとした
luasub命令の引数限定で発生、おそらく内部処理の問題。
生の文字列・文字変数の中身ともに511バイトを超えるとクラッシュする。
文字変数に格納した長いデータをLuaに投げる際は注意が必要。
「100%確実に文字変数を渡す」と分かっている命令ならNSPopStrRef()とNSGetStr()を組み合わせて一応は回避できる。
生の文字列はどうしようもない…
プラグイン内部でエラーが生じた
プラグイン(Dll)の中でエラーが発生した場合、本体がクラッシュする。
(行儀は悪いかもしれないが)最低限の対処として、内部の関数全体をtry〜catchで括っておけば本体クラッシュだけは防げる。
オープンソースでない限り、プラグイン内部は利用者側から手出しできない。
運良く再現性を見つけたらプラグイン作者に問い合わせてみよう。
現役で返事を寄越してくれる人の割合は別として修正してもらえるかもしれない。
結局のところ
標準環境ではデバッグウインドウからログを垂れ流しつつ地道にデバッグするしかない。
どれかには当てはまるといいね。
ガラスグサに戻る