1 2
9 10 11 12 13 14
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
If the RNG changes every frame, then it's going to get slowed down since then the script has to be re-run every frame. Including all of the drawing. I'd suggest not using the RNG tab if you don't actually need it (or only using it if you need TAS precision on the RNG which at that point wouldn't you be doing frame-advance anyway?) Can you share the script? I'm curious to know how it works and to see how it runs on my machine.
Adventures in Lua When did I get a vest?
Experienced player (658)
Joined: 5/16/2009
Posts: 235
Pokota wrote:
If the RNG changes every frame, then it's going to get slowed down since then the script has to be re-run every frame. Including all of the drawing. I'd suggest not using the RNG tab if you don't actually need it (or only using it if you need TAS precision on the RNG which at that point wouldn't you be doing frame-advance anyway?) Can you share the script? I'm curious to know how it works and to see how it runs on my machine.
Yeah, that's what I currently do. I'd like to have a way to run the script like each 2 or 3 frames, but drawing is cleared after each frame (I don't know if it's possible to change this). This is the link of the lua: https://www.dropbox.com/s/l8dte92o51pw6p2/PokemonGen3Lua.zip?dl=0
dan
He/Him
Joined: 9/23/2016
Posts: 25
mkdasher wrote:
Also I have another question, everytime frameadvance is called the whole lua is cleared. Is there a way to refresh the lua manually and only update it when I want to?
I was operating under the assumption that reading memory is the slow part. My idea was to read the memory on certain frames, and stick them in a local Lua variable so it can be drawn every frame. Also, after seeing where you are going, I underestimated just how much you were doing. Wow.
Editor, Player (175)
Joined: 4/7/2015
Posts: 331
Location: Porto Alegre, RS, Brazil
I'm testing the script and it's amazing! The main lag problem is drawing inside fors. Since the main RNG drawing is
Language: lua

local rngcols = 12 for i=0,rngcols-1,1 do for j=0,17,1 do --[...] drawRNGsquare(j,i,clr) end end
you're drawing 216 squares each frame, which indeed lags (my fps drops from 60 to 45, which is not that bad). I removed just this square printing and my fps went back to normal, as you can see. I noticed that this RNG grid is like a stream of values: the squares are floating from the right to the left, like if it was printing the current RNG value + the past 17 values (correct me if I'm wrong). If so, is it really necessary printing past values?
Games are basically math with a visual representation of this math, that's why I make the scripts, to re-see games as math. My things: YouTube, GitHub, Pastebin, Twitter
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
Fun fact - you can't currently draw image files onto a lua canvas, unless you can and I'm just a dumb. The goal is to put these image files onto a separate canvas so that the emulator window remains uncluttered while streaming/recording.
Language: lua

-- pokemon.lua -- Version 1 "Carol" require 'pkmn_g1_data' memory.usememorydomain("System Bus") game_offset = 0 yellow = false pikachu = 0xd46f pikachu_happy = 0 drawSpace = gui.createcanvas(32*6,32*5) controls = forms.newform(160,144, "Pokemon Support Controls") function cleanLog() console.clear() end function discernGame() gameflag = memory.read_u8(0x13c) if gameflag == 0x59 then console.log("Pokemon Yellow detected, using Yellow addresses.") game_offset = 1 yellow = true else console.log("Pokemon Yellow not detected, using Red/Blue addresses.") end end discernGame() function drawArray(array) width = 32 height = 32 row = 0 column = 0 drawSpace.Clear(0xff000000) if array[1].sprite then for k, v in pairs(array) do console.log(v.sprite) if column > 5 then column = 0 row = row + 1 console.log("Row "..row..", Column", column) end --drawSpace.drawImage(v.sprite,width*column,height*row) column = column + 1 end else console.log(debug.traceback()) error("Error in drawArray - target array does not define sprites") end end function generateArray(address) array = { } i = memory.read_u8(address) for j=1,i do array[j] = getPokemonByIndex(memory.read_u8(address + j)) end console.log("Array generated from address ".. address) return array end function updateParty() partyArray = generateArray(0xD163 - game_offset) console.log("Party array populated") if yellow then pikachu_happy = memory.read_u8(pikachu) console.log("Pikachu's happiness is ".. pikachu_happy) end drawArray(partyArray) drawSpace.SetTitle("Party Mode") end function updateBox() box_total = 0xDA80 boxArray = generateArray(box_total - game_offset) box_total = memory.read_u8(box_total) message = "There are ".. box_total .." pokemon in the active box." console.log(message) if box_total > 17 then gui.addmessage(message) end drawArray(boxArray) drawSpace.SetTitle("Box Mode") end manualPartyUpdate = forms.button(controls, "Party", updateParty, 5, 5) manualBoxUpdate = forms.button(controls, "Box", updateBox, 5, 40) manualLogClear = forms.button(controls, "Clear Log", cleanLog, 5, 75) console.log("Controls dialog has been assigned handle "..controls)
Adventures in Lua When did I get a vest?
Experienced player (658)
Joined: 5/16/2009
Posts: 235
brunovalads wrote:
I'm testing the script and it's amazing! The main lag problem is drawing inside fors. Since the main RNG drawing is
Language: lua

local rngcols = 12 for i=0,rngcols-1,1 do for j=0,17,1 do --[...] drawRNGsquare(j,i,clr) end end
you're drawing 216 squares each frame, which indeed lags (my fps drops from 60 to 45, which is not that bad). I removed just this square printing and my fps went back to normal, as you can see. I noticed that this RNG grid is like a stream of values: the squares are floating from the right to the left, like if it was printing the current RNG value + the past 17 values (correct me if I'm wrong). If so, is it really necessary printing past values?
Yeah, I know most of the lag comes from there. Printing a similar grid like this doesn't have any effect on lag on other emus like VBA (FractalFusion did a similar lua) and Desmume, but unfortunatelly it does here. The grid doesn't print the past RNG values, it prints the current RNG plus the next 18 * (number of rows) values. It helps predicting the RNG and manipulate it (how to hit a critical hit as fast as possible, capture a pokemon, get a certain encounter, etc). My solution for this grid right now is having a "-" and "+" button just below that adds and eliminates rows. By eliminating some rows in that grid I can run the lua with almost no lag.
Pokota wrote:
Fun fact - you can't currently draw image files onto a lua canvas, unless you can and I'm just a dumb. The goal is to put these image files onto a separate canvas so that the emulator window remains uncluttered while streaming/recording.
Language: lua

<codeblock>
That looks interesting! I'll have a look at this, thanks!
Post subject: emu.registerbefore()
oddyman
He/Him
Joined: 3/28/2017
Posts: 2
Okay! So after some checking, I've figured out this must be the place to post my first post on this forum. I couldn't find the answer anywhere so here goes. I'm running the BizHawk version 1.12.0 and emulating Sonic The Hedgehog for Genesis. At the moment my goal is to always force Sonic to face left, despite the input of left and right. There is but one hinch in my plan as of yet that bugs me, the fact that he is not consistently facing left. After some thinking and digging I've concluded that it's because it's updating after I've written to memory. But before the frame is emulated, I therefore tried using emu.registerbefore() to fix this... So first of all I should probably ask: will it actually fix it? If so, I'll have to ask what is wrong, because I cannot for the life of me get it to work. I'm starting to think that the emulator does not support it? This is the error message I get: LuaInterface.LuaScriptException: [string "main"]:5: attempt to call field 'registerbefore' (a nil value) It does not happen if I put it after the while true loop (but then again I suppose it never runs then) Here is the code (feel free to ignore the updateSonicFacing() in the while loop, but it's there to represent my earlier attempt)
Language: lua

local function updateSonicFacing() memory.writebyte(0xFFD022, bit.bor(memory.readbyte(0xFFD022), 0x01)) end emu.registerbefore(updateSonicFacing) --Here it begins memory.writebyte(0xFFFE1C, 0x1) -- Allows lives to be set local timer = 60 while true do -- Forces 4 lives if memory.readbyte(0xFFFE12) < 0x4 then memory.writebyte(0xFFFE12, 0x4) end updateSonicFacing() timer = timer - 1 emu.frameadvance() end
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
It looks like you're using bus addresses instead of domain addresses. This is fine for most emulators but not for BizHawk, which uses domain addresses for everything except event hooks (which I've griped about this before). In the lua console, run the command memory.getmemorydomainlist() - you'll want to use the one that says Bus, probably the M68K Bus. You should start your script with
memory.usememorydomain("M68K BUS")
In BizHawk, you want event.onframestart(function) if you want a function to run every frame before the frame update. I fully suggest you have the Lua Functions page on hand while you work with your scripts, it's a handy reference. Also, what purpose does the timer serve? You don't appear to compare anything to it or evaluate using it or anything. You just count backwards from 60 by one each frame the script is running...?
Adventures in Lua When did I get a vest?
oddyman
He/Him
Joined: 3/28/2017
Posts: 2
Ah... Now I understand... thanks. It seems my hunch about the non-constant facing left was wrong, but at least now I can experiment a bit more.
Pokota wrote:
Also, what purpose does the timer serve? You don't appear to compare anything to it or evaluate using it or anything. You just count backwards from 60 by one each frame the script is running...?
Oops, I forgot to remove that part when posting the code, it was originally for clearing the console window and display new information every 60th frame. But I decided to remove that part to shorten the code.
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
So, question. Is there a function for reading specific bytes from the file on disk, or should I just use the lua IO functions? Alternatively, is there a lua function that exposes the SNES header?
Adventures in Lua When did I get a vest?
Amaraticando
It/Its
Editor, Player (162)
Joined: 1/10/2012
Posts: 673
Location: Brazil
Pokota wrote:
So, question. Is there a function for reading specific bytes from the file on disk, or should I just use the lua IO functions?
You should use the complete Lua I/O model. If I understood your question correctly, you wanna grab the content of the nth byte of a file. You should use the seek method. According Ierusalimschy's book, Programming in Lua:
The seek method can both get and set the current position of a file. Its general form is f:seek(whence,offset) , where the whence parameter is a string that specifies how to interpret the offset. Its valid values are “ set ”, when offsets are interpreted from the beginning of the file; “ cur ”, when offsets are interpreted from the current position of the file; and “ end ”, when offsets are interpreted from the end of the file. Independently of the value of whence , the call returns the new current position of the file, measured in bytes from the beginning of the file.
Pokota wrote:
Alternatively, is there a lua function that exposes the SNES header?
Please, explain. You wanna a function that returns true if the ROM is headered? Wanna read its contents? AFAIK, in BizHawk, there's no way to do it beautifully. Maybe using gameinfo.getromname().
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
Reading the contents of the header is more what I had in mind. I'll take a refresher course on Lua I/O, then, since that way I can do other neat things.
Adventures in Lua When did I get a vest?
Masterjun
He/Him
Site Developer, Expert player (2047)
Joined: 10/12/2010
Posts: 1185
Location: Germany
I'd always link the actual official documentation (this is for version 5.1, which all emulators except lsnes are still stuck on, for 5.3 check here). Make sure to use a 'b' character at the end of the mode when you open the file, to properly open it in binary mode.
Warning: Might glitch to credits I will finish this ACE soon as possible (or will I?)
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
In this specific instance I found the header in the System Bus domain, so I have a short-term workaround. Meanwhile, I've run into a new issue - I created a mode switcher but it throws an unhandled exception when I try to switch modes. Can I get a second pair of eyes to check for something stupid that I may have missed? The code is on github since it kinda would have eaten the full page.
Adventures in Lua When did I get a vest?
Masterjun
He/Him
Site Developer, Expert player (2047)
Joined: 10/12/2010
Posts: 1185
Location: Germany
The forms.button function wants a function as the 3rd argument. You are doing:
Language: lua

emptyMode = forms.button(controls, "Default", setMode("none"), 5, 5)
However setMode("none") is not a function, it's an expression that calls a function. So either create a whole new function calling the setMode function, or do something like this:
Language: lua

emptyMode = forms.button(controls, "Default", function() setMode("none") end, 5, 5)
Warning: Might glitch to credits I will finish this ACE soon as possible (or will I?)
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
Thanks. I figured it was a simple something I'd forgotten about.
Adventures in Lua When did I get a vest?
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
A (temporary?) lua function that pulls ascii strings from memory. In practice, this is a wrapper for memory.readbyterange
Language: lua

function readAsAscii(address, length, domain) ret = "" q = memory.readbyterange(address-1, length+1, domain) for a,b in next,q do ret = ret .. string.char(b) end ret = bizstring.substring(ret,0,length) return ret end
address and length are adjusted by the wrapper to account for lua being 1-indexed while readbyterange returns a 0-indexed table.
Adventures in Lua When did I get a vest?
Masterjun
He/Him
Site Developer, Expert player (2047)
Joined: 10/12/2010
Posts: 1185
Location: Germany
Then, instead of using some weird for-loop, how about just accessing the 0-indexed table by using 0?
Language: lua

q = memory.readbyterange(address, length, domain) for i=0,length-1 do ret = ret .. string.char(q[i]) end return ret
Warning: Might glitch to credits I will finish this ACE soon as possible (or will I?)
Pokota
He/Him
Joined: 2/5/2014
Posts: 779
Because that didn't occur to me - I was stuck in thinking using pairs was the only way to iterate reliably through the returned table since I couldn't know the bounds. Despite the upper bound being explicitly passed in the function call. It's really a good thing that I do this thread, since as it turns out y'all are pretty good at programming.
Adventures in Lua When did I get a vest?
Amaraticando
It/Its
Editor, Player (162)
Joined: 1/10/2012
Posts: 673
Location: Brazil
Caution with big loops of concatenations, they aren't a good idea in Lua. I didn't test this snippet because I don't have BizHawk. https://www.lua.org/manual/5.3/manual.html#pdf-table.concat
Language: lua

function readAsAscii(address, length, domain) local q = memory.readbyterange(address, length, domain) -- what if address == 0? table.move(q, 0, #q, 1, q) -- this shifts the elements of q to be 1-based, not sure about performance q[0] = nil for index, byte in ipairs(q) do q[index] = string.char(byte) end return table.concat(q) end
TOHN
He/Him
Joined: 1/11/2017
Posts: 7
Hi, Now I am working on my AI project and looking for a way to save a state for every frame of the gameplay in order to resume to any state I want without running from the start of the movie. Any pointer to an existing project or a lua script will be greatly appreciated.
Editor, Player (175)
Joined: 4/7/2015
Posts: 331
Location: Porto Alegre, RS, Brazil
TOHN wrote:
looking for a way to save a state for every frame of the gameplay
It depends on which emulator you're using, but I guess it would be simple for BizHawk and Lsnes (for example):
Language: lua

while true do --[[ YOUR CODE OR FUNCTION CALLS HERE --]] local savestate_name = string.format("bla bla bla %d", emu.framecount()) -- "bla bla bla" is anything you want, like your AI's name savestate.save(savestate_name) -- IF BIZHAWK exec("save-movie " .. savestate_name .. ".lsmv") -- IF LSNES emu.frameadvance() end
Tho, I'm not sure if BizHawk's savestate.save(string path) needs the full path for the file or it can save in .\[console]\State Edit: savestate.save(string path) saves in the same folder of your script, so you can make:
Language: lua

local savestate_name = string.format("AI_savestates\\bla bla bla %d", emu.framecount())
Games are basically math with a visual representation of this math, that's why I make the scripts, to re-see games as math. My things: YouTube, GitHub, Pastebin, Twitter
TOHN
He/Him
Joined: 1/11/2017
Posts: 7
brunovalads wrote:
TOHN wrote:
looking for a way to save a state for every frame of the gameplay
It depends on which emulator you're using, but I guess it would be simple for BizHawk and Lsnes (for example):
Language: lua

while true do --[[ YOUR CODE OR FUNCTION CALLS HERE --]] local savestate_name = string.format("bla bla bla %d", emu.framecount()) -- "bla bla bla" is anything you want, like your AI's name savestate.save(savestate_name) -- IF BIZHAWK exec("save-movie " .. savestate_name .. ".lsmv") -- IF LSNES emu.frameadvance() end
Tho, I'm not sure if BizHawk's savestate.save(string path) needs the full path for the file or it can save in .\[console]\State Edit: savestate.save(string path) saves in the same folder of your script, so you can make:
Language: lua

local savestate_name = string.format("AI_savestates\\bla bla bla %d", emu.framecount())
Thank you very much, brunovalads. It works like a charm. Yes, I am using BizHawk.
Lil_Gecko
He/Him
Player (98)
Joined: 4/7/2011
Posts: 520
Is it possible to use Lua to record an AVI file ? To be more specific, I would like that Bizhawk dumps the AVI only when a certain address is not equal to 1 for example. Stop dumping when it turns to 1, then continue dumping afterwards. I can't seem to find a function for that, so I was wondering.
Site Admin, Skilled player (1255)
Joined: 4/17/2010
Posts: 11495
Location: Lake Char­gogg­a­gogg­man­chaugg­a­gogg­chau­bun­a­gung­a­maugg
Have you even tried? http://tasvideos.org/Bizhawk/LuaFunctions.html client.pause_av()
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
1 2
9 10 11 12 13 14