This supplementary chapter will deal with a trick for allowing more than one script to run at once, by simply loading up each file one at a time, in any arbitrary order, and none of the scripts have to know the others even exist. The way lsnes is structured allows for this simple trick to run multiple scripts in a chain.

A Simple on_paint Chain

local OldPaint= on_paint or function() end   -- Make a local reference to on_paint
function on_paint(non_synth)
    OldPaint(non_synth)       -- Call the old display
    -- Add your own code here
end
You can run any number of these scripts.
In case it isn't obvious from just reading it, this piece of code defines a local OldPaint and gives it the global on_paint (or an empty function if there is no global on_paint). It then replaces global on_paint with a new function that immediately calls our local OldPaint, which thankfully refers to the function we now just removed out of global on_paint. This means the old stuff we're displaying is still getting displayed, while we can add new stuff to it as we wish.
In this case, it is vital to use the local keyword for OldPaint (or some other name you like), as without such, the moment you try to chain any script in, it will replace a global OldPaint with the previous on_paint, and now the old script is calling itself in an endless recursion (and hit a stack overflow). By using local, you forbid any new scripts you load from referring to local values used in a previous script, and it will define a separate local OldPaint for every new script in the chain.

Working with Gaps

Somewhere in our chain, a script is making use of some gap function, such as gui.right_gap. If we have another script that makes use of the same gap, we may end up overlapping our displays. It is often undesirable to have multiple texts on the same spot in the screen, and also inconvenient to edit multiple different scripts to have their displays play nice with each other.
If you're going to chain scripts, it may be best to use delta_gap functions, such as gui.delta_right_gap, and read the value it returns. The delta_gap will add an additional gap on top of what's there, and if you use the number to position your display, it will fit comfortably in the new spot.
Also recall the left and top gap functions will return positive values. The coordinates that write to this gap need to be negative, so don't forget to subtract rather than add these gaps.
local OldPaint= on_paint or function() end
function on_paint(non_synth)
    OldPaint(non_synth)

    local Right,Bottom= gui.resolution()     -- Find our screen edges

    local X= Right + gui.delta_right_gap(96) -- X now points to the left edge of our added gap
    gui.text(X,1,"Hello World.")             -- Hello world. Nice to meet you from this gap.

    X= -gui.delta_left_gap(40) - 40          -- Left side gap, for comparison
    gui.text(X,1,"Left.")
end

Other Callbacks

In most cases, the only thing you have to worry about is making sure to pass all parameters along to the next function in the chain. However, anything dealing with input could easily interfere.
For on_keyhook, if you plan to chain the script, then it's easily possible that another script may have need of other keys. If your script only needs one key, one could just call input.keyhook once and expect only that key to be used. If the script is chained, suddenly the assumption that only that key will activate on_keyhook will fail. Always add code to make sure that the key you wanted is the one being pressed. In the odd case where your script dynamically enable or disable keyhooks, there aren't any good detection methods to ensure you avoid disabling a keyhook needed by chained scripts other than using a consistently named global variable.
For on_input, there is the possibility the script can feed lsnes controller input. Multiple scripts trying to affect on_input independently will only let the latest set functions decide the input. Again, detection methods between scripts is best handled by a consistently named global variable.
Finally, while this isn't unique to any callback, any writes to memory or hostmemory might need to be accounted for. If your scripts are all information scripts that only read, there's no problem, but if you're using a few cheat scripts, there's a possibility of conflict between scripts. The latest write goes through.

EmulatorResources/Lsnes/LuaTutorials/ChainingScripts last edited by FatRatKnight on 10/5/2014 7:47 PM
Page History Latest diff List referrers View Source