Plan for script multitasking

From OHRRPGCE-Wiki

Jump to: navigation, search

To support script multitasking, each script "thread" will have separate execution information: the list of running scripts (currently scrat), a private stack for arguments and state information, and probably space for local variables (if each script instance doesn't get it's own buffer for locals).

Now each triggered script will run in its own thread. So a map autorun script containing a loop will be able to check if the player has left the map so it should stop, even if another looping map autorun script was triggered. This will of course break a great many games, so it will be turned on by a general bitset which will be off for old games and on for new games.

Contents

[edit] Naming

I think restricting the use of the term "thread" will avoid confusion. Thus, these proposed commands refer to scripts instead of script threads. Ignore use of 'thread' in this Plan.

[edit] Blocking scripts

A general bitset will disable threading for old games, so backwards compatibility will not be affected.

However, to ease the transition from blocking scripts to threading scripts for authors of existing games, we can add a new script command, blocking script (see above). By adding this command to the top of a script, it will behave like old scripts, and pause any other blockingscripts until it is finished.

A game author who wishes to convert their existing scripts to use threads can do the following:

  1. Add blocking script at the top of all their plotscripts
  2. Change the general bitset to enable threads for their RPG
  3. Remove blocking script one-at-a-time, with testing.

[edit] Execution order

New triggered threads should be set to run before all current ones. That way, onkeypress scripts are run first, and the current script order is preserved: the mapautorun script of the first map runs before the newgame script.

However, script threads started from other scripts are tricky.

My current thinking is this: firstly, any new threads should run that tick instead of having to wait. In fact, it would hopefully be least confusing if they ran immediately, in the order in which they are written in the script. In fact, they would be put before the parent thread in the thread list, and the interpreter would jump up the list by one script. This has the additional advantage that all spawned threads will run each tick in the order they were created from their parent.

(This idea gets the James seal of approval Bob the Hamster 20:48, 16 October 2008 (UTC))

[edit] Hazards

The script debugger would need a lot of attention to handle threads. It also needs a lot of attention to make it user-usable, and potentially to handle extra HSpeak outputted debug info. In sum, a rewrite. Before it was ever officially ready for use :(

Some commands, like teleporttomap, imply a wait and others set variables to trigger an effect when they finish, like showtextbox. Commands which communicate with want variables can be rewritten where it's not backwards incompatible to do so, other all threads could just continue to use the same want variables. Still uncertain-

Here is the list of want flags:

wantbox, wantdoor, wantbattle, wantteleport, wantusenpc, wantloadgame

(How about making want variables members of the script's UDT, that way each "thread" has its own copy of them and they cannot collide. I don't think this would break any compat. Bob the Hamster 20:50, 16 October 2008 (UTC))
(Also, most want variables will indeed be possible to eliminate. The main reason they exist is to provide a way for code inside subs to trigger jumps to module-level GOSUBs which is irrelevant now that many of those gosubs have already be SUBified Bob the Hamster 20:52, 16 October 2008 (UTC))


[edit] Opportunities!

Perhaps the implicit waits in commands like usenpc could be disabled when script multitasking is enabled? Rational: continuing with the current behaviour would require either forcing just the calling script to wait and allowing others to run (when a user who doesn't know about the implicit wait probably won't expect them to!) or jumping out of the interpreter completely, halting other scripts for a tick. Both seem illogical.

[edit] New script commands

A tentative list of suggested new commands to handle threads:

Omitted are many commands to check what threads are currently running, to manipulate the order they run in, other usefuls. Possibly the ability to switch temporarily between threads?

[edit] new script, begin, ..., end

When this block (which is actually a flow control type (or maybe a new type altogether), and NOT a top level statement like 'script') is encountered, execution appears to proceed into it, but in fact occurs in a newly created thread which is set to run BEFORE this thread in the thread list, but immediately gets focus. See #Execution order. When it waits or completes, the parent script is reentered all in the same tick. Can contain anything. This block also returns a script handle.

The parent script can quit regardless of what spawned scripts do, and if any are still alive, then the parent script's local variables aren't freed.

You can access variables declared in the parent script, while variables declared inside this block could be in a different scope to the parent script. This can be achieved by putting all the variables in the parent script's variable list, but keeping track in HSpeak what scope variables have (probably with some behind the scenes name decoration) But is suddenly adding scoping to HS a good idea? I guess it's just not needed, and encourages people to put big messy scripts in newscript blocks instead of splitting them off.

Should be nestable.

Example:

script, split up!, begin
 variable (Sean, Sarah, Sam)
 Sean := npc reference (...
 #... NPC manip

 show text box (49)  # everyone split up!
 wait for textbox
 variable (count down)
 count down := 300

 new script, begin
  walk npc (Sean, left, 4)
  wait for npc (Sean)
  walk npc (Sean, up, 10)
  #disappear off map...
 end

 variable (Sarah's path)
 Sarah's path := new script, begin
  walk npc (Sarah, right, 8)
  wait for npc (Sarah)
  #...
 end

 #continue main script here...
 # some special condition: Sarah stops and comes back
  kill script (Sarah's path)
  wait for npc (Sarah)
  #walk Sarah back...
 
end

But normally you would split things up into other scripts if they are nontrivial:

To run a script in a new thread instead of a bunch of commands, you would just write

handle := new script( falling chimney animation(npc) )

Since this would be common, and we won't want the parent script's variables and data kept around so unnecessarily, this should be optimised as special command/whatever.

What if you don't want the script to run immediately, but just want create it and get the handle to manipulate it? You could write

handle := new script( wait, falling chimney animation(npc) )

but this bypasses the above proposed optimisation and slightly unpleasant. Prehaps we need another command.

[edit] new script by id (@scriptname, arg1, arg2, ...)

Probably don't need/shouldn't add this command. You can write

new script, begin
 run script by id (@scriptname, arg1, arg2, ...)
end

instead.

[edit] pause script (handle)

Stop a thread from being executed. For example, if you want the current and another thread to wait for something, you could do:

pause script (handle)
wait for npc (npc)
unpause script (handle)

[edit] resume script (handle)

[edit] wait for script (handle)

Waits for the given script (thread) to finish.

[edit] wait for child scripts

Waits for all script threads spawned from this one to finish. (What about grand children?)

[edit] blocking script

Pauses all other script(thread)s which have run this command, and automatically resumes them when the script(thread) in which this appears quits.

[edit] pause all scripts

Pause all other scripts.

[edit] resume all scripts

[edit] kill script (handle)

Kill off a whole thread, ending all scripts in it. Could be used to kill the current thread (prehaps a command to get an id for the current thread? But would it have other uses?)

[edit] exit thread??

(Replace by kill script with default argument, I suppose) Kill off current thread. This could also be used if multitasking threads are not enabled, killing all scripts.

[edit] pause script triggers

Stop triggering of scripts (by game events). This would also be useful if multitasking were disabled.

[edit] resume script triggers

[edit] See Also

Personal tools