-
Notifications
You must be signed in to change notification settings - Fork 160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AGS 4.0: rewrite ccInstance into non-forking ScriptExecutor #2621
base: ags4
Are you sure you want to change the base?
AGS 4.0: rewrite ccInstance into non-forking ScriptExecutor #2621
Conversation
I think we can push these changes further, and introduce a "Script Thread" class. The "Script Thread" will contain its own data stack and call stack, leaving only registers in the ScriptExecutor. ScriptExecutor will be told to run this or that script on a specific "script thread". Then there will be 2 default "threads": the "main" one, and a "non-blocking" one to run "rep-exec-always" on. This will nicely mimic the previous behavior. But the "script thread" objects may become more useful somewhen in the future if we try to implement coroutines in AGS. |
I see there are changes to the script compiler, does this change requires rebuild of the auto test game with it or should be able to run the previously built ags4 auto test game ? |
No, there are no changes to compiler itself, there are only corrections to data types in ccScript. I did not have time earlier, but I will check out what happens when this runs a test game. |
e100dad
to
db24398
Compare
Just a small note, this pr is working when the engine is run on its own, but it's crashing whenever run from IDE. Possibly the debugger connection is broken. I did not have time to look into this yet, but will finish this in the new year. Besides the mentioned bug there are couple of modifications that I wanted to make. And then this also will need to be tested for performance. The main execution loop did not change, it's mostly just copied from one class to another, but it's better be safe. |
tested and confirmed, weird error though.
I guess Line 108 in b60cc86
|
2fadfb7
to
e78fa60
Compare
Save original input code[] values meant for fixups in _fixupValues[] array, as code[] array will be overwritten with resolved addresses etc. Use saved _fixupValues[] when resolving imports.
1. Push and pop callstack on calls to ScriptExecutor::Run() in case we detect a nested Run. Thus we don't have to push/pop callstack on any CALLEXT, so not slowing it down further. 2. Add a Busy bit flag which is set when we start running bytecode, and temporarily unset when we call an external function. This flag indicates whether it's safe to Run another nested script. 3. Reorganized code a bit, try to keep push/pop callstack and saving previous stack pointers as immediately close to the script function call as possible, in sake of the code clarity.
e78fa60
to
2beb19c
Compare
The debugger connection is fixed. As an additional rework - implemented ScriptThread class which contains a saved state of a "script thread": a data stack, callstack and execution position. The ScriptExecutor now has to be told to execute the script on a particular "thread". There are 2 standard threads created by the engine: "Main" and "Non-blocking" (for running "rep-exec-always"), but more could be added if needed. My belief is that this system potentially allows to implement "coroutines" in AGS at some point in the future, because it allows to pause a script thread, save its state, and then resume later at some point. I am referring to this coroutine idea I once posted on AGS forums. |
ScriptThread class contains a saved state of a running script thread, with a data stack, a callstack and current exec position. ScriptExecutor always runs a script on a certain thread. ScriptExecutor maintains a nested list of active threads. These may only be run one at a time in AGS, so when a new script runs while there is another active thread, that thread gets pushed to a thread stack and popped back after that new script finishes running. Engine creates 2 default script threads: "Main" and "Non-blocking" for running rep-exec-always kind of callbacks. There could be more ScriptThread instances created as necessary.
2beb19c
to
816fe23
Compare
Not planned as a part of this PR, and strictly as a "proof of concept", I made this dumb coroutine test: Not done fully right, but works in practice. Normally you'd make a script keyword for async or iterating operations (like In principle it works like this:
The current implementation cannot have multiple simultaneous coroutines, any new one will overwrite previous completely, but that's just because I was lazy. Normally it should have a list of wait counters mapped to coroutine threads, or something similar. Example of script:
|
Purpose:
What is done
In the situation where a script would call a blocking action, such as Wait() function, for example,
previously engine would do this:
after this change it will do: