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

36
sources/Users/UserAPI.uc

@ -18,6 +18,7 @@
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. * along with Acedia. If not, see <https://www.gnu.org/licenses/>.
*/ */
class UserAPI extends AcediaObject class UserAPI extends AcediaObject
dependson(Users_Feature)
config(AcediaSystem); config(AcediaSystem);
var private config string userdataDBLink; 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`. * 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. * Will only work if `Users_Feature` is active.
* In case active config of `Users_Feature` is set up to load user groups * 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 * 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; 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 * Checks whether user given by the `UserID` belongs to the group named
* `groupName`. * `groupName`.

135
sources/Users/Users_Feature.uc

@ -26,6 +26,19 @@ var private /*config*/ bool useDatabase;
var private /*config*/ string databaseLink; var private /*config*/ string databaseLink;
var private /*config*/ array<string> availableUserGroups; 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 // List of all available user groups for current config
var private array<Text> loadedUserGroups; var private array<Text> loadedUserGroups;
// `HashTable` (with group name keys) that stores `HashTable`s used as // `HashTable` (with group name keys) that stores `HashTable`s used as
@ -105,11 +118,12 @@ private final function LoadLocalGroup(
BaseText groupName, BaseText groupName,
optional bool localGroupIsExpected) optional bool localGroupIsExpected)
{ {
local int i; local int i;
local Text newSteamID, lowerCaseGroupName; local Text lowerCaseGroupName;
local HashTable newPlayerSet; local HashTable newPlayerSet;
local UserGroup groupConfig; local UserGroup groupConfig;
local array<string> groupUserArray; local IDAnnotationPair nextUserPair;
local array<string> groupUserArray;
if (groupName == none) { if (groupName == none) {
return; return;
@ -129,9 +143,10 @@ private final function LoadLocalGroup(
groupUserArray = groupConfig.user; groupUserArray = groupConfig.user;
for (i = 0; i < groupUserArray.length; i += 1) for (i = 0; i < groupUserArray.length; i += 1)
{ {
newSteamID = _.text.FromString(groupUserArray[i]); nextUserPair = ParseConfigUserName(groupUserArray[i]);
newPlayerSet.SetItem(newSteamID, none); newPlayerSet.SetItem(nextUserPair.id, nextUserPair.annotation);
newSteamID.FreeSelf(); _.memory.Free(nextUserPair.id);
_.memory.Free(nextUserPair.annotation);
} }
lowerCaseGroupName = groupName.LowerCopy(); lowerCaseGroupName = groupName.LowerCopy();
loadedGroupToUsersMap.SetItem(lowerCaseGroupName, newPlayerSet); loadedGroupToUsersMap.SetItem(lowerCaseGroupName, newPlayerSet);
@ -140,6 +155,29 @@ private final function LoadLocalGroup(
groupConfig.FreeSelf(); 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() private final function SaveLocalData()
{ {
local Text nextGroup, activeConfigName; local Text nextGroup, activeConfigName;
@ -178,6 +216,15 @@ private final function SaveLocalData()
_.memory.Free(activeConfigName); _.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. * 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; loadedUserGroups[loadedUserGroups.length] = lowerCaseGroupName;
// Try loading local `UserGroup`? // Try loading local `UserGroup`?
LoadLocalGroup(lowerCaseGroupName); LoadLocalGroup(lowerCaseGroupName);
ScheduleConfigSave();
return true; return true;
} }
@ -284,6 +332,7 @@ public final function bool RemoveGroup(BaseText groupName)
// Try loading local `UserGroup`? // Try loading local `UserGroup`?
loadedGroupToUsersMap.RemoveItem(lowerCaseGroupName); loadedGroupToUsersMap.RemoveItem(lowerCaseGroupName);
lowerCaseGroupName.FreeSelf(); lowerCaseGroupName.FreeSelf();
ScheduleConfigSave();
return true; return true;
} }
@ -347,6 +396,7 @@ public final function bool AddSteamIDToGroup(
} }
groupUsers.SetItem(steamID, none); groupUsers.SetItem(steamID, none);
groupUsers.FreeSelf(); groupUsers.FreeSelf();
ScheduleConfigSave();
return true; return true;
} }
@ -402,7 +452,7 @@ public final function bool AddUserIDToGroup(
if (groupName == none) return false; if (groupName == none) return false;
if (id == none) return false; if (id == none) return false;
steamID = id.GetSteamID64String(); steamID = id.GetUniqueID();
if (steamID == none) return false; if (steamID == none) return false;
result = AddSteamIDToGroup(steamID, groupName); result = AddSteamIDToGroup(steamID, groupName);
@ -472,6 +522,7 @@ public final function bool RemoveSteamIDFromGroup(
hadUser = groupUsers.HasKey(steamID); hadUser = groupUsers.HasKey(steamID);
groupUsers.RemoveItem(steamID); groupUsers.RemoveItem(steamID);
groupUsers.FreeSelf(); groupUsers.FreeSelf();
ScheduleConfigSave();
return hadUser; return hadUser;
} }
@ -527,7 +578,7 @@ public final function bool RemoveUserIDFromGroup(
if (groupName == none) return false; if (groupName == none) return false;
if (id == none) return false; if (id == none) return false;
steamID = id.GetSteamID64String(); steamID = id.GetUniqueID();
if (steamID == none) return false; if (steamID == none) return false;
result = RemoveSteamIDFromGroup(steamID, groupName); result = RemoveSteamIDFromGroup(steamID, groupName);
@ -666,7 +717,7 @@ public final function array<Text> GetGroupsForUserID(UserID id)
local array<Text> result; local array<Text> result;
if (id == none) return result; if (id == none) return result;
steamID = id.GetSteamID64String(); steamID = id.GetUniqueID();
if (steamID == none) return result; if (steamID == none) return result;
result = GetGroupsForSteamID(steamID); 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`. * 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 * 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. * (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 * 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; 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 * Checks whether user given by `UserID` belongs to the group named
* `groupName`. * `groupName`.
@ -781,7 +892,7 @@ public final function bool IsUserIDInGroup(UserID id, Text groupName)
if (loadedGroupToUsersMap == none) return false; if (loadedGroupToUsersMap == none) return false;
if (groupName == none) return false; if (groupName == none) return false;
if (id == none) return false; if (id == none) return false;
steamID = id.GetSteamID64String(); steamID = id.GetUniqueID();
if (steamID == none) return false; if (steamID == none) return false;
lowerGroupName = groupName.LowerCopy(); lowerGroupName = groupName.LowerCopy();

Loading…
Cancel
Save