@ -8,115 +8,65 @@ author expwnent
This module is intended to help facilitate persistent table lookups .
It is a wrapper over dfhack.persistent calls .
It supports tables of arbitrary dimension and shape .
Argument names should not include any of the following symbols : _ \ /&
It stores information about each table and subtable ' s size and children.
For convenience , all stored information is itself persistent .
It would be more efficient to cache stored information in global variables and update it on save / load but this is not yet implemented .
Ask expwnent to try this if performance is bad .
--]]
--[[
function accessTable ( ... )
local args = { ... }
local name = ' mastertable '
local previousName = nil
local child
for n , arg in ipairs ( args ) do
child = ensure ( prefix .. name .. ' $$ ' .. arg )
local old = child.value
if old == ' ' then
child.value = gensym ( )
child.ints [ 1 ] = 0
local size = ensure ( prefix .. name .. ' $size ' )
size.value = tostring ( 1 + ( tonumber ( size.value ) or 0 ) )
size : save ( )
if previousName then
--local size = ensure(previousName .. '$size')
--size.value = tostring(1+(tonumber(size.value) or 0))
--size:save()
local prev = ensure ( prefix .. name .. ' $previous ' )
prev.value = previousName
prev : save ( )
end
child : save ( )
--new child
dfhack.persistent . save ( { key = prefix .. name , value = arg } , true )
end
--print(n,arg,previousName,child.key,child.value)
previousName = name
name = child.value
end
return child
Usage :
local persistTable = require ' persist-table '
persistTable.GlobalTable . doomitude = ' doom! ' -- will be stored persistently
print ( persistTable.GlobalTable . doomitude ) --doom!
persistTable.GlobalTable . doomitude = nil --delete the persistent record
print ( persistTable.GlobalTable . doomitude ) --nil
persistTable.GlobalTable . mana = { } --allocate a subtable for mana
local mana = persistTable.GlobalTable . mana --setting elements in this table will be persistent too
mana [ ' 1 ' ] = ' 3 ' --slightly faster than persistTable.GlobalTable.mana['1'] = '3'
--be aware that if you don't change the local variable mana when the game exits and reloads a different save you will run into mysterious problems so don't do that
mana [ ' 2 ' ] = ' 100 '
mana.maximum = ' 1000 ' --tables can be any arbitrary shape
local globalTable = persistTable.GlobalTable --this is safe too
globalTable.mana = nil --this is safe: it will deallocate all subtables cleanly
globalTable.revengeDesire = { }
--globalTable.revengeDseire.foo = {} -- error: tables must be allocated first with parentTable.subtableName = {}
--you can check if it exists with globalTable.tableName != nil
local revengeTable = globalTable.revengeDesire
revengeTable [ ' 2 ' ] = revengeTable [ ' 2 ' ] or { } --this is fine too
revengeTable [ ' 2 ' ] [ ' 3 ' ] = ' totally a lot, man '
revengeTable [ ' 3 ' ] = { }
revengeTable [ ' 3 ' ] [ ' 2 ' ] = ' maybe a little bit '
revengeTable [ ' 2 ' ] = nil
-------------
And so on .
Be careful not to name your tables in a way that will conflict with other scripts ! The easiest way is to just put all your tables in one giant table named based on your script .
All stored values MUST be strings . Numbers can be supported later but are not yet supported . Keep in mind there is a significant overhead for each table element so don ' t go totally crazy storing massive amounts of information or the game will run out of RAM.
--table._children returns a list of child keys
for _ , childKey in ipairs ( table._children ) do
local child = table [ childKey ]
--blah
end
function deleteTable ( name )
if not name then
do return end
end
local previous = ensure ( prefix .. name .. ' $previous ' ) . value
local children = dfhack.persistent . get_all ( prefix .. name ) or { }
for _ , child in ipairs ( children ) do
--print('delete: ', name, previous, child)
local ptr = ensure ( prefix .. name .. ' $$ ' .. child.value )
if ( ptr.ints [ 1 ] == 0 ) then
--releasesym(ptr.value)
deleteTable ( ptr.value )
end
ptr : delete ( )
child : delete ( )
end
ensure ( prefix .. name .. ' $previous ' ) : delete ( )
ensure ( prefix .. name .. ' $size ' ) : delete ( )
if previous ~= ' ' then
local size = ensure ( prefix .. previous .. ' $size ' )
size.value = tostring ( - 1 + tonumber ( size.value ) )
size : save ( )
local children = dfhack.persistent . get_all ( prefix .. previous ) or { }
for _ , sibling in ipairs ( children ) do
--print(sibling.value, name, previous .. '$$' .. sibling.value)
local ptr = ensure ( prefix .. previous .. ' $$ ' .. sibling.value )
if ptr.value == name then
ptr : delete ( )
sibling : delete ( )
end
end
end
releasesym ( name )
end
function setTable ( ... )
local args = { ... }
local last = args [ # args ]
table.remove ( args , # args )
--table.setn(args, #args-1)
local entry = accessTable ( table.unpack ( args ) )
local old = entry.value
if entry.ints [ 1 ] == 0 then
deleteTable ( old )
end
entry.ints [ 1 ] = - 1
entry.value = last
entry : save ( )
return old
end
--]]
local prefix = ' persist-table '
prefix = ' persist-table '
function ensure ( name )
local function ensure ( name )
return dfhack.persistent . save ( { key = name } )
end
function gensym ( )
local function gensym ( )
local availables = dfhack.persistent . get_all ( prefix .. ' $available ' ) or { }
local available = nil
local smallest = nil
for _ , candidate in pairs ( availables ) do
--TODO: it would be great if we could iterate over these in order but we can't
local score = tonumber ( candidate.value )
print ( ' gensym ' , candidate , score , available , smallest )
--print('gensym', candidate, score, available, smallest)
if ( score and ( not available or score < smallest ) ) then
smallest = score
available = candidate
@ -125,7 +75,7 @@ function gensym()
if available then
local value = available.value
available : delete ( )
print ( ' gensym: allocate ' .. value )
--print('gensym: allocate ' .. value )
return value
end
--none explicitly available, so smallest unused is the next available number
@ -136,21 +86,21 @@ function gensym()
local result = smallestUnused.value
smallestUnused.value = tostring ( 1 + tonumber ( result ) )
smallestUnused : save ( )
print ( ' gensym: allocate ' .. result )
--print('gensym: allocate ' .. result )
return result
end
function releasesym ( symb )
local function releasesym ( symb )
local availables = dfhack.persistent . get_all ( prefix .. ' $available ' ) or { }
for _ , available in pairs ( availables ) do
print ( ' releasesym: ' , symb , available.value , available )
--print('releasesym: ', symb, available.value, available )
if available.value == symb then
print ( ' error: persist-table.releasesym( ' .. symb .. ' ): available.value = ' .. available.value )
return
end
end
dfhack.persistent . save ( { key = prefix .. ' $available ' , value = symb } , true )
print ( ' releasesym: unallocate ' .. symb )
--print('releasesym: unallocate ' .. symb )
end
local intCount = 7
local existIndex = intCount - 0
@ -179,30 +129,39 @@ local function deletePersistent(name)
releasesym ( name )
end
GlobalTable = { key = ' mastertable ' }
GlobalTable.mt = { }
GlobalTable.mt . __index = function ( table , key )
print ( rawget ( table , ' key ' ) .. ' [ ' .. key .. ' ] ' )
local entry = ensure ( prefix .. rawget ( table , ' key ' ) .. ' $$ ' .. key )
GlobalTable = GlobalTable or { key = ' mastertable ' }
GlobalTable.mt = GlobalTable.mt or { }
GlobalTable.mt . __index = GlobalTable.mt . __index or function ( theTable , key )
if key == ' _children ' then
return rawget ( theTable , key )
end
--print(rawget(theTable,'key') .. '[' .. key .. ']')
local entry = ensure ( prefix .. rawget ( theTable , ' key ' ) .. ' $$ ' .. key )
if entry.ints [ existIndex ] == existValue and entry.ints [ pointerIndex ] == defaultValue then
print ( ' string: ' .. entry.value )
--print('string: ' .. entry.value )
return entry.value
end
if entry.ints [ pointerIndex ] == pointerValue then
--pre-existing pointer
local result = { key = entry.value }
result.mt = rawget ( GlobalTable , ' mt ' )
local childArgs = dfhack.persistent . get_all ( prefix .. entry.value )
result._children = { }
for _ , childArg in ipairs ( childArgs or { } ) do
--print(result._children, childArg.value)
table.insert ( result._children , childArg.value )
end
setmetatable ( result , rawget ( GlobalTable , ' mt ' ) )
print ( ' table: ' .. entry.value )
--print('theTable: ' .. entry.value )
return result
end
entry : delete ( )
print ' table[key] does not exist. '
--print 'theTable[key] does not exist. '
return nil
end
GlobalTable.mt . __newindex = function ( t able, key , value )
print ( rawget ( table , ' key ' ) .. ' [ ' .. key .. ' ] = ' .. tostring ( value ) )
local entry = ensure ( prefix .. rawget ( t able, ' key ' ) .. ' $$ ' .. key )
GlobalTable.mt . __newindex = GlobalTable.mt . __newindex or function ( t heT able, key , value )
--print(rawget(theTable,'key') .. '[' .. key .. '] = ' .. tostring(value) )
local entry = ensure ( prefix .. rawget ( t heT able, ' key ' ) .. ' $$ ' .. key )
local old = entry.value
local isNew = entry.ints [ existIndex ] == defaultValue
if entry.ints [ existIndex ] == existValue and entry.ints [ pointerIndex ] == pointerValue then
@ -213,9 +172,9 @@ GlobalTable.mt.__newindex = function(table, key, value)
deletePersistent ( entry.value )
end
if not value then
print ( ' __newindesx: delete ' )
--print('__newindesx: delete' )
--delete
for i , child in ipairs ( dfhack.persistent . get_all ( prefix .. rawget ( t able, ' key ' ) ) or { } ) do
for i , child in ipairs ( dfhack.persistent . get_all ( prefix .. rawget ( t heT able, ' key ' ) ) or { } ) do
if child.value == key then
child : delete ( )
end
@ -223,23 +182,23 @@ GlobalTable.mt.__newindex = function(table, key, value)
entry : delete ( )
return
elseif type ( value ) == ' string ' then
print ( ' __newindesx: string ' )
--print('__newindesx: string' )
entry.value = value
entry.ints [ pointerIndex ] = defaultValue
entry.ints [ existIndex ] = existValue
entry : save ( )
if isNew then
print ( ' new child! ' )
dfhack.persistent . save ( { key = prefix .. rawget ( t able, ' key ' ) , value = key } , true )
--print('new child!' )
dfhack.persistent . save ( { key = prefix .. rawget ( t heT able, ' key ' ) , value = key } , true )
end
return
elseif type ( value ) == ' table ' then
print ( ' __newindesx: table ' )
--print('__newindesx: table' )
if rawget ( value , ' mt ' ) ~= rawget ( GlobalTable , ' mt ' ) then
if not isEmpty ( value ) then
error ( ' setting value to an invalid table ' )
end
print ( ' __newindesx: empty table ' )
--print('__newindesx: empty table' )
--empty table: allocate a thing
entry.ints [ pointerIndex ] = pointerValue
entry.ints [ existIndex ] = existValue
@ -247,19 +206,19 @@ GlobalTable.mt.__newindex = function(table, key, value)
entry : save ( )
if isNew then
print ( ' new child! ' )
dfhack.persistent . save ( { key = prefix .. rawget ( t able, ' key ' ) , value = key } , true )
--print('new child!' )
dfhack.persistent . save ( { key = prefix .. rawget ( t heT able, ' key ' ) , value = key } , true )
end
return
end
print ( ' __newindesx: table assignment ' )
--print('__newindesx: table assignment' )
entry.value = rawget ( value , ' key ' )
entry.ints [ pointerIndex ] = pointerValue
entry.ints [ existIndex ] = existValue
entry : save ( )
if isNew then
print ( ' new child! ' )
dfhack.persistent . save ( { key = prefix .. rawget ( t able, ' key ' ) , value = key } , true )
--print('new child!' )
dfhack.persistent . save ( { key = prefix .. rawget ( t heT able, ' key ' ) , value = key } , true )
end
return
else