savedata module

This commit is contained in:
Jill 2022-09-22 18:41:21 +03:00
parent 1e58e1fe80
commit a6f17c0f2a
4 changed files with 205 additions and 1 deletions

View File

@ -47,7 +47,7 @@ if not select then
return #arg
else
local values = {}
for i = math.max(index + 1, 1), #arg do
for i = math.max(index, 1), #arg do
table.insert(values, arg[i])
end
return unpack(values)

180
stdlib/savedata.lua Normal file
View File

@ -0,0 +1,180 @@
local self = {}
local binser = require('stdlib.binser')
require('stdlib.util')
local savedataName
local savedata = {
_lastSave = {0, 0, 0, 0, 0}
}
local loadedSavedata = false
self.saveNextFrame = false
---@param name string @a unique name for the entire modfile
---@param forceIgnore boolean | nil
--- for picking a name:
--- please head to this link:
--- https://www.random.org/strings/?num=1&len=16&digits=on&upperalpha=on&loweralpha=on&unique=on&format=plain&rnd=new
--- and generate yourself a hash. afterwards, append it to your game name like so:
--- savedata.initializeModule('myGameName_doAmaUOBIjiaSWyz')
--- this helps ensure no collisions happen, breaking both of the modfiles in the process!
--- **DON'T CHANGE THIS!!** else you'll break existing saves
function self.initializeModule(name, forceIgnore)
if loadedSavedata then
error('uranium: cannot initialize savedata module after uranium.init!', 3)
end
if not forceIgnore then
if #name < 16 then
error('uranium: savedata name too short! must be at least 16 characters long', 2)
end
local normalChars = 0
local specialChars = 0
for i = 1, #name do
local char = string.sub(name, i, i)
local special = string.lower(char) == string.upper(char)
if special then
specialChars = specialChars + 1
else
normalChars = normalChars + 1
end
end
if normalChars < 4 then
error('uranium: savedata name should have at least 4 non-special characters!', 2)
end
if specialChars < 2 then
error('uranium: savedata name should have at least 2 special characters!', 2)
end
end
savedataName = name
end
local function checkIfInitialized()
if not savedataName then
error('uranium: savedata not initialized! please run savedata.initializeModule first', 3)
end
end
local function convertSource(s)
local pathSplit = split(s.source, '/')
local lastTwo = pathSplit[#pathSplit - 1] .. '/' .. string.sub(pathSplit[#pathSplit], 1, -5)
return lastTwo
end
---@param data table
---@param name string | nil
function self.s(data, name)
if not name then name = convertSource(debug.getinfo(2, 'S')) end
checkIfInitialized()
if loadedSavedata then
error('uranium: cannot add new savedata after initializing!', 3)
end
if string.sub(name, 1, 1) == '_' then
error('uranium: cannot start savedata name with an underscore', 2)
end
savedata[name] = data
end
--- prefers tab1 with type mismatches; prefers tab2 with value mismatches
local function mergeTable(tab1, tab2)
local tab = {}
for k, v1 in pairs(tab1) do
local v2 = tab2[k]
if type(v1) ~= type(v2) then
tab[k] = v1
else
if type(v1) == 'table' then
tab[k] = mergeTable(v1, v2)
else
tab[k] = v2
end
end
end
return tab
end
--- replaces tab1 with tab2, preserving pointers
--- this sucks
local function replaceFromRoot(tab1, tab2)
local keys = {}
for k in pairs(tab1) do
if not includes(keys, k) then
table.insert(keys, k)
end
end
for k in pairs(tab2) do
if not includes(keys, k) then
table.insert(keys, k)
end
end
for _, key in ipairs(keys) do
if type(tab1[key]) == type(tab2[key]) and type(tab1[key]) == 'table' then
replaceFromRoot(tab1[key], tab2[key])
else
tab1[key] = tab2[key]
end
end
end
function self.load()
checkIfInitialized()
print('loading...')
local profile = PROFILEMAN:GetMachineProfile():GetSaved()
local save = {}
local serialized = profile[savedataName]
if not serialized then
print('no savedata found')
save = savedata
else
print('savedata found: ' .. serialized)
save = binser.deserializeN(serialized, 1)
print('deserialized: ' .. fullDump(save))
end
local newSavedata = mergeTable(savedata, save)
replaceFromRoot(savedata, newSavedata)
print('merged: ' .. fullDump(savedata))
end
---@param instant boolean | nil
function self.save(instant)
checkIfInitialized()
print('saving...')
local profile = PROFILEMAN:GetMachineProfile():GetSaved()
savedata._lastSave = {Hour(), Minute(), DayOfMonth(), MonthOfYear(), Year()}
print('savedata: ' .. fullDump(savedata))
local serialized = binser.serialize(savedata)
print('serialized: ' .. serialized)
profile[savedataName] = serialized
uranium:call('save')
if instant then
PROFILEMAN:SaveMachineProfile()
uranium:call('saveFinished')
else
self.saveNextFrame = true
end
end
function uranium.init()
loadedSavedata = true
if savedataName then
self.load()
end
end
function uranium.update()
if self.saveNextFrame then
self.saveNextFrame = false
PROFILEMAN:SaveMachineProfile()
uranium:call('saveFinished')
print('saved to profile')
end
end
return self

View File

@ -154,4 +154,26 @@ function clearMetatables(tab)
clearMetatables(obj)
end
end
end
---@param source string
---@param sep string
---@return string[]
-- https://stackoverflow.com/a/42607786
function split(source, sep)
local result, i = {}, 1
while true do
local a, b = string.find(source, sep)
if not a then break end
local candidat = string.sub(source, 1, a - 1)
if candidat ~= '' then
result[i] = candidat
end
i = i + 1
source = string.sub(source, b + 1)
end
if source ~= '' then
result[i] = source
end
return result
end

View File

@ -11,6 +11,8 @@ GAMESTATE = {}
PREFSMAN = {}
---@type ScreenManager
SCREENMAN = {}
---@type ProfileManager
PROFILEMAN = {}
---@return Quad
--- Defines a Quad actor.