diff --git a/sources/Users/User.uc b/sources/Users/User.uc index 021dbcf..919ef35 100644 --- a/sources/Users/User.uc +++ b/sources/Users/User.uc @@ -2,7 +2,7 @@ * Object that is supposed to store a persistent data about the * certain player. That is data that will be remembered even after player * reconnects or server changes map/restarts. - * Copyright 2020 Anton Tarasenko + * Copyright 2020 - 2021 Anton Tarasenko *------------------------------------------------------------------------------ * This file is part of Acedia. * @@ -27,6 +27,13 @@ var private UserID id; // an easy reference in console commands var private int key; +// Database where user's persistent data is stored +var private Database persistentDatabase; +// Pointer to this user's "settings" data in particular +var private JSONPointer persistentSettingsPointer; + +var private LoggerAPI.Definition errNoUserDataDatabase; + /** * Initializes caller `User` with id and it's session key. Should be called * right after `APlayer` was created. @@ -61,6 +68,123 @@ public final function int GetKey() return key; } +/** + * Reads user's persistent data saved under name `dataName`, saving it into + * a collection using mutable data types. + * Only should be used if `_.users.PersistentStorageExists()` returns `true`. + * + * @param groupName Name of the group these settings belong to. + * This exists to help reduce name collisions between different mods. + * Acedia stores all its settings under "Acedia" group. We suggest that you + * pick at least one name to use for your own mods. + * It should be unique enough to not get picked by others - "weapons" is + * a bad name, while "CoolModMastah79" is actually a good pick. + * @param dataName Any name, from under which settings you are interested + * (inside `groupName` group) should be read. + * @return Task object for reading specified persistent data from the database. + * For more info see `Database.ReadData()` method. + * Guaranteed to not be `none` iff + * `_.users.PersistentStorageExists() == true`. + */ +public final function DBReadTask ReadPersistentData( + Text groupName, + Text dataName) +{ + local DBReadTask task; + if (groupName == none) return none; + if (dataName == none) return none; + if (!SetupDatabaseVariables()) return none; + + persistentSettingsPointer.Push(groupName).Push(dataName); + task = persistentDatabase.ReadData(persistentSettingsPointer, true); + _.memory.Free(persistentSettingsPointer.Pop()); + _.memory.Free(persistentSettingsPointer.Pop()); + return task; +} + +/** + * Writes user's persistent data under name `dataName`. + * Only should be used if `_.users.PersistentStorageExists()` returns `true`. + * + * @param groupName Name of the group these settings belong to. + * This exists to help reduce name collisions between different mods. + * Acedia stores all its settings under "Acedia" group. We suggest that you + * pick at least one name to use for your own mods. + * It should be unique enough to not get picked by others - "weapons" is + * a bad name, while "CoolModMastah79" is actually a good pick. + * @param dataName Any name, under which settings you are interested + * (inside `groupName` group) should be written. + * @param data JSON-compatible (see `_.json.IsCompatible()`) data that + * should be written into database. + * @return Task object for writing specified persistent data into the database. + * For more info see `Database.WriteData()` method. + * Guarantee to not be `none` iff + * `_.users.PersistentStorageExists() == true`. + */ +public final function DBWriteTask WritePersistentData( + Text groupName, + Text dataName, + AcediaObject data) +{ + local DBWriteTask task; + local AssociativeArray emptyObject; + if (groupName == none) return none; + if (dataName == none) return none; + if (!SetupDatabaseVariables()) return none; + + emptyObject = _.collections.EmptyAssociativeArray(); + persistentSettingsPointer.Push(groupName); + persistentDatabase.IncrementData(persistentSettingsPointer, emptyObject); + persistentSettingsPointer.Push(dataName); + task = persistentDatabase.WriteData(persistentSettingsPointer, data); + _.memory.Free(persistentSettingsPointer.Pop()); + _.memory.Free(persistentSettingsPointer.Pop()); + _.memory.Free(emptyObject); + return task; +} + +// Setup database `persistentDatabase` and pointer to this user's data +// `persistentSettingsPointer`. +// Return `true` if these variables were setup (during this call or before) +// and `false` otherwise. +private function bool SetupDatabaseVariables() +{ + local Text userDataLink; + local Text userTextID; + local AssociativeArray skeletonObject; + if ( persistentDatabase != none && persistentSettingsPointer != none + && persistentDatabase.IsAllocated()) + { + return true; + } + if (id == none || !id.IsInitialized()) { + return false; + } + _.memory.Free(persistentSettingsPointer); + userDataLink = _.users.GetUserDataLink(); + persistentDatabase = _.db.Load(userDataLink); + if (persistentDatabase == none) + { + _.logger.Auto(errNoUserDataDatabase).Arg(userDataLink); + return false; + } + persistentSettingsPointer = _.db.GetPointer(userDataLink); + userTextID = id.GetSteamID64String(); + skeletonObject = _.collections.EmptyAssociativeArray(); + skeletonObject.SetItem( P("statistics"), + _.collections.EmptyAssociativeArray(), true); + skeletonObject.SetItem( P("settings"), + _.collections.EmptyAssociativeArray(), true); + persistentSettingsPointer.Push(userTextID); + persistentDatabase.IncrementData(persistentSettingsPointer, skeletonObject); + persistentSettingsPointer.Push(P("settings")); + _.memory.Free(userTextID); + _.memory.Free(userDataLink); + _.memory.Free(skeletonObject); + return true; +} + defaultproperties { + errNoUserDataDatabase = (l=LOG_Error,m="Failed to load persistent user database instance given by link \"%1\".") } \ No newline at end of file diff --git a/sources/Users/UserAPI.uc b/sources/Users/UserAPI.uc index 48f3e5e..3d18e49 100644 --- a/sources/Users/UserAPI.uc +++ b/sources/Users/UserAPI.uc @@ -1,6 +1,6 @@ /** * API that allows easy access to `User` persistent data and `UserID`s. - * Copyright 2020 Anton Tarasenko + * Copyright 2020 - 2021 Anton Tarasenko *------------------------------------------------------------------------------ * This file is part of Acedia. * @@ -19,6 +19,8 @@ */ class UserAPI extends AcediaObject; +var private string userDataDBLink; + /** * Returns reference to the database of user records that Acedia was * set up to use. @@ -73,6 +75,35 @@ public final function User FetchByKey(int userKey) return class'UserDatabase'.static.GetInstance().FetchUserByKey(userKey); } +/** + * Returns configured database link to the JSON object in which users' data + * is stored. + * + * @return Database link to the JSON object in which users' data is stored. + * Guaranteed to not be `none`. + */ +public final function Text GetUserDataLink() +{ + return P(userDataDBLink).Copy(); +} + +/** + * Checks whether database setup to store users' persistent data was configured + * and actually exists. + * + * This does not check for whether that database is properly configured. + * If sub-object set to store users' data was not created inside it, then + * Acedia will not be able to make use of users' persistent storage. + * + * @return `true` if database for users' persistent data storage exists and + * `false` otherwise. + */ +public final function bool PersistentStorageExists() +{ + return (_.db.Load(P(userDataDBLink)) != none); +} + defaultproperties { + userDataDBLink = "[local]database:/users" } \ No newline at end of file