ユーザーがオブジェクトのカスタム動作を作成できるように、他のプログラム内にスクリプトエンジンを埋め込みたいとしましょう。
たとえば、特定のイベントが発生すると、サーバーは組み込みのスクリプトエンジンを起動し、各アクターによって提供されるユーザー提供のイベントへの応答スクリプトを実行します。概念的には、映画の脚本のように想像することができます。新しいキャラクターが登場します。シーン内の各アクターは、新しい到着に応答する必要があり、その動作は、ユーザーが指定したスクリプトによって制御されます。
これで、ユーザー提供のスクリプトを実行する組み込みインタープリターを起動できますが、メインアプリケーションでこれらのサブプロセスを「タイムスライス」またはスケジュールする必要があります。
たとえば、各アクターが1秒あたり1つのアクションを実行できるというルールがある場合があります。ですから、俳優が1つのアクションを実行した後、次のアクションの機会まで通訳を一時停止したいと思います。
それを実装することを考えたもう1つの方法は、アクターが自発的に制御を解放することでした。例えば彼らの台本は「こんにちは、1000ミリを眠る、腕を振る、1000ミルを眠る」となるかもしれません。もし私がそうするつもりなら、私は彼らに「クラッシュミー」ボタンを押してプッシュするのもよいでしょう。
インタープリターを破棄または再入力する必要がある場合は、状態/プログラムカウンターを格納して再開できるようにする方法も必要になります。
それらを別々のスレッドまたはプロセスとして実行することも検討していますが、この方法でスレッドまたはサブプロセスを一時停止および再開できるようにするために、それらをどの程度正確に制御できるかはわかりません。編集:私はまた、まるで彼らが眠ることに依存しているかのように、すべてを同期するためにクライアントの行動に依存するでしょう。
これは一般的なゲームデザインパターンだと言われていますが、実装方法に関する有用な情報は実際には見つかりません。埋め込まれたインタープリター、たとえばJavaのNashornJSまたはC++のLua)を見てきましたが、インタープリターの一時停止をサポートしていないようです。これは「簡単な方法」です。
誰かがここでガイドラインを与えることができますか?現時点では、スレッドまたはプロセス制御が最善の解決策であると思いますか?
編集:私は本当に自分の通訳を書きたくありません...私が書いた場合、私は明らかにそれを自由に中断することができますが。基本的なスタックマシンでも大変な作業になります。
編集:「100個のオペコード(など)を実行してからスリープする」ように指示するカスタムインタープリターが必要だと思い始めています。
インタプリタは、この問題のために特別に定義する必要があります。具体的には、多くのインタープリターはネイティブのシステムコールスタックを使用してコンテキストを格納します(つまり、関数呼び出しを解釈するためなど、再帰的に自分自身を呼び出します)。そのような通訳者は容易に停止することはできません。代わりに、インタプリタは関数呼び出しの継続を直接保存する必要があります。
Stackless Pythonは、ゲーム開発業界で人気のあるこの手法の実装です。
だから私はこれが古いことを知っていますが、なぜ私の2セントをポップしませんか:
インタプリタの実行モデルがバイトコードVMバイトコード評価ループ付き)の場合、タイムスライシングスケジューラは次の戦略で実装できます。
これらの「スクリプト」はそれぞれ、VMベースの独自の「スレッド」で実行されます。これらの各スレッドは基本的に、評価スタック、呼び出しスタック、および独自の命令ポインタを保持するメモリの一部です。
VMは、現在実行中の「スレッド」へのポインタと、システム内のすべての「スレッド」のある種のリストを保持します。毎回VM呼び出しスタック、評価スタック、または命令ポインターにアクセスする必要があり、現在の「スレッド」でそれを探します。
評価ループは、何らかの戦略に従って次のスレッドに切り替える責任があります。単純なオプションは、Nバイトコードオペコードごとの単純なラウンドロビンです。これにより、異なる「スレッド」間に「タイムスライス」が作成されます。
例Cの擬似コード(論理的なバグはチェックされていません):
while (program_running) {
Opcode opcode = *vm.current_thread->instruction_pointer;
/* Big switch-case to handle all possible opcodes */
switch (opcode) {
/* ... Cases for each possible opcode ... */
}
/* At the end of the iteration, check if we need to switch thread */
opcode_counter = (opcode_counter + 1) % THREAD_SWITCH_INTERVAL;
if (opcode_counter == 0) {
/* Switch to next thread */
vm.current_thread = vm.threads[vm.thread_index++ % vm.threads_count]
}
}
私はこの戦略を私のおもちゃプロジェクトに実装しました。魅力のように機能するようです。
これが将来誰かに役立つことを願っています! :)