diff --git a/library/lua/persist-table.lua b/library/lua/persist-table.lua index 2ff8746d3..f4a4b9e3c 100644 --- a/library/lua/persist-table.lua +++ b/library/lua/persist-table.lua @@ -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(table, key, value) - print(rawget(table,'key') .. '[' .. key .. '] = ' .. tostring(value)) - local entry = ensure(prefix .. rawget(table,'key') .. '$$' .. key) +GlobalTable.mt.__newindex = GlobalTable.mt.__newindex or function(theTable, key, value) + --print(rawget(theTable,'key') .. '[' .. key .. '] = ' .. tostring(value)) + local entry = ensure(prefix .. rawget(theTable,'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(table,'key')) or {}) do + for i,child in ipairs(dfhack.persistent.get_all(prefix .. rawget(theTable,'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(table,'key'), value=key}, true) + --print('new child!') + dfhack.persistent.save({key=prefix .. rawget(theTable,'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(table,'key'), value=key}, true) + --print('new child!') + dfhack.persistent.save({key=prefix .. rawget(theTable,'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(table,'key'), value=key}, true) + --print('new child!') + dfhack.persistent.save({key=prefix .. rawget(theTable,'key'), value=key}, true) end return else