Former player
Joined: 2/19/2007
Posts: 424
Location: UK
Kuwaga: The lua scripts never actually make the emulator frameadvance. Instead, the emulator runs the scripts once per frame, and does not continue before they are finished for this frame. The emulator does something like this:
while(true)
{
    call lua's beforeemulation hooks
    emulate a frame
    call lua's afteremulation hooks
    if(displayframe)
    {
        call lua's graphics hooks
        update screen
    }
    call main lua function
}
Note that these calls do not happen in parallel, but one after another, so that the emulator does not do anything while lua is running, but only continues when lua returns (by calling "emu.frameadvance" in the case of the lua main function). In the case of multiple scripts, the emulator calls the first one, which does something and then returns by calling emu.frameadvance. When the emulator gets back control, it calls the next one, and so on. So emu.frameadvance does not advance the frame, it simply says "I'm done for now, continue doing whatever you were doing". DarkKobold: If you look at the pseudocode I posted above, you will probably see the cause of your problem. The lua main loop is called after the screen has been updated, but before the next frame has been emulated. So at this point, your script has access to the previous frame's state, but anything you draw will appear the next frame, and thus effectively lag one frame. To avoid this problem, you can use the other lua hooks. Afteremulation, for example.
Emulator Coder, Skilled player (1310)
Joined: 12/21/2004
Posts: 2687
amaurea wrote:
Kuwaga: The lua scripts never actually make the emulator frameadvance.
This depends on the emulator. It's actually reentrant in Gens and DeSmuME. The Lua script is really calling the emulator's function to advance a frame, there is no outer loop in the emulator that does it while the script is running, there is no "call main lua function" happening every frame, and there's no coroutine yield involved. If two different scripts call emu.frameadvance it in a row, the emulator makes no attempt to somehow merge the two calls into one (they each get what they asked for). Admittedly, it's a small distinction (each way has different advantages and disadvantages but they behave almost the same most of the time). But I think that frameadvance is not a bad name for describing what it is conceptually doing, either way. Warp: There is an implicit pause inside the emulator's frame advance function depending on the settings: if not in some fast-forward mode, it will wait enough for the framerate to stay steady, and if the user has paused emulation, it will wait until the user unpauses to return from the frame advance function. Because it's a function call, the Lua script also waits where it was until the frame advance function returns, that's just how function calls work (in the absence of asynchronous madness which is best avoided in cases like this). And frame advance means "run 1 frame" in this context.
Emulator Coder, Site Developer, Former player
Joined: 11/6/2004
Posts: 833
Internally emu.frameadvance() is almost literally coroutine.yield() - almost, because there is some extra book-keeping. I expected to need more yielding functions in the future so emu.frameadvance is maybe closer to coroutine.yield("SECRET-SIGNAL-NEXTFRAME") and then the emulator just keeps on running as though there was no script loaded. It simplifies both the emulator and the script. You can call frameadvance anywhere you want any time you want and modifying the emulator is so much easier. So think of frameadvance as "Ok, I'm done for now - keep going as usual"
Banned User
Joined: 3/10/2004
Posts: 7698
Location: Finland
DeHackEd wrote:
So think of frameadvance as "Ok, I'm done for now - keep going as usual"
Scheduler yielding is what came to my mind as well, when reading the explanations in this thread (see "man sched_yield" if using linux for an idea). "yield()" would be a much more accurate name than "frameadvance()", which really doesn't. The explanation clarifies the issue a lot, but the name of the function is quite misleading. Well, I suppose it's too late now for anyone to change it.
Joined: 1/19/2010
Posts: 146
I don't know all that much about lua scripts yet myself, but I think you may have forgotten something, wicked. In the scripts that you are copying and pasting, you need to change something. Where it says "emu.frameadvance()" You should change it to "The emulator you use.frameadvance()" So as an example of what it would be in snes9x it would say this: snes9x.frameadvance() Sorry if I have misinformed as I am new to this and still learning, but I do hope I was correct.
Joined: 10/3/2005
Posts: 1332
Raijin: The snes9x/FCEU/etc tables should be (and probably have already been) deprecated in favor of the "emu" table. One reason for this is that a sufficiently generic script should work with any emulator without need of modification. Having to globally change "snes9x" to "FCEU" to make a script work on another platform isn't exactly onerous, but it is undesirable just the same.
Joined: 1/19/2010
Posts: 146
Dromiceius wrote:
Raijin: The snes9x/FCEU/etc tables should be (and probably have already been) deprecated in favor of the "emu" table. One reason for this is that a sufficiently generic script should work with any emulator without need of modification. Having to globally change "snes9x" to "FCEU" to make a script work on another platform isn't exactly onerous, but it is undesirable just the same.
Oh I see. I guess I need to learn some more before I try to help someone so they don't get mixed up :P Thanks for letting me know this as well.
TRT
Former player
Joined: 5/13/2009
Posts: 132
I've been looking at the sample lua scripts to get an idea of how to write it (I really see how I can use this in some of my future projects) Its nothing too different from Java or C++. However, some parts of it seem fuzzy for me. Just to be sure I get the basics of it, how would the general syntax of these two look like (with Desmume 0.9.5 as the emulator): While a certain memory value is 0, press "right" and X Keep advancing one frame until a certain memory value is 1, then have a save state at that point.
Banned User
Joined: 12/23/2004
Posts: 1850
TRT wrote:
I've been looking at the sample lua scripts to get an idea of how to write it (I really see how I can use this in some of my future projects) Its nothing too different from Java or C++. However, some parts of it seem fuzzy for me. Just to be sure I get the basics of it, how would the general syntax of these two look like (with Desmume 0.9.5 as the emulator): While a certain memory value is 0, press "right" and X Keep advancing one frame until a certain memory value is 1, then have a save state at that point.
state = savestate.create();

while true do
  if memory.readbyte(0x1234) == 0 then
    joypad.set(1, {"right" = true, "X" = true});
  elseif memory.readbyte(0x1234) == 1 then
    savestate.save(state);
    emu.pause();
  end;
  
  emu.frameadvance();
end;
Actual code may vary, but that should be more or less usable.
Perma-banned
Skilled player (1652)
Joined: 11/15/2004
Posts: 2202
Location: Killjoy
amaurea wrote:
DarkKobold: If you look at the pseudocode I posted above, you will probably see the cause of your problem. The lua main loop is called after the screen has been updated, but before the next frame has been emulated. So at this point, your script has access to the previous frame's state, but anything you draw will appear the next frame, and thus effectively lag one frame. To avoid this problem, you can use the other lua hooks. Afteremulation, for example.
I'm not really sure how to do that, with a function that uses frameadvance. Also, how do joypad.get and joypad.set interact with frameadvance?
Sage advice from a friend of Jim: So put your tinfoil hat back in the closet, open your eyes to the truth, and realize that the government is in fact causing austismal cancer with it's 9/11 fluoride vaccinations of your water supply.
Emulator Coder, Site Developer, Former player
Joined: 11/6/2004
Posts: 833
Joypad.get reads what the user is pressing (the user's intentions) on some joypad. joypad.set replaces the user's intentions with the script's own when it next calls frameadvance trampling the user's choices. Here's your button mashing script:
-- Script runs forever
while true do

   -- push button
   joypad.set(1, {A = true})
   snes9x.frameadvance()

   -- release button
   joypad.set(1, {A = false})
   snes9x.frameadvance()
end
And here's a more complicated example that involves a user-accessible Stop command using the Start (ha!) button
-- On/off rapid toggling of this variable
local status = true


while true do

   -- Give the user an out by pressing start. This exits out of the loop
   -- and since the script ends at that point, the script exits
   if joypad.get(1)['start'] then
      break
   end

   -- Press (or not) the A button. All other buttons are not pressed
   -- and the user's input be damned.
   joypad.set(1, {A = status})

   -- Toggle toggle
   status = not status

   -- Next frame
   snes9x.frameadvance()
end
amaurea: rather than saying the emulator "calls [the] main lua function", you should say it "resumes" the main function. The function is a coroutine and hence has the appearance of running as its own thread non-stop. In fact if the main function ever exits the script is unloaded. It can only be stopped/frozen at the frameadvance() function which may be executed in many different locations. I realize I'm being a bit asinine on a technicality but I want to make it clear how it works since it seems people are still confused.
Skilled player (1652)
Joined: 11/15/2004
Posts: 2202
Location: Killjoy
Let's say you wanted to copy whatever input the player just made, on the current frame, and force it to be the same input on the next frame. Would it be:
Joypad.get()
Snes9x.frameadvance()
Joypad.set()
snes9x.frameadvance()
Or
Snes9x.frameadvance
Joypad.get
Joypad.set
Snes9x.frameadvance
Sage advice from a friend of Jim: So put your tinfoil hat back in the closet, open your eyes to the truth, and realize that the government is in fact causing austismal cancer with it's 9/11 fluoride vaccinations of your water supply.