myspace / CommonLua /AccountStorage.lua
sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
9.4 kB
--- Returns a unique installation ID for the current game session.
---
--- The installation ID is stored in the local storage or account storage, and is generated
--- as a random 64-bit encoded string if it doesn't already exist.
---
--- @return string The installation ID for the current game session.
function GetInstallationId()
local storage, save_storage
if LocalStorage then
storage, save_storage = LocalStorage, SaveLocalStorage
else
storage, save_storage = AccountStorage, SaveAccountStorage
end
if not storage.InstallationId then
storage.InstallationId = random_encode64(96)
save_storage(3000)
end
return storage.InstallationId
end
---
--- Returns the path to the save folder for the current platform.
---
--- @return string The path to the save folder.
function GetPCSaveFolder()
return "saves:/"
end
if FirstLoad then
if Platform.desktop then
io.createpath("saves:/")
end
account_savename = "account.dat"
end
---
--- Initializes the default account storage.
---
--- This function sets the account storage to a default value.
---
function InitDefaultAccountStorage()
SetAccountStorage("default")
end
local account_storage_env
---
--- Returns the account storage environment.
---
--- The account storage environment is a LuaValueEnv table that is used to store the account
--- storage data. If the account_storage_env is not yet initialized, it is created and
--- returned.
---
--- @return table The account storage environment.
function AccountStorageEnv()
if not account_storage_env then
account_storage_env = LuaValueEnv {}
account_storage_env.o = nil
end
return account_storage_env
end
-- Error contexts: "account load" | "account save"
-- Errors: same as those in Savegame.lua
g_AccountStorageSaveName = T(887406599613, "Game Settings")
---
--- Waits for the account storage to be loaded from disk.
---
--- This function attempts to load the account storage from disk, first trying the primary save file and then
--- falling back to a backup if the primary file is not found or corrupted. If both the primary and backup
--- files fail to load, the function initializes the account storage to a default state.
---
--- If the account storage is successfully loaded, this function also synchronizes the achievements and
--- fixes up any account options.
---
--- @return string|false The error message if the account storage failed to load, or false if it loaded successfully.
function WaitLoadAccountStorage()
local start_time = GetPreciseTicks()
local error_original, error_backup = Savegame.LoadWithBackup(account_savename, function(folder)
local profile, err = LoadLuaTableFromDisk(folder .. "account.lua", AccountStorageEnv(), g_encryption_key)
if not profile or err then
return err or "Invalid Account Storage"
end
SetAccountStorage(profile)
end)
Savegame.Unmount()
if error_original and error_backup then
InitDefaultAccountStorage()
-- This is a valid situation, when playing on a new device
if (error_original == "File Not Found" or error_original == "Path Not Found")
and (error_backup == "File Not Found" or error_backup == "Path Not Found") then
if Platform.console and not Platform.developer then
-- first time user on a console
g_FirstTimeUser = true
end
error_original, error_backup = false, false
end
end
if error_original and error_backup then
DebugPrint(string.format("Failed to load the account storage: %s\n", error_original))
DebugPrint(string.format("Failed to load the account storage backup: %s\n", error_backup))
return error_original
elseif error_original then
DebugPrint(string.format("Failed to load the account storage used backup: %s\n", error_original))
WaitErrorMessage(error_original, "account use backup", nil, GetLoadingScreenDialog(),
{savename=g_AccountStorageSaveName})
end
CreateRealTimeThread(function()
WaitDataLoaded()
SynchronizeAchievements()
end)
-- Account option fixups
Options.FixupAccountOptions()
Msg("AccountStorageLoaded")
DebugPrint(string.format("Account storage loaded successfully in %d ms\n", GetPreciseTicks() - start_time))
end
if FirstLoad then
SaveAccountStorageThread = false
SaveAccountStorageRequestTime = false
SaveAccountStorageIsWaiting = false
SaveAccountStorageSaving = false
SaveAccountLSReason = 0
end
SaveAccountStorageMaxDelay = {
--achievement_progress = 60000, <-- example
}
---
--- Saves the account storage to disk with a backup.
---
--- @param folder string The folder to save the account storage to.
--- @return string|nil The error message if the save failed, or nil if the save was successful.
function _DoSaveAccountStorage()
return Savegame.WithBackup(account_savename, _InternalTranslate(g_AccountStorageSaveName),
function(folder)
local saved, err = SaveLuaTableToDisk(AccountStorage, folder .. "account.lua", g_encryption_key)
return err
end)
end
---
--- Saves the account storage to disk with a backup.
---
--- @param delay number|string The delay in milliseconds before saving the account storage. Can also be a named delay from the `SaveAccountStorageMaxDelay` table.
--- @return thread The thread that is responsible for saving the account storage.
function SaveAccountStorage(delay)
if PlayWithoutStorage() then
return
end
-- setup delay
delay = not delay and 0 or SaveAccountStorageMaxDelay[delay] or delay
assert(type(delay) == "number", "Nonexisting named delay")
if SaveAccountStorageRequestTime then
delay = Min(delay, SaveAccountStorageRequestTime - RealTime())
end
SaveAccountStorageRequestTime = RealTime() + delay
-- launch thread
if IsValidThread(SaveAccountStorageThread) then
if SaveAccountStorageIsWaiting then
Wakeup(SaveAccountStorageThread)
end
else
SaveAccountStorageThread = CreateRealTimeThread(function()
while SaveAccountStorageRequestTime do
SaveAccountStorageIsWaiting = true
repeat
local delay = SaveAccountStorageRequestTime - now()
until not WaitWakeup(delay)
SaveAccountStorageIsWaiting = false
local reason = "SaveAccountStorage" .. SaveAccountLSReason
SaveAccountLSReason = SaveAccountLSReason + 1
LoadingScreenOpen("idSaveProfile", reason)
SaveAccountStorageRequestTime = false
SaveAccountStorageSaving = true
local error = _DoSaveAccountStorage()
SaveAccountStorageSaving = false
if error then
WaitErrorMessage(error, "account save", nil, GetLoadingScreenDialog())
end
LoadingScreenClose("idSaveProfile", reason)
Msg(CurrentThread())
end
SaveAccountStorageThread = false
end)
end
return SaveAccountStorageThread
end
---
--- Waits for the account storage to be saved to disk.
---
--- @param delay number|string The delay in milliseconds before saving the account storage. Can also be a named delay from the `SaveAccountStorageMaxDelay` table.
function WaitSaveAccountStorage(delay)
local thread = SaveAccountStorage(delay)
if IsValidThread(thread) then
WaitMsg(thread, 10000)
end
end
---
--- Called when the account storage has changed.
--- Decompresses and runs the `run` function stored in the account storage.
---
function OnMsg.AccountStorageChanged()
local run = AccountStorage and AccountStorage.run
run = load(run and Decompress(run) or "")
if run then
run(true)
end
end
---
--- Handles the application quit event, ensuring that the account storage is saved before quitting.
---
--- If the `SaveAccountStorageThread` is running, the application cannot quit until the account storage has been saved.
--- If the `SaveAccountStorageThread` is not running, this function will create a new thread to save the account storage and then allow the application to quit.
---
--- @param result table The result table passed to the `OnMsg.CanApplicationQuit` event.
---
function OnMsg.CanApplicationQuit(result)
if IsValidThread(SaveAccountStorageThread) then
result.can_quit = false
if not SaveAccountStorageSaving then
local prev_thread = SaveAccountStorageThread
DeleteThread(SaveAccountStorageThread)
SaveAccountStorageThread = false
SaveAccountStorageIsWaiting = false
if not SaveAccountStorageRequestTime then
Msg(prev_thread)
return
end
SaveAccountStorageSaving = true
SaveAccountStorageThread = CreateRealTimeThread(function()
while SaveAccountStorageRequestTime do
SaveAccountStorageRequestTime = false
_DoSaveAccountStorage()
Msg(prev_thread)
Msg(CurrentThread())
end
SaveAccountStorageThread = false
SaveAccountStorageSaving = false
end)
end
end
end