Ack! OK... I've run into a really weird problem that I'm utterly stumped how to resolve (or even troubleshoot further), and was hoping for some more input. I've nearly got my glorious upgrade of a co-operative-friendly-FSPort-campaign ready to roll, but this issue is turning into a huge blocker.
Basically, after about 15 animations play, I suddenly start getting nil values returned from my variables. Here's an example error that's thrown:
---------------------------
Error!
---------------------------
LUA ERROR: [string "scripting.tbl - On HUD Draw"]:15: attempt to perform arithmetic on global 'totalframes' (a nil value)
------------------------------------------------------------------
ADE Debug:
------------------------------------------------------------------
Name: (null)
Name of:
Function type: main
Defined on: 0
Upvalues: 0
Source: scripting.tbl - On HUD Draw
Short source: [string "scripting.tbl - On HUD Draw"]
Current line: 13
- Function line: 0
------------------------------------------------------------------
------------------------------------------------------------------
stack traceback:
[C]: ?
[string "scripting.tbl - On HUD Draw"]:15: in main chunk
------------------------------------------------------------------
------------------------------------------------------------------
[ This info is in the clipboard so you can paste it somewhere now ]
Use Yes to break into Debugger, No to continue.
and Cancel to Quit
---------------------------
Yes No Cancel
---------------------------
"totalframes" is a variable in my script that gets populated with the number of frames in the animation I'm loading. Errors involving a nil value can occur if I pass it an invalid file to process... except I'm not. I've thrown debug notices just ahead of the execution and can confirm it's getting a valid file to process. Everything just BEFORE that step in the execution gets lined up fine, but when it hits "totalframes", suddenly it thinks the data is nil / invalid.
And this suddenly only happens after the same animation script has already been called several times and has worked fine (in a particular test case that I'll attach, it's the 16th execution that causes this).
What's up - am I tripping on some kind of obscure garbage cleanup routine? Is there some kind of weird overflow happening with my data? Am I stuffing one of my variables or arrays with too much data to process?
I don't have any command briefings in my co-op campaign which in themselves involve sixteen stages - but over the course of a continuous gameplay session and multiple command briefing segments, you'll eventually hit this limit and break the script. The only way to fix it is to restart the game - no new animations called from the script will properly appear until you do this.
I've back-ported all of my work into a small sample that can be easily dropped and tested into a base FSOpen install just in case you want to see this for yourselves - this includes an exaggerated mission that will load 16 cbanims, in sequence. Here it is:
http://illuminatihq.net/mp-cbanim.rarTo repro: Stick Blank.fs2 into your data\missions folder, cbanims.txt into your data\scripts folder, and scripting.tbl (which of course is the actual script in question) into your data\tables folder. Load FSOpen (i.e. fs2_open_3_7_2_RC3-DEBUG), start a new multiplayer co-op session, and load the "CrashTestDummy" single mission.
Step through the command briefings (hit "T" to advance the stage, as per on-screen instructions) - as each loads, you should get a pop-up window confirming which anim file is about to be loaded. When it hits the 16th, you'll see it tries to load it, then throws the error about "totalframes" being nil and it'll refuse to render any further anims. You can bomb out of the mission and retry it to get the same result (all previously-loaded animations will play until it again hits the 16th). If you want to confirm that the file being loaded *is* valid, feel free to open cbanims.txt and switch the order of the files around; no matter what file it is, it'll throw the same error when you hit the 16th stage and break.
In short: Zah?
Just for a quick at-a-glance reference, here's a copy of the latest version of the script I'm using (same as in the linked sample download, minus a couple of pages of "howto" comments)
#Conditional Hooks
$Application: FS2_Open
$On Game Init:
[
readfile = {} --Sets up our table and gets it ready to store strings.
readfile.strings = {}
readfile.filename = "cbanims.txt" --We're going to be pulling our strings from "cbanims.txt"
screenx = gr.getScreenWidth()
screeny = gr.getScreenHeight() --Get our current screen resolution. Used for image scaling and placement purposes.
if cf.fileExists(readfile.filename, "data/scripts",true) then --Confirms the existence of, then opens cbanims.txt in the data\scripts sub-folder.
local file = cf.openFile(readfile.filename,"r","data/scripts")
local i = 1
while true do
local entry = file:read("*l")
if entry == nil then break end
readfile.strings[i] = entry --Store indexed lines from cbanims.txt in readfile.strings
i=i+1
end
file:close()
end
readfile.showcbanim = function(line) --Function called from FRED for showing our anim - uses the line number in cbanims.txt as an argument
local t = readfile.strings[line]
if t then
ba.warning("Attempting to render " ..t.. " ") --Blast a debug notice confirming what file we're loading (confirm data is being read correctly)
cbanim = gr.loadTexture(""..t.."",true) --Pull what anim resource to use from cbanims.txt and store it in cbanim
totalframes = cbanim:getFramesLeft() --Magic 3 lines to make the animation animate
animfps = cbanim:getFPS()
starttime = mn.getMissionTime()
else
ba.warning("readfile.showcbanim() has just attempted to load line "..line.." from "..readfile.filename.." and failed. Most likely this line or the file does not exist.") --Give a debug warning if the table entry is empty
end
end
readfile.showcboverlay = function(line) --Function called from FRED for showing our command-briefing overlay - uses the line number in cbanims.txt as an argument
local t = readfile.strings[line]
if t then
--ba.warning("Attempting to render " ..t.. " ") --Blast a debug notice confirming what file we're loading (confirm data is being read correctly)
cboverlay = gr.loadTexture(""..t.."",true) --Pull what anim resource to use from cbanims.txt and store it in cbanim
else
ba.warning("readfile.showcboverlay() has just attempted to load line "..line.." from "..readfile.filename.." and failed. Most likely this line or the file does not exist.") --Give a debug warning if the table entry is empty
end
end
]
$On HUD Draw:
[
if cboverlay ~= nil then --If readfile.showcboverlay has been called and has populated the "cboverlay" variable, display the command-briefing overlay.
local x = (screenx - cboverlay:getWidth())
local y = (screeny - cboverlay:getHeight()) --Stretch it across the entire screen
gr.drawImage(cboverlay, 0, 0, screenx, screeny) --Play our animation
end
stopcboverlay = function() --FRED-called function to stop drawing the command-briefing overlay. REMEMBER TO CALL THIS BEFORE ENDING YOUR BRIEFING MISSION.
cboverlay:unload()
cboverlay = nil
end
if cbanim ~= nil then --If readfile.showcbanim has been called and has populated the "cbanim" variable, display the command-briefing animation.
local animtime = mn.getMissionTime() - starttime
--local i = animtime * animfps
local i = (animtime * animfps) % totalframes
if i >= totalframes then
cbanim = nil
else
local x = (screenx - cbanim:getWidth()) / 2 - 120
local y = (screeny - cbanim:getHeight()) / 2 + 200 --Make this appear in the appropriate box towards lower-left-ish corner of the screen. TODO: Adjust for proper resolution scaling!
gr.drawImage(cbanim[i],x, y) --Play our animation
end
end
stopcbanim = function() --FRED-called function to stop drawing the command-briefing animation. REMEMBER TO CALL THIS BEFORE ENDING YOUR BRIEFING MISSION.
cbanim:unload()
cbanim = nil
end
if (mn.SEXPVariables['playanim']:isValid()) then --Ignore all these, chaos happened here. TODO: Clean up the chaos
if (mn.SEXPVariables['playanim'].Value) > 0 then
ba.warning("Trying to process your variable dood")
readfile.showcbanim(mn.SEXPVariables['playanim'].Value)
mn.SEXPVariables['playanim'].Value = 0
end
end
]
#End
Any ideas? Hope I'm not missing something obvious
Going to experiment a bit more with some of the unloading functions - I already tried invoking the "unload" function for cbanim (and setting it to nil) for the first step of every stage of the briefing, and while this fixes a potential long-term memory usage problem (and is something I should do for the final product), it doesn't seem to make any difference as far as this issue goes.