Lua stack overflow in recursive function in corona sdk - lua

For some reason the code below throws a stack overflow error if the else statement gets executed too many times. I am trying to have the scene.targeting function select a target from the objTable passed in the params, but only targets with a .tgtFlag == false are valid selections. If the function selects a target that has a .tgtFlag == true, it recalls the scene.targeting function passing in the same set of params.
The line that breaks is local theTarget = params.objTable[math.random( 1, #params.objTable )] but only after else scene.targeting(params) end is called several times.
Any help would be greatly appreciated.
function scene.targeting( params ) -- Targeting functions
function animateTarget( target )
if target.savedFlag == false then
transition.to( target, {time = 100, y = target.y - 15} )
transition.to( target, {time = 100, delay = 150, y = target.y, onComplete = animateTarget} )
end
end
local theTarget = params.objTable[math.random( 1, #params.objTable )]
if theTarget.tgtFlag == false then
theTarget.tgtFlag = true
animateTarget(theTarget)
else
scene.targeting(params)
end
end

Referring to Programming in Lua:
A tail call is a kind of goto dressed as a call. A tail call happens when a function calls another as its last action, so it has nothing else to do.
In your example, animateTarget obviously isn't called in such a way, and can and will suffer from stack overflows. Either rewrite it to make use of TCO or change it to non-recursive version.

Related

LUA using function variable outside of it

I am trying to edit a script in LUA but I couldn't get access of a local defined in a function
LUA Code
function getSafeMoney()
local SafeMoney = nil
QBCore.Functions.ExecuteSql(false, 'SELECT * FROM `moneysafes` WHERE `safe` = "mechanic"', function(result)
SafeMoney = json.decode(json.encode(result[1])).money;
end)
return SafeMoney
end
print(getSafeMoney())
Result :
nil
here's the sql function as sysdevs asked
QBCore.Functions.ExecuteSql = function(wait, query, cb)
local rtndata = {}
local waiting = true
exports['ghmattimysql']:execute(query, {}, function(data)
if cb ~= nil and wait == false then
cb(data)
end
rtndata = data
waiting = false
end)
if wait then
while waiting do
Citizen.Wait(5)
end
if cb ~= nil and wait == true then
cb(rtndata)
end
end
return rtndata
end
Your second function is not holding the main function I assume, I am bad at lua aswell, but I think that changing
QBCore.Functions.ExecuteSql(false, ............
to
QBCore.Functions.ExecuteSql(true, ............
This will probably fix your problem, still your code is ambiguous and needs concentration if you could provide more information I might be able to help more

Tarantool fiber behavior with fiber.yield() and fiber.testcancel()

I ran into an unexpected behavior while building Tarantool app based on fibers.
Simple reproducer of my code looks like this:
local log = require('log')
local fiber = require('fiber')
box.cfg{}
local func = function()
for i = 1, 100000 do
if pcall(fiber.testcancel) ~= true then
return 1
end
fiber.yield()
end
return 0
end
local wrapfunc = function()
local ok, resp = pcall(func)
log.info(ok)
log.info(resp)
end
for _ = 1, 100 do
local myfiber = fiber.create(wrapfunc)
fiber.sleep(0.02)
fiber.kill(myfiber)
end
and it prints to log false, fiber is cancelled. Moreover, if I use the following func:
local func = function()
for i = 1, 100000 do
if pcall(fiber.testcancel) ~= true then
return 1
end
pcall(fiber.yield)
end
return 0
end
it prints to log true, 1, and if I use
local func = function()
for i = 1, 100000 do
if pcall(fiber.testcancel) ~= true then
return 1
end
if pcall(fiber.yield) ~= true then
return 2
end
end
return 0
end
it prints to log true, 2.
I expected that after yielding from running myfiber, if control returns to the external fiber and it calls fiber.kill(myfiber), the next time control returns to the cancelled myfiber we will be in the end of cycle iteration and on the next iteration code will successfully return 1. However, the work of func ends with throwing error fiber is cancelled, not with return. So how the real life cycle of yielding fiber works?
Actually, there is no unexpected behaviour here. I believe it mostly documentation issue. Let me explain. I've simplified your example a bit:
#!/usr/bin/env tarantool
local fiber = require('fiber')
local f1 = function() fiber.yield() end
local f2 = function() pcall(fiber.yield) end
local func = function(fn)
fn()
if not pcall(fiber.testcancel) then
return 'fiber.testcancel() failed'
end
end
local fiber1 = fiber.create(function() print(pcall(func, f1)) end)
fiber.kill(fiber1)
local fiber2 = fiber.create(function() print(pcall(func, f2)) end)
fiber.kill(fiber2)
The output would be:
false fiber is cancelled
true fiber.testcancel() failed
When you call fiber.kill, fiber.yield() or fiber.sleep() just raises an error, so your fiber is not able to reach fiber.testcancel and just dies. When you do pcall(fiber.yield), you basically suppress this error and proceed. Then fiber.testcancel checks its fiber status and reraise an exception. But this is a silly example.
Now, with bigger chunks of code, when lots of function invocations involved, you usually want to catch those errors during yield, do some finalisation work and call fiber.testcancel() to promote error upwards (imagine multiple checks of this kind in different parts of big stacktrace). I believe it is the basic use case, fiber.testcancel was introduced for, besides discussions if its design is usable or not.
P.s. And yes, occasional exceptions of such yield calls are not documented. At least I could not find anything in the fiber page

Different methods for determining whether a number is included in a given list

Is it faster to check whether a number is equivalent to another, or to look up the number in a table?
I have a program where commands sent from a server are received as numbers. Commands are sent frequently: anywhere from 1/second to 30+/second. Certain commands are ignored while others trigger various events. Would it be better to determine which event to trigger by doing this:
function incoming_integer(n)
if n == 33 then
print('a')
elseif n == 44 then
print('b')
elseif n == 567 then
print('c')
... (the actual list is occasionally pretty long: upwards of ten valid numbers)
end
end
or this:
functions = {
[33] = function() print('a') end,
[44] = function() print('b') end,
[567] = function() print('c') end,
...
}
relevant_commands = {[33]= true, [44]=true, [567]=true ...}
function incoming_integer(n)
if relevant_commands[n] then
functions[n]()
end
end
Is there a point at which one method becomes more efficient?
What if the commands were instead sent as strings?
For your throughput, I would suggest that the overhead for either mechanism is likely to be negligible.
I ran very quick and dirty test of the overhead of the table lookup for both string and numeric keys. See below. Even with a large number of keys and a high hit rate, I struggled to get >1us overhead even on the slowest box I could find.
f = {}
function incoming_integer(n)
local t = f[n]
return t and t()
end
SIZE = 10000
RAN = 20000
REPEAT = 10000000
for i =1,SIZE do
--f[math.random(RAN)] = function() return "z" end
f[tostring(math.random(RAN))] = function() return "z" end
end
tm = os.time()
for i = 1,REPEAT do
--math.random(RAN)
tostring(math.random(RAN))
end
print(os.difftime(os.time(),tm))
tm = os.time()
for i = 1,REPEAT do
--incoming_integer(math.random(RAN))
incoming_integer(tostring(math.random(RAN)))
end
print(os.difftime(os.time(),tm))
If you want to trigger different events with the returned numbers, I would use this way...
function trigger( value )
if ( value == "..." ) then
triggerThis()
elseif ( value == "..." ) then
triggerElse()
-- and so on
end
end
function incoming_integer(n)
if ( n ) then -- check if variable is incoming to prevent errors
trigger( n )
else
alert( "noop, there is nothing here" )
end
end
Maybe you could try keys. Didn't tested it just an example.
http://lua-users.org/wiki/TablesTutorial
table = { [33]="a", [44]="b", [567]="c" }
function onCall( value ) -- if 33
if ( table[value] ) then
return( table[value] ) -- then if exists return a
end
end
onCall( 33 ) -- now it will search in table and get key
EDIT: You could name the triggered functions like the number, so you can directly trigger them. This would save a lot of time!
functions = {[33] = function() print('a') end, etc...}
setmetatable(functions, {__index = function() return function() end end}) -- return empty function if no defined function is found
Usage:
functions[32]() -- ignores
functions[33]() -- does defined function

Corona SDK 'nil value' issue

I am making a shooter game using the corona sdk...
My problem is that i am trying to delete an object(that is part of an array)when it leaves the screen.. when i do, i get an error that says 'trying to compare nil value to variable' which refers to a simple move function for every object in the array. Here is relevant parts of code:
function addAlien()
listeners('add')
end
function listeners(action)
if(action == 'add') then
Runtime:addEventListener('enterFrame',update)
enemyTimer = timer.performWithDelay(800,addEnemy,0)
else
Runtime:removeEventListener('enterFrame',update)
timer.cancel(enemyTimer)
end
end
function addEnemy(e)
enemy = display.newImage('drone.png')
enemy.x = 500
enemy.y = math.floor(math.random()*300)
enemy:scale(-0.1,0.1)
enemy.speed = math.random(2,6)
enemies.insert(enemies,enemy)
enemy.enterFrame = moveEnemy
Runtime:addEventListener('enterFrame',enemy)
end
function moveEnemy(self,event)
--if self.x < 100 then
---self:removeSelf()
--self = nil
--removeSelf()
--else
self.x = self.x-self.speed
--end
end
function update(e)
if(enemies.numChildren ~= 0)then
for i = 1,enemies.numChildren do
if(enemies[i] ~= nil)then
--enemies[i].x = enemies[i].x-3
if(enemies[i].x<100)then
--enemies:remove(enemies[i])
--display.remove(enemies[i])
--enemies[i] = nil
end
end
end
end
end
I have commented out the parts that give me errors.
Any help would be appreciated,
thanks
You might want to try and put the delete code in it's own function and then use a timer to remove it so that the function where you are currently deleting the object can return and you're not deleting yourself.
Another option would be to make it temporarily invisible and then loop through the table periodically and remove anything outside of the move handler.
You have a problem with the following code:
function listeners(action)
if(action == 'add') then
Runtime:addEventListener('enterFrame',update)
enemyTimer = timer.performWithDelay(800,addEnemy,0)
else
Runtime:removeEventListener('enterFrame',update)
timer.cancel(enemyTimer)
end
end
You only want to Runtime:addEventListener('enterFrame', update) once. If you do it everytime you add an enemy things will go wrong. The problem is your code runs again and again after the objects have been removed already.
hmmm. did you use the scene template? if so, you should only put the :removeself () and = nil values in the scene destroy section at the bottom. you also dont need the second removeself that is not attached to an object as that is probably the nil issue.
to summarize.
1-put all remove self () and = nil at the destroy section of scene template and use object.isVisible = false instead.
2-the nil error is most likely coming from the removeself() statement with no object. so change it to moveenemy:removeself ()
if my understanding is right this is how i would do it without using runtime
and using timer instead. ask away if you have question
local scrWidth = display.actualContentWidth
local scrHeight = display.actualContentHeight
local enemy = {} --this will hold your aliens
function addEnemy()
enemy[#enemy + 1] = display.newImage("drone.png")
enemy[#enemy].x = 500
enemy[#enemy].y = math.floor(math.random()*300)
enemy[#enemy]:scale(-0.1,0.1)
enemy[#enemy].speed = math.random(2,6)
end
local function update()
addEnemy()
--this will move the enemy to the left from right
for i=1,#enemy,1 do
enemy[i].x = enemy[i].x - enemy[i].speed
end
--the below codes will destroy each enemy that is out side the screen on the left
local function destroyWhenOutside()
for i=1,#enemy,1 do
if enemy[i].x < 0 - enemy[i].width then
enemy[i]:removeSelf()
enemy[i] = nil
elseif enemy[i].y < 0 - enemy[i].height then
enemy[i]:removeSelf()
enemy[i] = nil
end
end
end
destroyWhenOutside()
end
--this will loop the update every 1/1000 seconds
local timerUpdate = timer.performWithDelay(1,update,-1)
.

How to move an object randomly with transition.to?

I'm trying to move an object randomly to different locations, so I came out with the following: transition.to generates the x,y randomly as well as the time, and on finish runs another function which checks if the object is still there and sends it to a different location.
but I'm getting an error:
Runtime error
main.lua:352: stack overflow
stack traceback:
main.lua:352: in function
'toAnotherPlace'
looks like corona doesn't really waits for transition complete, so it goes on infinite loop
code
function toAnotherPlace(object)
if object ~= nil then
transition.to( object,
{
time=math.random(1500,6000),
alpha=1,
x=(math.random(10, 310)),
y=(math.random(10, 400)),
onComplete=toAnotherPlace(object)
})
end
end
transition.to( bossess[boss],
{
time=math.random(1500,6000),
alpha=1,
x=(math.random(10, 310)),
y=(math.random(10, 400)),
onComplete=toAnotherPlace(bossess[boss])
})
You can try this, I added an onComplete = function() ... end and I call the toAnotherPlace(object) function inside it.
I think it's a bug if you directly call a function on onComplete
function toAnotherPlace(object)
print(object.width)
if object ~= nil then
transition.to( object,
{
time = math.random(1500,6000),
alpha = 1,
x = math.random(10, 310),
y = math.random(10, 400),
onComplete = function()
toAnotherPlace(object)
end
})
end
end
transition.to(bossess[boss],
{
time = math.random(1500,6000),
alpha = 1,
x = math.random(10, 310),
y = math.random(10, 400),
onComplete = function()
toAnotherPlace(bossess[boss])
end
})
I tried this and is working fine, no errors.
If you still getting errors, check the bossess[boss] if there is a reference to your object

Resources