Page 1 of 1
BroccoliUtils - Misc. Collections of Libraries
Posted: Sun Mar 04, 2018 3:33 pm
by BroccoliRaab
BroccoliUtils is a collection of Libraries that I have used for developing my own games with Love2D.
It has a libraries for:
- Saving data as a lua file that when run returns a table with all of your data
- Using timers to run a function periodically, or create a delay before it runs
- Converting a spritesheet into a table of quad objects
- Creating animations as objects from a spritesheet or a directory containing pictures
- Drawing to a game scene like a canvas for it to render each thing to draw to them in order as objects[
- Handling events for each scene
- Creating a GUI by rendering functions that draw to the screen as objects that can be altered or have events tied to them
https://github.com/BroccolliRaab/BroccoliUtils
I am still in the process of documenting it
Documentation:
https://github.com/BroccolliRaab/BroccoliUtils/wiki
Re: BroccoliUtils - Misc. Collections of Libraries
Posted: Sun Mar 04, 2018 7:13 pm
by ivan
A few notes on your saveData util:
- it will hang with table cycles: a = {} a.b = a
- easier way to escape strings: escaped = string.format("%q", raw)
- loading code from the appdata directory is not "safe", consider looking into setenv
Re: BroccoliUtils - Misc. Collections of Libraries
Posted: Mon Mar 05, 2018 7:38 pm
by BroccoliRaab
I thought I added an assert statement that would not allow cyclic tables, but I guess not. I'll fix that.
I didn't know about that string.format so thats awesome to know about, And I probably should have known not to load from appdata. Of course thats a bad idea. I will update those things when I get the chance. I'll at least add the assert right now.
Re: BroccoliUtils - Misc. Collections of Libraries
Posted: Tue Mar 06, 2018 6:39 am
by ivan
The cycles thing is not that important - but if you want to assert/support it then you need something like:
Code: Select all
local function formatData1(data, visited)
visited = visited or {}
assert(not visited[data], "Cyclic tables are not allowed. At least not yet.")
visited[data] = true
...
for i, v in pairs(data) do
...
if type(v) == "table" then
...
formatData1(v, visited)
probably should have known not to load from appdata. Of course thats a bad idea
It's not bad, just remember that other apps can write to the appdata directory too.
Basically, you don't want to give global access to code loaded from non trusted locations.
Before you execute the saved script, use setfenv (
https://www.lua.org/pil/14.3.html )
Re: BroccoliUtils - Misc. Collections of Libraries
Posted: Tue Mar 06, 2018 7:42 am
by ivan
Regarding your timer lib: you can't store the index of a timer, because when you use table.remove all of the existing indexes shift over.
Also, you don't need "self:" when you are working with closures.
This is untested but it might give you a few ideas:
Code: Select all
local dim4 = {}
local timers = {}
function dim4.newTimer(isclock, delay, func, ...)
local running = false
local elapsed = 0
local t = {}
function t.update(dt)
elapsed = elapsed + dt
if elapsed < delay then
return true
end
t.callback()
if isclock then
elapsed = elapsed%delay
end
return isclock
end
function t.callback()
func(elapsed, ...)
end
function t.start()
if running then
running = true
table.insert(timers, t)
end
end
function t.stop(index)
if running then
running = false
if not index then
for i = 1, #timers do
if timers[i] == t then
index = i
break
end
end
end
assert(t == timers[index])
table.remove(timers, index)
end
end
function t.reset()
elapsed = 0
end
function t.destroy()
t.stop()
func = nil
elapsed = nil
end
return t
end
function dim4.update(dt)
for i = #timers, 1, -1 do
local t = timers[i]
if not t.update(dt) then
t.stop(i)
end
end
end
return dim4
Note that in "destroy" we unset the important "func" reference and the "elapsed" variable - this ensures that your timer cannot be used after calling "destroy".
One of the benefits of closures is that you have access to the original up-values so you can pass additional arguments:
Code: Select all
local dim4 = require("path.to.dim4")
function trigger(actualtime, p)
print(actualtime, p)
end
dim4.newTimer(true, 1, trigger, "every second!")
dim4.newTimer(false, 5, trigger, "once after five seconds")
function love.update(dt)
dim4.update(dt)
end
Re: BroccoliUtils - Misc. Collections of Libraries
Posted: Tue Mar 06, 2018 12:10 pm
by grump
BroccoliRaab wrote: ↑Mon Mar 05, 2018 7:38 pm
I'll at least add the assert right now.
Code: Select all
Error: Syntax error: saveData.lua:64: ')' expected near '='
You're assigning a value in the assert line when you should be comparing it using ~=.
Check ivan's example code, it shows how to detect cycles, even deeply nested cycles. Just checking the current table for references to itself is not enough.
There are some more checks needed to make it stable:
Code: Select all
local sd = require('saveData')
local t = { [love.image.newImageData(8, 8)] = 64 }
sd.save(t, 'test.lua')
This results in invalid code that can't be loaded. I would expect a serialization library to fail in this case and tell me what went wrong. Better check if all index types are valid, not just for tables as indices. From the top of my head: userdata, table, function and thread are problematic. There may be more.