Vars within function are not reset per call (LUA) - function

I am trying to set a function to add min to current time object and return a new time object. I created a function for this but for some reason the vars in the functions are not been re-set / local each time I call the function.
each call to the function will use the past value of the local vars within the function, why ?
local function AddTime (MinAfter, BaseTime)
if (MinAfter == nil) then MinAfter = 0 end
if (BaseTime == nil) or (BaseTime.min == nil) or (BaseTime.hour == nil) then BaseTime = os.date("*t") end
BaseTime.hour = BaseTime.hour + math.floor((BaseTime.min + MinAfter)/60)
BaseTime.min = BaseTime.min + MinAfter - (60 * (math.floor((BaseTime.min + MinAfter)/60)))
if BaseTime.hour > 24 then BaseTime.hour = 24 end
return BaseTime
end
local sunriseHour = os.date("*t" ,os.time {year = 2014, month = 4, day = 19, yday = 259, wday = 4, hour = 6, min = 0, sec = 0, isdst = false});
-- this is the original time object in this case sunraiseHour
print ("sunriseHour time:" .. (string.format("%02d",sunriseHour.hour) .. ":" .. string.format("%02d", sunriseHour.min)));
-- first call
local newtime1= AddTime(10, sunriseHour);
print ("call 1 time:" .. string.format("%02d", newtime1.hour) .. ":" .. string.format("%02d", newtime1.min));
-- on the 1st call I get 07:10 which is right
-- 2nd call
local newtime2= AddTime(10, sunriseHour);
print ("call 1 time:" .. string.format("%02d", newtime2.hour) .. ":" .. string.format("%02d", newtime2.min));
-- on the 2nd call I get 07:20 and not 07:10 since this was the 2nd call to the function - the BaseTime var within the function was not local

When you pass sunriseHour into AddTime, it is passed by reference rather than by value, which means that any changes made to BaseTime inside of AddTime are changes to sunriseHour -- both varibles (sunriseHour and BaseTime) point to the same object.
So when you write the following in AddTime:
BaseTime.hour = BaseTime.hour + math.floor((BaseTime.min + MinAfter)/60)
BaseTime.min = BaseTime.min + MinAfter - (60 * (math.floor((BaseTime.min + MinAfter)/60)))
You're modifying sunriseHour.
It seems you don't quite understand this, because you also assign a new value to BaseTime inside of AddTime, which suggests you think you have a new object. If you want to create an altered copy of sunriseHour, then you'll need to either do that inside of AddTime, or create some kind of copy constructor for your time object.

thanks Mud,
I understood that I needed to copy my time object otherwise since I use a reference of it, it will modify the original object.
I found a copy function and use it to copy the object into a local one within the function
thanks
function table.copy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in pairs(orig) do
copy[orig_key] = orig_value
end
else -- number, string, boolean, etc
copy = orig
end
return copy
end
function AddTime (MinAdd, TimeObj)
local BaseTime = {};
local MinAfter = 0;
if (TimeObj == nil) or (TimeObj.min == nil) or (TimeObj.hour == nil) then BaseTime = table.copy(os.date("*t")) else BaseTime = table.copy(TimeObj) end;
if (MinAdd == nil) then MinAfter = 0 else MinAfter = MinAdd end;
BaseTime.hour = BaseTime.hour + math.floor((BaseTime.min + MinAfter)/60)
BaseTime.min = BaseTime.min + MinAfter - (60 * (math.floor((BaseTime.min + MinAfter)/60)))
if BaseTime.hour > 24 then BaseTime.hour = 24 end
return BaseTime
end
-- this is the original time object in this case sunraiseHour
local sunriseHour = os.date("*t" ,os.time {year = 2014, month = 4, day = 19, yday = 259, wday = 4, hour = 6, min = 0, sec = 0, isdst = false});
print ("sunriseHour time:" .. (string.format("%02d",sunriseHour.hour) .. ":" .. string.format("%02d", sunriseHour.min)));
-- first call
local newtime1= AddTime(10,sunriseHour);
print ("call 1 time:" .. string.format("%02d", newtime1.hour) .. ":" .. string.format("%02d", newtime1.min));
-- on the 1st call I get 07:10 which is right
-- 2nd call
local newtime2= AddTime(10,sunriseHour);
print ("call 2 time:" .. string.format("%02d", newtime2.hour) .. ":" .. string.format("%02d", newtime2.min));
-- on the 2nd call I get 07:20 and not 07:10 since this was the 2nd call to the function - the BaseTime var within the function become global
print ("Added time:" .. string.format("%02d", AddTime(20, sunriseHour).hour) .. ":" .. string.format("%02d", AddTime(20, sunriseHour).min));

Related

using a function from an outside script in love2d gives me an error

I'm making an action RPG in Love2d, and I moved all of my player code into a separate Lua script for the sake of organization - but now upon attempting to use player.load, I get this error:
Error
main.lua:22: attempt to index global 'player' (a boolean value)
Traceback
main.lua:22: in function 'load'
[C]: in function 'xpcall'
[C]: in function 'xpcall'
this is my main.lua script:
-- RPG PROJECT IN LOVE2D
-- debug mode
debug = true -- SET FALSE BEFORE SHIPPING
-- ROOM CHART:
-- 0 - Title
-- 1 - Overworld
-- 2 - Shop
-- 3 - Boss Room
Room = 0
-- PLAYER STATE CHART:
-- 0 - free
-- 1 - attacking
-- 3 - can't move
function love.load(dt)
player = require 'Scripts/player'
player.load()
sti = require 'Libraries/sti'
gameMap = sti('Maps/overworld.lua')
menuImg = love.graphics.newImage('Assets/Menu.png')
love.window.setMode(800, 600)
end
function love.draw(dt)
if Room == 0 then
love.graphics.draw(menuImg, 0, 0)
end
if Room == 1 then
gameMap:draw()
player.draw()
end
if debug == true then
love.graphics.print("Current FPS: "..tostring(love.timer.getFPS( )), 10, 10)
end
end
function love.update(dt)
if Room == 0 then
if love.keyboard.isDown('space') then
Room = 1
end
end
if Room == 1 then
player.Update(dt)
if love.keyboard.isDown('escape') then
Room = 0
end
end
if love.keyboard.isDown('t') then
debug = not debug
end
end
and this is my player.lua script:
-- PLAYER SCRIPT
player = {x = 50, y = 50, speed = 3, state = 0, img = nil}
function player.load()
playerRight = love.graphics.newImage('Assets/playerRight.png')
playerLeft = love.graphics.newImage('Assets/playerLeft.png')
playerUp = love.graphics.newImage('Assets/playerUp.png')
playerDown = love.graphics.newImage('Assets/playerDown.png')
player.img = playerDown
end
function GetInput()
if player.state == 0 or player.state == 1 then
if love.keyboard.isDown('w') then
player.y = player.y - player.speed
player.img = playerUp
elseif love.keyboard.isDown('s') then
player.y = player.y + player.speed
player.img = playerDown
end
if love.keyboard.isDown('a') then
player.x = player.x - player.speed
player.img = playerLeft
elseif love.keyboard.isDown('d') then
player.x = player.x + player.speed
player.img = playerRight
end
end
end
function player.update(dt)
GetInput()
end
function player.draw()
love.graphics.draw(player.img, player.x, player.y)
end
Any help would be much appreciated!
Here's my folders as well, just in case it's a path issue:
UPDATE:
I solved it by renaming the player object to oPlayer, that was what was giving me an error.
I have severall Hints for you
Unclear
It looks you write it under Linux but should it also run under Windows?
If so, use OS independent folder delimiter ( the dot ) in require().
Example
player = require 'Scripts.player'
In General: Dont use Slash / Backslash \ in require()
Tip: LÖVE uses LuaJIT with modified Lua 5.1 check the Windows OS is easy
(For deciding to use Slash or Backslash (not in/for require()))
Clear
As #Luke100000 mentioned...
A Script for require has to return something.
If not than only a true wil be cached and a next require returns only true.
Therefore your player.lua content should be...
-- player.lua
local player = {x = 50, y = 50, speed = 3, state = 0, img = nil}
-- player.load() will be local
-- GetInput() wil be global
-- And the last line...
return(player)

Can I run a function for the Lua shell prompt?

I can set the prompt with _PROMPT = "> ", but can I make the prompt update every time?
I tried this, but it doesn't work:
i = 0
function inc()
i = i + 1
return i
end
_PROMPT = inc
This shows _PROMPT, but nothing related:
for k, v in pairs(_G) do
print(k)
end
The primary prompt is the value of the global variable _PROMPT, if this value is a string; otherwise, the default prompt is used.
https://www.lua.org/manual/5.1/lua.html
You assigned a function to _PROMPT.
I tried
_PROMPT = {no = 0}; setmetatable (_PROMPT, {__tostring = function (self) self.no = self.no + 1; return tostring (self.no) .. ' >' end})
, but no luck, although _PROMPT was incremented every time I typed = _PROMPT.
UPD Yet this can be done! In Lua mailing list I was advised to use
setmetatable(_ENV, {__index = function(t, k) if k == '_PROMPT' then t._N = (t._N or 0) + 1; return t._N .. ' >' end end})
It works. This effectively is a way to override any global in a deeper sense than simply to assign a new value to it, or make a set of global variables effectively infinite.
You can start Lua in interactive mode with a one-liner:
lua -i -e "setmetatable(_ENV, {__index = function(t, k) if k == '_PROMPT' then t._N = (t._N or 0) + 1; return t._N .. ' >' end end})"
I found out a method with debug.sethook().
It sounds a bit strange but it is really simple ;-)
Here we go...
# /usr/bin/lua -i
Lua 5.3.5 Copyright (C) 1994-2018 Lua.org, PUC-Rio
> debug.sethook(function(...) _PROMPT=os.date('%H:%M:%S # ') end,'r')
10:49:42 # -- Hiting ENTER some times
10:51:00 #
10:51:01 #
10:51:05 #
( Done with Lua 5.3.5 and tested 5.4 - Should work with 5.1 but not tested )
The 'r' means: Fire on each return
EDIT
Another way directly with _PROMPT and _PROMPT2...
-- Simple method for changing and/or time logging the prompts
_PROMPT=setmetatable({},{__index=table})
_PROMPT2=setmetatable({},{__index=table})
getmetatable(_PROMPT).__tostring=function()
_PROMPT:insert(os.date('%H:%M:%S',os.time())..'> ')
return string.format('%s',_PROMPT:concat(#_PROMPT,#_PROMPT))
end
getmetatable(_PROMPT2).__tostring=function()
_PROMPT2:insert(os.date('%H:%M:%S',os.time())..'>> ')
return string.format('%s',_PROMPT2:concat(#_PROMPT2,#_PROMPT2))
end
...using __tostring and __index has table metamethods.
...much fun and stay healthy.

Redis Lua Differetiating empty array and object

I encountered this bug in cjson lua when I was using a script in redis 3.2 to set a particular value in a json object.
Currently, the lua in redis does not differentiate between an empty json array or an empty json object. Which causes serious problems when serialising json objects that have arrays within them.
eval "local json_str = '{\"items\":[],\"properties\":{}}' return cjson.encode(cjson.decode(json_str))" 0
Result:
"{\"items\":{},\"properties\":{}}"
I found this solution https://github.com/mpx/lua-cjson/issues/11 but I wasn't able to implement in a redis script.
This is an unsuccessful attempt :
eval
"function cjson.mark_as_array(t)
local mt = getmetatable(t) or {}
mt.__is_cjson_array = true
return setmetatable(t, mt)
end
function cjson.is_marked_as_array(t)
local mt = getmetatable(t)
return mt and mt.__is_cjson_array end
local json_str = '{\"items\":[],\"properties\":{}}'
return cjson.encode(cjson.decode(json_str))"
0
Any help or pointer appreciated.
There are two plans.
Modify the lua-cjson source code and compile redis, click here for details.
Fix by code:
local now = redis.call("time")
-- local timestamp = tonumber(now[1]) * 1000 + math.floor(now[2]/1000)
math.randomseed(now[2])
local emptyFlag = "empty_" .. now[1] .. "_" .. now[2] .. "_" .. math.random(10000)
local emptyArrays = {}
local function emptyArray()
if cjson.as_array then
-- cjson fixed: https://github.com/xiyuan-fengyu/redis-lua-cjson-empty-table-fix
local arr = {}
setmetatable(arr, cjson.as_array)
return arr
else
-- plan 2
local arr = {}
table.insert(emptyArrays, arr)
return arr
end
end
local function toJsonStr(obj)
if #emptyArrays > 0 then
-- plan 2
for i, item in ipairs(emptyArrays) do
if #item == 0 then
-- empty array, insert a special mark
table.insert(item, 1, emptyFlag)
end
end
local jsonStr = cjson.encode(obj)
-- replace empty array
jsonStr = (string.gsub(jsonStr, '%["' .. emptyFlag .. '"]', "[]"))
for i, item in ipairs(emptyArrays) do
if item[1] == emptyFlag then
table.remove(item, 1)
end
end
return jsonStr
else
return cjson.encode(obj)
end
end
-- example
local arr = emptyArray()
local str = toJsonStr(arr)
print(str) -- "[]"

mysql-proxy result field manipulation

I have a MYSQL server and MYSQL-PROXY and I am trying to manipualte the results I send to the client as a response to a SELECT query. I have writen this code in lua:
function string.starts(String,Start)
return string.sub(String,1,string.len(Start))==Start
end
function read_query_result(inj)
local fn = 1
local fields = inj.resultset.fields
while fields[fn] do
fn = fn + 1
end
fn = fn - 1
print("FIELD NUMBER: " .. fn)
for row in inj.resultset.rows do
print ("--------------")
for i = 1, fn do
if (string.starts(fields[i].name,"TEST")) then
row[i]="TESTED"
end
print ("DATA: " .. fields[i].name .. " -> " .. row[i])
end
end
return proxy.PROXY_SEND_RESULT
end
I can correctly read the field names and values. I can detect the condition where I want the result modified, but I can not get the data sent to the client.
I see two problems:
I am setting the value in the local row variable, but I have not found the way to set the real resultset (inj.Resultset.row[i] or something similar).
There is something wrong with return proxy.PROXY_SEND_RESULT, because I am seeing that whenever I comment that sentence I see the results, and If I uncomment it I get an error.
I have not found example code as a reference.
Ok. Solved.
Data has to be inserted in a table
PROXY_SEND_RESULT requires proxy.response.type to be set.
This is the correct module:
function read_query_result(inj)
local fn = 1
local fields = inj.resultset.fields
proxy.response.resultset = {fields = {}, rows = {}}
while fields[fn] do
table.insert(proxy.response.resultset.fields, {type = proxy.MYSQL_TYPE_STRING, name = fields[fn].name})
fn = fn + 1
end
fn = fn - 1
for row in inj.resultset.rows do
for i = 1, fn do
if (string.starts(fields[i].name,"TEST")) then
row[i]="TESTED"
end
end
table.insert(proxy.response.resultset.rows, row )
end
proxy.response.type = proxy.MYSQLD_PACKET_OK
return proxy.PROXY_SEND_RESULT
end

Corona sdk, functions run in wrong order?

I'm trying to save some data in to a table. I get the data from a database and it works ok.
My problem is that the data is not saved in the table. It is a lua table like table = {} and NOT a database table.
Maybe it is saved but it looks like the prints are done before the saving even though I call them after. In fact it seems like my network request is done last in my program even though I call it first.
I would real like to know the reason for this. Any ideas?
Here is the code:
---TESTING!
print("Begin teting!")
--hej = require ( "test2" )
local navTable = {
Eng_Spd = 0,
Spd_Set = 0
}
local changeTab = function()
navTable.Eng_Spd = 2
end
printNavTable = function()
print("navTable innehåller: ")
print(navTable.Eng_Spd)
print(navTable.Spd_Set)
end
require "sqlite3"
local myNewData
local json = require ("json")
local decodedData
local SaveData2 = function()
local i = 1
local counter = 1
local index = "livedata"..counter
local navValue = decodedData[index]
print(navValue)
while (navValue ~=nil) do
--tablefill ="INSERT INTO navaltable VALUES (NULL,'" .. navValue[1] .. "','" .. navValue[3] .."','" .. navValue[4] .."','" .. navValue[5] .."','" .. navValue[6] .."');"
--print(tablefill)
--db:exec(tablefill)
if navValue[3] == "Eng Spd" then navTable.Eng_Spd = navValue[4]
elseif navValue[3] == "Spd Set" then navTable.Spd_Set = navValue[4]
else print("blah")
end
print(navTable.Eng_Spd)
print(navTable.Spd_Set)
counter=counter+1
index = "livedata"..counter
navValue = decodedData[index]
end
end
local function networkListener( event )
if (event.isError) then
print("Network error!")
else
myNewData = event.response
print("From server: "..myNewData)
decodedData = (json.decode(myNewData))
SaveData2()
--db:exec("DROP TABLE IN EXISTS navaltable")
end
end
--function uppdateNavalTable()
network.request( "http://127.0.0.1/firstMidle.php", "GET", networkListener )
--end
changeTab()
printNavTable()
--uppdateNavalTable()
printNavTable()
print("Done!")
And here is the output:
Copyright (C) 2009-2012 C o r o n a L a b s I n c .
Version: 2.0.0
Build: 2012.971
Begin teting!
navTable innehåller:
2
0
navTable innehåller:
2
0
Done!
From server: {"livedata1":["1","0","Eng Spd","30","0","2013-03-15 11:35:48"],"li
vedata2":["1","1","Spd Set","13","0","2013-03-15 11:35:37"]}
table: 008B5018
30
0
30
13
And btw, navTable innehåller means navTable contains.
The answer is that networklistener run parallell with the rest of the code.