345 lines
11 KiB
Lua
345 lines
11 KiB
Lua
|
|
local Util = require("classes.util.Util")
|
|
|
|
---@class Perstistence
|
|
---@field private _path string
|
|
---@field private _updateRequired boolean
|
|
local Persistence = {}
|
|
do
|
|
local EXTENSION = "spearhead"
|
|
|
|
---@class PersistentData
|
|
---@field version string
|
|
---@field unitsStates table<string, UnitState>
|
|
---@field random_missions table<string, MissionState>
|
|
---@field deliveredKilos table<string, number>
|
|
---@field activeStage number? required for backwards compatibility
|
|
---@field activeStageInStageLane table<string, number>
|
|
|
|
---@class UnitState
|
|
---@field isDead boolean
|
|
---@field pos Vec3?
|
|
---@field heading number?
|
|
---@field type string?
|
|
|
|
|
|
local persistanceWriteIntervalSeconds = 15
|
|
local enabled = false
|
|
|
|
local version = "1.0.0"
|
|
|
|
---@type PersistentData
|
|
local tables = {
|
|
version = version,
|
|
unitsStates = {},
|
|
random_missions = {},
|
|
deliveredKilos = {},
|
|
activeStage = nil, -- Backwards compatibility for previous version without lanes
|
|
activeStageInStageLane = {}
|
|
}
|
|
|
|
local logger = {}
|
|
|
|
if SpearheadConfig == nil then SpearheadConfig = {} end
|
|
if SpearheadConfig.Persistence == nil then SpearheadConfig.Persistence = {} end
|
|
|
|
|
|
local createFileIfNotExists = function(path)
|
|
if not path or path == "" then
|
|
logger:error("Persistence file path is not set, cannot create file")
|
|
return
|
|
end
|
|
|
|
local f = io.open(path, "r")
|
|
if f == nil then
|
|
logger:info("Persistence file does not exist, creating new one at: " .. path)
|
|
f = io.open(path, "w+")
|
|
if f == nil then
|
|
logger:error("Could not create a file")
|
|
else
|
|
f:write("{}")
|
|
f:close()
|
|
end
|
|
logger:info("Created new persistence file at: " .. path)
|
|
else
|
|
f:close()
|
|
end
|
|
end
|
|
|
|
---@param path string
|
|
local loadTablesFromFile = function(path)
|
|
if not path then return end
|
|
|
|
logger:info("Loading data from persistance file...")
|
|
local f = io.open(path, "r")
|
|
if f == nil then
|
|
logger:error("Could not open persistence file for reading: " .. path)
|
|
return
|
|
end
|
|
|
|
local json = f:read("*a")
|
|
f:close()
|
|
|
|
logger:debug("Loaded persistence file content: " .. json)
|
|
local lua = net.json2lua(json) --[[@as PersistentData]]
|
|
|
|
if lua then
|
|
tables = lua
|
|
logger:debug("Loaded persistence data: " .. Util.toString(lua))
|
|
else
|
|
logger:error("Could not load persistence file, using default tables")
|
|
end
|
|
|
|
if tables.version == nil then tables.version = version end
|
|
end
|
|
|
|
local writeToFile = function()
|
|
if not Persistence._path then return end
|
|
local path = Persistence._path
|
|
local f = io.open(path, "w+")
|
|
if f == nil then
|
|
error("Could not open file for writing")
|
|
return
|
|
end
|
|
|
|
local jsonString = net.lua2json(tables)
|
|
f:write(jsonString)
|
|
|
|
if f ~= nil then
|
|
f:close()
|
|
end
|
|
Persistence._updateRequired = false
|
|
logger:info("Wrote persistence data to file")
|
|
end
|
|
|
|
local UpdateContinuous = function(null, time)
|
|
env.info("[Spearhead][Persistence] Checking up on persistence state...")
|
|
if Persistence._updateRequired == true then
|
|
local status, result = pcall(writeToFile)
|
|
if status == false then
|
|
env.error("[Spearhead][Persistence] Could not write state to file: " .. result)
|
|
end
|
|
end
|
|
|
|
return time + persistanceWriteIntervalSeconds
|
|
end
|
|
|
|
Persistence.UpdateNow = function()
|
|
if enabled == true then
|
|
writeToFile()
|
|
end
|
|
end
|
|
|
|
Persistence.isEnabled = function()
|
|
return enabled
|
|
end
|
|
|
|
|
|
local warnForNonPersistenceContinous = function(null, time)
|
|
trigger.action.outText("Persistence was enabeld, however, io and lfs are not available and no persistence will be done. Make sure to either disable persistence or fix the issues before continuing.", 10)
|
|
return time + 9
|
|
end
|
|
|
|
---@param dir string @BaseDirectory
|
|
---@return string
|
|
local getLastFileOrDefault = function(dir, startsWith, default)
|
|
|
|
local latestFile, lastNumber = default, 0
|
|
for file in lfs.dir(dir) do
|
|
|
|
local split = Util.split_string(file, ".")
|
|
local doesStartWith = Util.startswith(file, startsWith, true)
|
|
if split and #split > 0 and split[#split] == EXTENSION and doesStartWith == true then
|
|
local numberString = split[#split-1]
|
|
local number = tonumber(numberString)
|
|
if number ~= nil and number > lastNumber then
|
|
lastNumber = number
|
|
latestFile = file
|
|
end
|
|
end
|
|
end
|
|
return latestFile
|
|
end
|
|
|
|
---comment
|
|
---@param persistenceLogger Logger
|
|
Persistence.Init = function(persistenceLogger)
|
|
logger = persistenceLogger
|
|
|
|
logger:info("Initiating Persistence Manager")
|
|
logger:debug("Initiating Persistence Manager")
|
|
|
|
if lfs == nil or io == nil then
|
|
logger:error("lfs and io seem to be sanitized. Persistence is skipped and disabled")
|
|
enabled = false
|
|
timer.scheduleFunction(warnForNonPersistenceContinous, nil, timer.getTime() + 10)
|
|
return
|
|
end
|
|
|
|
---@type string
|
|
local dir = lfs.writedir() .. "\\Data"
|
|
local fileName = "Spearhead_Persistence.0.spearhead"
|
|
if SpearheadConfig and SpearheadConfig.Persistence then
|
|
if SpearheadConfig.Persistence.fileName then
|
|
|
|
local userFileName = SpearheadConfig.Persistence.fileName
|
|
local split = Util.split_string(userFileName, ".")
|
|
|
|
if not split and #split < 3 then
|
|
split[#split+1] = "0"
|
|
split[#split+1] = "spearhead"
|
|
end
|
|
|
|
if tonumber(split[#split-1]) == nil then
|
|
split[#split+1] = "0"
|
|
split[#split+1] = "spearhead"
|
|
end
|
|
|
|
fileName = table.concat(split, ".")
|
|
end
|
|
|
|
if SpearheadConfig.Persistence.directory ~= nil then
|
|
dir = SpearheadConfig.Persistence.directory
|
|
end
|
|
end
|
|
|
|
local split = Util.split_string(fileName, ".")
|
|
local matchingPart = table.concat(Util.sublist(split, 1, #split-2), ".")
|
|
|
|
local lastFile = getLastFileOrDefault(dir--[[@as string]], matchingPart, fileName)
|
|
|
|
local fileSplit = Util.split_string(lastFile, ".")
|
|
fileSplit[#fileSplit-1] = tostring(tonumber(fileSplit[#fileSplit-1]) + 1)
|
|
fileName = table.concat(fileSplit, ".")
|
|
|
|
if lastFile ~= fileName then
|
|
logger:info("Found last persistence file: " .. lastFile)
|
|
else
|
|
logger:info("No previous persistence file found, using default: " .. fileName)
|
|
end
|
|
|
|
|
|
logger:info("New Persistence file name: " .. tostring(fileName))
|
|
local lastPath = dir .. "\\" .. lastFile
|
|
local path = dir .. "\\" .. fileName
|
|
Persistence._path = path
|
|
|
|
createFileIfNotExists(path)
|
|
loadTablesFromFile(lastPath)
|
|
timer.scheduleFunction(UpdateContinuous, nil, timer.getTime() + 120)
|
|
enabled = true
|
|
Persistence.UpdateNow()
|
|
end
|
|
|
|
---Sets the stage in the persistence table
|
|
---@param stageLane string?
|
|
---@param stageNumber number
|
|
Persistence.SetActiveStage = function(stageLane, stageNumber)
|
|
stageLane = stageLane or "nil"
|
|
tables.activeStageInStageLane[stageLane] = stageNumber
|
|
Persistence._updateRequired = true
|
|
end
|
|
|
|
---Get the active stage as in the persistance file
|
|
---@param stageLane string?
|
|
---@return integer|nil
|
|
Persistence.GetActiveStage = function(stageLane)
|
|
stageLane = stageLane or "nil"
|
|
|
|
-- backwards compatibility for previous versions without stage lanes
|
|
if stageLane == "nil" and tables.activeStageInStageLane[stageLane] == nil then
|
|
tables.activeStageInStageLane[stageLane] = tables.activeStage
|
|
tables.activeStage = nil -- Clear the old activeStage so it's not used or confused later
|
|
end
|
|
|
|
return tables.activeStageInStageLane[stageLane]
|
|
end
|
|
|
|
---comment
|
|
---@param missionName string
|
|
---@param pickedZone string
|
|
Persistence.RegisterPickedRandomMission = function(missionName, pickedZone)
|
|
if enabled == false then return end
|
|
|
|
if tables.random_missions == nil then tables.random_missions = {} end
|
|
tables.random_missions[string.lower(missionName)] = pickedZone
|
|
Persistence._updateRequired = true
|
|
end
|
|
|
|
---Get the picked random mission from the persistence file
|
|
---@param missionName string
|
|
---@return string?
|
|
Persistence.GetPickedRandomMission = function(missionName)
|
|
if enabled == false then return nil end
|
|
return tables.random_missions[string.lower(missionName)]
|
|
end
|
|
|
|
|
|
---comment
|
|
---@param zoneName string
|
|
---@param kilos number
|
|
Persistence.SetZoneDeliveredKilos = function(zoneName, kilos)
|
|
if enabled == false then return end
|
|
|
|
if tables.deliveredKilos == nil then
|
|
tables.deliveredKilos = {}
|
|
end
|
|
|
|
tables.deliveredKilos[zoneName] = kilos
|
|
Persistence._updateRequired = true
|
|
end
|
|
|
|
Persistence.GetZoneDeliveredKilos = function(zoneName)
|
|
if enabled == false then return 0 end
|
|
|
|
if tables.deliveredKilos == nil then
|
|
tables.deliveredKilos = {}
|
|
end
|
|
|
|
return tables.deliveredKilos[zoneName] or 0
|
|
end
|
|
|
|
|
|
|
|
---Check if the unit was dead during the last save. Nil if persitance not enabled or no state is found
|
|
---@param unitName string name
|
|
---@return UnitState|nil { isDead, pos = {x,y,z}, heading, type, country_id }
|
|
Persistence.UnitState = function(unitName)
|
|
if Persistence.isEnabled() == false then
|
|
return nil
|
|
end
|
|
|
|
local entry = tables.unitsStates[unitName]
|
|
if entry then
|
|
return entry
|
|
else
|
|
---@type UnitState
|
|
local state = {
|
|
isDead = false
|
|
}
|
|
return state
|
|
end
|
|
end
|
|
|
|
---Pass the unit to be saved as "dead"
|
|
---@param name string
|
|
---@param position Vec3 { x, y ,z }
|
|
---@param heading number
|
|
---@param type string
|
|
Persistence.UnitKilled = function (name, position, heading, type)
|
|
if enabled == false then return end
|
|
|
|
logger:debug("Unit killed: " .. name .. ".. => persistenting")
|
|
|
|
tables.unitsStates[name] = {
|
|
isDead = true,
|
|
pos = position,
|
|
heading = heading,
|
|
type = type,
|
|
isCleaned = false
|
|
}
|
|
Persistence._updateRequired = true
|
|
end
|
|
end
|
|
|
|
return Persistence |