Browse Source

Add scheduler support to users feature

pull/8/head
Anton Tarasenko 2 years ago
parent
commit
7419c6f907
  1. 27
      sources/Config/AcediaConfig.uc
  2. 36
      sources/Users/UserAPI.uc
  3. 135
      sources/Users/Users_Feature.uc

27
sources/Config/AcediaConfig.uc

@ -67,6 +67,8 @@ class AcediaConfig extends AcediaObject
// was detected in config, but not yet loaded.
// Only its default value is ever used.
var private HashTable existingConfigs;
// TODO: comment and add static cleanup
var private array<AcediaConfig> clearQueue;
// Stores name of the config where settings are to be stored.
// Must correspond to value in `config(...)` modifier in class definition.
@ -174,7 +176,7 @@ public final static function bool NewConfig(BaseText name)
new(none, NameToStorageVersion(name.ToString())) default.class;
newConfig._ = __();
newConfig.DefaultIt();
newConfig.SaveConfig();
__().scheduler.RequestDiskAccess(newConfig).connect = StaticSaveConfig;
default.existingConfigs.SetItem(name, newConfig);
name.FreeSelf();
return true;
@ -212,14 +214,17 @@ public final static function bool Exists(BaseText name)
*/
public final static function DeleteConfig(BaseText name)
{
local AcediaObject value;
local AcediaConfig value;
if (name == none) return;
if (default.existingConfigs == none) return;
name = name.LowerCopy();
value = default.existingConfigs.TakeItem(name);
if (value != none) {
value.ClearConfig();
value = AcediaConfig(default.existingConfigs.TakeItem(name));
if (value != none)
{
__().scheduler.RequestDiskAccess(value).connect = HandleClearQueue;
default.clearQueue[default.clearQueue.length] = value;
}
__().memory.Free(name);
}
@ -317,8 +322,18 @@ public final static function SaveData(BaseText name, HashTable data)
if (requiredConfig != none)
{
requiredConfig.FromData(data);
requiredConfig.SaveConfig();
__().scheduler.RequestDiskAccess(requiredConfig).connect =
StaticSaveConfig;
}
}
private final static function HandleClearQueue()
{
if (default.clearQueue.length <= 0) {
return;
}
default.clearQueue[0].ClearConfig();
default.clearQueue.Remove(0, 1);
}
defaultproperties

36
sources/Users/UserAPI.uc

@ -18,6 +18,7 @@
* along with Acedia. If not, see <https://www.gnu.org/licenses/>.
*/
class UserAPI extends AcediaObject
dependson(Users_Feature)
config(AcediaSystem);
var private config string userdataDBLink;
@ -676,6 +677,9 @@ public final function array<Text> GetGroupsForUser(User user)
/**
* Returns `UserID`s of all users that belong into the group named `groupName`.
*
* @see For more information alongside `UserID`s use
* `GetAnnotatedGroupMembers()`.
*
* Will only work if `Users_Feature` is active.
* In case active config of `Users_Feature` is set up to load user groups
* from a database (either local or remote), the returned value is a locally
@ -699,6 +703,38 @@ public final function array<UserID> GetGroupMembers(BaseText groupName)
return emptyResult;
}
/**
* Returns annotated `UserID`s of all users that belong into the group named
* `groupName`. `UserID`s aren't necessarily human-readable (e.g. SteamID)
* and to help organize configs they can be annotated with a `Text` name.
* This method returns `UserID` alongside such annotation, if it exists.
*
* @see For just `UserID`s use `GetGroupMembers()`.
*
* Will only work if `Users_Feature` is active.
* In case active config of `Users_Feature` is set up to load user groups
* from a database (either local or remote), the returned value is a locally
* cached one. This helps us avoid having to query database each time we want
* to check something about user groups, but it also means we might have
* an outdated information.
*
* @param groupName Name of the group. Case-insensitive.
* @return Array with `UserID`s for every user in the user group named
* `groupName`. All array elements are guaranteed to be not-`none` and
* correspond to unique players.
* If data wasn't yet loaded - returns empty array.
*/
public final function array<Users_Feature.AnnotatedUserID> GetAnnotatedGroupMembers(
BaseText groupName)
{
local array<Users_Feature.AnnotatedUserID> emptyResult;
if (usersFeature != none) {
return usersFeature.GetAnnotatedGroupMembers(groupName);
}
return emptyResult;
}
/**
* Checks whether user given by the `UserID` belongs to the group named
* `groupName`.

135
sources/Users/Users_Feature.uc

@ -26,6 +26,19 @@ var private /*config*/ bool useDatabase;
var private /*config*/ string databaseLink;
var private /*config*/ array<string> availableUserGroups;
var private bool diskSaveScheduled;
struct AnnotatedUserID
{
var public UserID id;
var public Text annotation;
};
struct IDAnnotationPair
{
var Text id, annotation;
};
// List of all available user groups for current config
var private array<Text> loadedUserGroups;
// `HashTable` (with group name keys) that stores `HashTable`s used as
@ -105,11 +118,12 @@ private final function LoadLocalGroup(
BaseText groupName,
optional bool localGroupIsExpected)
{
local int i;
local Text newSteamID, lowerCaseGroupName;
local HashTable newPlayerSet;
local UserGroup groupConfig;
local array<string> groupUserArray;
local int i;
local Text lowerCaseGroupName;
local HashTable newPlayerSet;
local UserGroup groupConfig;
local IDAnnotationPair nextUserPair;
local array<string> groupUserArray;
if (groupName == none) {
return;
@ -129,9 +143,10 @@ private final function LoadLocalGroup(
groupUserArray = groupConfig.user;
for (i = 0; i < groupUserArray.length; i += 1)
{
newSteamID = _.text.FromString(groupUserArray[i]);
newPlayerSet.SetItem(newSteamID, none);
newSteamID.FreeSelf();
nextUserPair = ParseConfigUserName(groupUserArray[i]);
newPlayerSet.SetItem(nextUserPair.id, nextUserPair.annotation);
_.memory.Free(nextUserPair.id);
_.memory.Free(nextUserPair.annotation);
}
lowerCaseGroupName = groupName.LowerCopy();
loadedGroupToUsersMap.SetItem(lowerCaseGroupName, newPlayerSet);
@ -140,6 +155,29 @@ private final function LoadLocalGroup(
groupConfig.FreeSelf();
}
private final function IDAnnotationPair ParseConfigUserName(
string configUserName)
{
local Parser parser;
local MutableText parsingResult;
local IDAnnotationPair result;
local Text.Character slashSeparator;
parser = _.text.ParseString(configUserName);
slashSeparator = _.text.GetCharacter("/");
if (parser.MUntil(parsingResult, slashSeparator).Match(P("/")).Ok()) {
result.annotation = parser.GetRemainderM().IntoText();
}
result.id = parsingResult.IntoText();
if (result.annotation != none && result.annotation.IsEmpty())
{
result.annotation.FreeSelf();
result.annotation = none;
}
parser.FreeSelf();
return result;
}
private final function SaveLocalData()
{
local Text nextGroup, activeConfigName;
@ -178,6 +216,15 @@ private final function SaveLocalData()
_.memory.Free(activeConfigName);
}
private final function ScheduleConfigSave()
{
if (diskSaveScheduled) {
return;
}
_.scheduler.RequestDiskAccess(self).connect = SaveLocalData;
diskSaveScheduled = true;
}
/**
* Returns names of all available groups that users can belong to.
*
@ -237,6 +284,7 @@ public final function bool AddGroup(BaseText groupName)
loadedUserGroups[loadedUserGroups.length] = lowerCaseGroupName;
// Try loading local `UserGroup`?
LoadLocalGroup(lowerCaseGroupName);
ScheduleConfigSave();
return true;
}
@ -284,6 +332,7 @@ public final function bool RemoveGroup(BaseText groupName)
// Try loading local `UserGroup`?
loadedGroupToUsersMap.RemoveItem(lowerCaseGroupName);
lowerCaseGroupName.FreeSelf();
ScheduleConfigSave();
return true;
}
@ -347,6 +396,7 @@ public final function bool AddSteamIDToGroup(
}
groupUsers.SetItem(steamID, none);
groupUsers.FreeSelf();
ScheduleConfigSave();
return true;
}
@ -402,7 +452,7 @@ public final function bool AddUserIDToGroup(
if (groupName == none) return false;
if (id == none) return false;
steamID = id.GetSteamID64String();
steamID = id.GetUniqueID();
if (steamID == none) return false;
result = AddSteamIDToGroup(steamID, groupName);
@ -472,6 +522,7 @@ public final function bool RemoveSteamIDFromGroup(
hadUser = groupUsers.HasKey(steamID);
groupUsers.RemoveItem(steamID);
groupUsers.FreeSelf();
ScheduleConfigSave();
return hadUser;
}
@ -527,7 +578,7 @@ public final function bool RemoveUserIDFromGroup(
if (groupName == none) return false;
if (id == none) return false;
steamID = id.GetSteamID64String();
steamID = id.GetUniqueID();
if (steamID == none) return false;
result = RemoveSteamIDFromGroup(steamID, groupName);
@ -666,7 +717,7 @@ public final function array<Text> GetGroupsForUserID(UserID id)
local array<Text> result;
if (id == none) return result;
steamID = id.GetSteamID64String();
steamID = id.GetUniqueID();
if (steamID == none) return result;
result = GetGroupsForSteamID(steamID);
@ -709,6 +760,9 @@ public final function array<Text> GetGroupsForUser(User user)
/**
* Returns `UserID`s of all users that belong into the group named `groupName`.
*
* @see For more information alongside `UserID`s use
* `GetAnnotatedGroupMembers()`.
*
* In case this feature is configured to load user groups from a database
* (either local or remote), the returned value is a locally cached one.
* This helps us avoid having to query database each time we want to check
@ -755,6 +809,63 @@ public final function array<UserID> GetGroupMembers(BaseText groupName)
return result;
}
/**
* Returns annotated `UserID`s of all users that belong into the group named
* `groupName`. `UserID`s aren't necessarily human-readable (e.g. SteamID)
* and to help organize configs they can be annotated with a `Text` name.
* This method returns `UserID` alongside such annotation, if it exists.
*
* @see For just `UserID`s use `GetGroupMembers()`.
*
* In case this feature is configured to load user groups from a database
* (either local or remote), the returned value is a locally cached one.
* This helps us avoid having to query database each time we want to check
* something about user groups, but it also means we might have an outdated
* information.
*
* @param groupName Name of the group. Case-insensitive.
* @return Array with `UserID`s for every user in the user group named
* `groupName`. All array elements are guaranteed to be not-`none` and
* correspond to unique players.
* If data wasn't yet loaded - returns empty array.
*/
public final function array<AnnotatedUserID> GetAnnotatedGroupMembers(
BaseText groupName)
{
local int i;
local Text lowerCaseGroupName;
local HashTable groupUsers;
local array<Text> groupUsersNames;
local AnnotatedUserID nextRecord;
local array<AnnotatedUserID> result;
if (loadedGroupToUsersMap == none) return result;
if (groupName == none) return result;
lowerCaseGroupName = groupName.LowerCopy();
groupUsers = loadedGroupToUsersMap.GetHashTable(lowerCaseGroupName);
lowerCaseGroupName.FreeSelf();
if (groupUsers == none) {
groupUsersNames = groupUsers.GetTextKeys();
}
for (i = 0; i < groupUsersNames.length; i += 1)
{
nextRecord.id = UserID(_.memory.Allocate(class'UserID'));
nextRecord.id.Initialize(groupUsersNames[i]);
if (nextRecord.id.IsInitialized())
{
nextRecord.annotation = groupUsers.GetText(groupUsersNames[i]);
result[result.length] = nextRecord;
}
else {
nextRecord.id.FreeSelf();
}
}
_.memory.FreeMany(groupUsersNames);
_.memory.Free(groupUsers);
return result;
}
/**
* Checks whether user given by `UserID` belongs to the group named
* `groupName`.
@ -781,7 +892,7 @@ public final function bool IsUserIDInGroup(UserID id, Text groupName)
if (loadedGroupToUsersMap == none) return false;
if (groupName == none) return false;
if (id == none) return false;
steamID = id.GetSteamID64String();
steamID = id.GetUniqueID();
if (steamID == none) return false;
lowerGroupName = groupName.LowerCopy();

Loading…
Cancel
Save