/** * Feature for managing Acedia's user groups. Supports both config- and * database-defined information about group sources. An instance of this * feature is necessary for functioning of Acedia's `UserAPI` methods related * to user groups. * Copyright 2022 Anton Tarasenko *------------------------------------------------------------------------------ * This file is part of Acedia. * * Acedia is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License, or * (at your option) any later version. * * Acedia is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Acedia. If not, see . */ class Users_Feature extends Feature; var private /*config*/ bool useDatabase; var private /*config*/ string databaseLink; var private /*config*/ array 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 loadedUserGroups; // `HashTable` (with group name keys) that stores `HashTable`s used as // a set data structure (has user id as keys and always `none` as a value). var private HashTable loadedGroupToUsersMap; var private LoggerAPI.Definition warnNoLocalGroup, errCannotCreateLocalGroup; protected function OnEnabled() { local Commands_Feature feature; _.users._reloadFeature(); feature = Commands_Feature(class'Commands_Feature'.static.GetEnabledInstance()); if (feature != none) { feature.RegisterCommand(class'ACommandUserGroups'); feature.FreeSelf(); } } protected function OnDisabled() { local Commands_Feature feature; _.users._reloadFeature(); feature = Commands_Feature(class'Commands_Feature'.static.GetEnabledInstance()); if (feature != none) { feature.RemoveCommand(class'ACommandUserGroups'); feature.FreeSelf(); } } protected function SwapConfig(FeatureConfig config) { local Users newConfig; newConfig = Users(config); if (newConfig == none) { return; } useDatabase = newConfig.useDatabase; databaseLink = newConfig.databaseLink; availableUserGroups = newConfig.localUserGroup; class'UserGroup'.static.Initialize(); LoadLocalData(); } private final function LoadLocalData() { local int i, j; local bool isDuplicate; local Text nextUserGroup; _.memory.FreeMany(loadedUserGroups); loadedUserGroups.length = 0; for (i = 0; i < availableUserGroups.length; i += 1) { isDuplicate = false; nextUserGroup = _.text.FromString(availableUserGroups[i]); for(j = 0; j < loadedUserGroups.length; j += 1) { if (loadedUserGroups[j].Compare(nextUserGroup, SCASE_INSENSITIVE)) { isDuplicate = true; break; } } if (!isDuplicate) { loadedUserGroups[loadedUserGroups.length] = nextUserGroup.LowerCopy(); } nextUserGroup.FreeSelf(); } LoadLocalGroupToUserMap(); } private final function LoadLocalGroupToUserMap() { local int i; _.memory.Free(loadedGroupToUsersMap); loadedGroupToUsersMap = _.collections.EmptyHashTable(); class'UserGroup'.static.Initialize(); // Go over every group for (i = 0; i < loadedUserGroups.length; i += 1) { LoadLocalGroup(loadedUserGroups[i], true); } } private final function bool LoadLocalGroup( BaseText groupName, optional bool localGroupIsExpected) { local int i; local Text lowerCaseGroupName; local HashTable newPlayerSet; local UserGroup groupConfig; local IDAnnotationPair nextUserPair; local array groupUserArray; if (groupName == none) { return false; } groupConfig = UserGroup( class'UserGroup'.static.GetConfigInstance(groupName)); if (groupConfig == none) { if (localGroupIsExpected) { _.logger.Auto(warnNoLocalGroup).Arg(groupName.Copy()); return false; } class'UserGroup'.static.NewConfig(groupName); groupConfig = UserGroup( class'UserGroup'.static.GetConfigInstance(groupName)); if (groupConfig == none) { _.logger.Auto(errCannotCreateLocalGroup).Arg(groupName.Copy()); return false; } } // Copy player IDs from `string` array into `HashTable` // that is serving as a set data structure newPlayerSet = _.collections.EmptyHashTable(); groupUserArray = groupConfig.user; for (i = 0; i < groupUserArray.length; i += 1) { 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); lowerCaseGroupName.FreeSelf(); newPlayerSet.FreeSelf(); groupConfig.FreeSelf(); return true; } 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; local Users currentConfig; local HashTableIterator iter; if (useDatabase) return; if (loadedGroupToUsersMap == none) return; availableUserGroups.length = 0; iter = HashTableIterator(loadedGroupToUsersMap.Iterate()); while (!iter.HasFinished()) { nextGroup = Text(iter.GetKey()); if (nextGroup != none) { availableUserGroups[availableUserGroups.length] = nextGroup.ToString(); SaveLocalGroup(nextGroup); nextGroup.FreeSelf(); } iter.Next(); } iter.FreeSelf(); activeConfigName = GetCurrentConfig(); if (activeConfigName != none) { currentConfig = Users(class'Users'.static .GetConfigInstance(activeConfigName)); } if (currentConfig != none) { currentConfig.localUserGroup = availableUserGroups; currentConfig.SaveConfig(); } _.memory.Free(currentConfig); _.memory.Free(activeConfigName); } private final function bool SaveLocalGroup(BaseText groupName) { local string nextUserLine; local int userLinesAdded; local Text nextID, nextAnnotation; local Text lowerCaseGroupName; local UserGroup configEntry; local HashTable playersSet; local HashTableIterator iter; if (loadedGroupToUsersMap == none) return false; if (groupName == none) return false; // Create group if missing class'UserGroup'.static.NewConfig(groupName); configEntry = UserGroup(class'UserGroup'.static.GetConfigInstance(groupName)); if (configEntry == none) { // Also add log for loading it // Move these logs and checks into a separate method? _.logger.Auto(errCannotCreateLocalGroup).Arg(groupName.Copy()); return false; } configEntry.user.length = 0; lowerCaseGroupName = groupName.LowerCopy(); playersSet = loadedGroupToUsersMap.GetHashTable(lowerCaseGroupName); lowerCaseGroupName.FreeSelf(); iter = HashTableIterator(playersSet.Iterate()); while(!iter.HasFinished()) { nextID = Text(iter.GetKey()); nextAnnotation = Text(iter.Get()); nextUserLine = nextID.ToString(); if (nextAnnotation != none) { nextUserLine = nextUserLine $ "/" $ nextAnnotation.ToString(); } configEntry.user[userLinesAdded] = nextUserLine; userLinesAdded += 1; iter.Next(); _.memory.Free(nextID); _.memory.Free(nextAnnotation); } iter.FreeSelf(); configEntry.SyncSave(); } private final function ScheduleConfigSave() { if (diskSaveScheduled) { return; } _.scheduler.RequestDiskAccess(self).connect = SaveLocalData; diskSaveScheduled = false; } /** * Returns names of all available groups that users can belong to. * * 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. * * @return Array with names of all available groups. * All array elements are guaranteed to be not-`none`, unique and in * lower case. */ public final function array GetAvailableGroups() { local int i; local array result; for (i = 0; i < loadedUserGroups.length; i += 1) { result[i] = loadedUserGroups[i].Copy(); } return result; } /** * Returns names of all available groups that users can belong to. * * 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. * * @return Array with names of all available groups. All array elements are * guaranteed to be unique and in lower case. */ public final /*unreal*/ function array GetAvailableGroups_S() { local int i; local array result; for (i = 0; i < loadedUserGroups.length; i += 1) { result[i] = loadedUserGroups[i].ToString(); } return result; } /** * Adds a new user group. * * 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. * Changes will always persist for the duration of the match, but writing * them into the (non-config) source might fail, leading to changes being reset * after the level switch. For non-database (config) sources changes will * always be saved. * * @param groupName Name of the group to add. Case-insensitive. * @return `true` if group was added and `false` otherwise (including if it * already existed). */ public final function bool AddGroup(BaseText groupName) { local Text lowerCaseGroupName; if (groupName == none) { return false; } lowerCaseGroupName = groupName.LowerCopy(); if (loadedGroupToUsersMap.HasKey(lowerCaseGroupName)) { lowerCaseGroupName.FreeSelf(); return false; } // Try loading local `UserGroup`? if (LoadLocalGroup(lowerCaseGroupName)) { // Move `lowerCaseGroupName` here, do NOT release the reference loadedUserGroups[loadedUserGroups.length] = lowerCaseGroupName; ScheduleConfigSave(); return true; } // In case we couldn't load local group - we don't need it and // can release the reference lowerCaseGroupName.FreeSelf(); return false; } /** * Adds a new user group. * * 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. * Changes will always persist for the duration of the match, but writing * them into the (non-config) source might fail, leading to changes being reset * after the level switch. For non-database (config) sources changes will * always be saved. * * @param groupName Name of the group to add. Case-insensitive. * @return `true` if group was added and `false` otherwise (including if it * already existed). */ public final /*unreal*/ function bool AddGroup_S(string groupName) { local bool result; local MutableText wrapper; wrapper = _.text.FromStringM(groupName); result = AddGroup(wrapper); wrapper.FreeSelf(); return result; } /** * Removes existing user group. * * 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. * Changes will always persist for the duration of the match, but writing * them into the (non-config) source might fail, leading to changes being reset * after the level switch. For non-database (config) sources changes will * always be saved. * * @param groupName Name of the group to remove. Case-insensitive. * @return `true` if group was removed and `false` otherwise (including if it * didn't exist in the first place). */ public final function bool RemoveGroup(BaseText groupName) { local int i; local bool groupExists; local Text lowerCaseGroupName; if (groupName == none) { return false; } lowerCaseGroupName = groupName.LowerCopy(); groupExists = loadedGroupToUsersMap.HasKey(lowerCaseGroupName); if (!groupExists) { lowerCaseGroupName.FreeSelf(); return false; } for (i = 0; i < loadedUserGroups.length; i += 1) { if (lowercaseGroupName.Compare(loadedUserGroups[i])) { loadedUserGroups.Remove(i, 1); break; } } // Try loading local `UserGroup`? loadedGroupToUsersMap.RemoveItem(lowerCaseGroupName); lowerCaseGroupName.FreeSelf(); ScheduleConfigSave(); return true; } /** * Removes existing user group. * * 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. * Changes will always persist for the duration of the match, but writing * them into the (non-config) source might fail, leading to changes being reset * after the level switch. For non-database (config) sources changes will * always be saved. * * @param groupName Name of the group to remove. Case-insensitive. * @return `true` if group was removed and `false` otherwise (including if it * didn't exist in the first place). */ public final /*unreal*/ function bool RemoveGroup_S(string groupName) { local bool result; local MutableText wrapper; wrapper = _.text.FromStringM(groupName); result = RemoveGroup(wrapper); wrapper.FreeSelf(); return result; } /** * Checks whether group with specified name exists. * * 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 to check existence of. * Case-insensitive. * @return `true` if group exists and `false` otherwise. */ public final function bool IsGroupExisting(BaseText groupName) { local bool result; local Text lowerCaseGroupName; if (groupName == none) { return false; } lowerCaseGroupName = groupName.LowerCopy(); result = loadedGroupToUsersMap.HasKey(lowerCaseGroupName); lowerCaseGroupName.FreeSelf(); return result; } /** * Checks whether group with specified name exists. * * 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 to check existence of. * Case-insensitive. * @return `true` if group exists and `false` otherwise. */ public final /*unreal*/ function bool IsGroupExisting_S(string groupName) { local bool result; local MutableText wrapper; wrapper = _.text.FromStringM(groupName); result = IsGroupExisting(wrapper); wrapper.FreeSelf(); return result; } /** * Adds user with the given SteamID into the specified group. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param steamID SteamID of the user to add to the group. * @param groupName Name of the group to add user to. Case-insensitive. * @return `true` if user was added to the group (including if her was already * added to it) and `false` in any other case. */ public final function bool AddSteamIDToGroup( BaseText steamID, BaseText groupName) { local Text lowercaseGroupName; local HashTable groupUsers; if (loadedGroupToUsersMap == none) return false; if (groupName == none) return false; lowercaseGroupName = groupName.LowerCopy(); groupUsers = loadedGroupToUsersMap.GetHashTable(lowercaseGroupName); lowercaseGroupName.FreeSelf(); // No specified group? Nothing to add! if (groupUsers == none) { return false; } groupUsers.SetItem(steamID, none); groupUsers.FreeSelf(); ScheduleConfigSave(); return true; } /** * Adds user with the given SteamID into the specified group. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param steamID SteamID of the user to add to the group. * @param groupName Name of the group to add user to. Case-insensitive. * @return `true` if user was added to the group (including if her was already * added to it) and `false` in any other case. */ public final /*unreal*/ function bool AddSteamIDToGroup_S( string steamID, string groupName) { local bool result; local MutableText idWrapper, groupWrapper; idWrapper = _.text.FromStringM(steamID); groupWrapper = _.text.FromStringM(groupName); result = AddSteamIDToGroup(idWrapper, groupWrapper); idWrapper.FreeSelf(); groupWrapper.FreeSelf(); return result; } /** * Adds user (given by the `UserID`) into the specified group. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param id `UserID` of the user to add to the group. * @param groupName Name of the group to add user to. Case-insensitive. * @return `true` if user was added to the group (including if her was already * added to it) and `false` in any other case. */ public final function bool AddUserIDToGroup( UserID id, BaseText groupName) { local bool result; local Text steamID; if (groupName == none) return false; if (id == none) return false; steamID = id.GetUniqueID(); if (steamID == none) return false; result = AddSteamIDToGroup(steamID, groupName); steamID.FreeSelf(); return result; } /** * Adds user (given by the `UserID`) into the specified group. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param id `UserID` of the user to add to the group. * @param groupName Name of the group to add user to. Case-insensitive. * @return `true` if user was added to the group (including if her was already * added to it) and `false` in any other case. */ public final /*unreal*/ function bool AddUserIDToGroup_S( UserID id, string groupName) { local bool result; local MutableText wrapper; wrapper = _.text.FromStringM(groupName); result = AddUserIDToGroup(id, wrapper); wrapper.FreeSelf(); return result; } /** * Adds given user into the specified group. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param user User to add to the group. * @param groupName Name of the group to add user to. Case-insensitive. * @return `true` if user was added to the group (including if her was already * added to it) and `false` in any other case. */ public final function bool AddUserToGroup(User user, BaseText groupName) { local bool result; local UserID id; if (groupName == none) return false; if (user == none) return false; id = user.GetID(); result = AddUserIDToGroup(id, groupName); _.memory.Free(id); return result; } /** * Adds given user into the specified group. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param user User to add to the group. * @param groupName Name of the group to add user to. Case-insensitive. * @return `true` if user was added to the group (including if her was already * added to it) and `false` in any other case. */ public final /*unreal*/ function bool AddUserToGroup_S( User user, string groupName) { local bool result; local MutableText wrapper; wrapper = _.text.FromStringM(groupName); result = AddUserToGroup(user, wrapper); wrapper.FreeSelf(); return result; } /** * Removes user with the given SteamID from the specified group. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param steamID SteamID of the user to remove to the group. * @param groupName Name of the group to remove user to. Case-insensitive. * @return `true` if user was removed to the group (including if her was * already removed to it) and `false` in any other case. */ public final function bool RemoveSteamIDFromGroup( BaseText steamID, BaseText groupName) { local bool hadUser; local Text lowercaseGroupName; local HashTable groupUsers; if (groupName == none) return false; if (loadedGroupToUsersMap == none) return false; lowercaseGroupName = groupName.LowerCopy(); groupUsers = loadedGroupToUsersMap.GetHashTable(lowercaseGroupName); lowercaseGroupName.FreeSelf(); if (groupUsers == none) { return false; } hadUser = groupUsers.HasKey(steamID); groupUsers.RemoveItem(steamID); groupUsers.FreeSelf(); ScheduleConfigSave(); return hadUser; } /** * Removes user with the given SteamID from the specified group. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param steamID SteamID of the user to remove to the group. * @param groupName Name of the group to remove user to. Case-insensitive. * @return `true` if user was removed to the group (including if her was * already removed to it) and `false` in any other case. */ public final /*unreal*/ function bool RemoveSteamIDFromGroup_S( string steamID, string groupName) { local bool result; local MutableText idWrapper, groupWrapper; idWrapper = _.text.FromStringM(steamID); groupWrapper = _.text.FromStringM(groupName); result = RemoveSteamIDFromGroup(idWrapper, groupWrapper); idWrapper.FreeSelf(); groupWrapper.FreeSelf(); return result; } /** * Removes user (given by the `UserID`) from the specified group. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param id `UserID` of the user to remove to the group. * @param groupName Name of the group to remove user to. Case-insensitive. * @return `true` if user was removed to the group (including if her was * already removed to it) and `false` in any other case. */ public final function bool RemoveUserIDFromGroup(UserID id, BaseText groupName) { local bool result; local Text steamID; if (groupName == none) return false; if (id == none) return false; steamID = id.GetUniqueID(); if (steamID == none) return false; result = RemoveSteamIDFromGroup(steamID, groupName); steamID.FreeSelf(); return result; } /** * Removes user (given by the `UserID`) from the specified group. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param id `UserID` of the user to remove to the group. * @param groupName Name of the group to remove user to. Case-insensitive. * @return `true` if user was removed to the group (including if her was * already removed to it) and `false` in any other case. */ public final /*unreal*/ function bool RemoveUserIDFromGroup_S( UserID id, string groupName) { local bool result; local MutableText groupWrapper; groupWrapper = _.text.FromStringM(groupName); result = RemoveUserIDFromGroup(id, groupWrapper); groupWrapper.FreeSelf(); return result; } /** * Removes user from the specified group. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param user User to remove to the group. * @param groupName Name of the group to remove user to. Case-insensitive. * @return `true` if user was removed to the group (including if her was * already removed to it) and `false` in any other case. */ public final function bool RemoveUserFromGroup(User user, BaseText groupName) { local bool result; local UserID id; if (groupName == none) return false; if (user == none) return false; id = user.GetID(); result = RemoveUserIDFromGroup(id, groupName); _.memory.Free(id); return result; } /** * Removes user from the specified group. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param user User to remove to the group. * @param groupName Name of the group to remove user to. Case-insensitive. * @return `true` if user was removed to the group (including if her was * already removed to it) and `false` in any other case. */ public final /*unreal*/ function bool RemoveUserFromGroup_S( User user, string groupName) { local bool result; local MutableText groupWrapper; groupWrapper = _.text.FromStringM(groupName); result = RemoveUserFromGroup(user, groupWrapper); groupWrapper.FreeSelf(); return result; } /** * Returns names of all groups available for the user given by SteamID. * * 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. * * @see `GetGroupsForUserID()` / `GetGroupsForUser()`. * * @param steamID SteamID of the user. * Must be specified in a SteamID64 format, e.g. "76561197960287930". * @return Array with names of the groups of the specified user. * All array elements are guaranteed to be not-`none`, unique and in * lower case. * If passed SteamID is `none` or data wasn't yet loaded - returns empty * array. */ public final function array GetGroupsForSteamID(BaseText steamID) { local Text immutableSteamID; local array result; local HashTableIterator iter; local Text nextGroup; local HashTable nextGroupUsers; if (loadedGroupToUsersMap == none) return result; if (steamID == none) return result; immutableSteamID = steamID.LowerCopy(); iter = HashTableIterator(loadedGroupToUsersMap.Iterate()); while (!iter.HasFinished()) { nextGroup = Text(iter.GetKey()); nextGroupUsers = HashTable(iter.Get()); if ( nextGroup != none && nextGroupUsers != none && nextGroupUsers.HasKey(steamID)) { result[result.length] = nextGroup.Copy(); } iter.Next(); _.memory.Free(nextGroup); _.memory.Free(nextGroupUsers); } iter.FreeSelf(); immutableSteamID.FreeSelf(); return result; } /** * Returns names of all groups available for the user with a given SteamID. * * 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. * * @see `GetGroupsForUserID()` / `GetGroupsForUser()`. * * @param steamID SteamID of the user. * Must be specified in a SteamID64 format, e.g. "76561197960287930". * @return Array with names of the groups of the specified user. * All array elements are guaranteed to be unique and in lower case. * If data wasn't yet loaded - returns empty array. */ public final /*unreal*/ function array GetGroupsForSteamID_S( string steamID) { local int i; local array wrapperResult; local array result; local MutableText wrapper; wrapper = _.text.FromStringM(steamID); wrapperResult = GetGroupsForSteamID(wrapper); wrapper.FreeSelf(); for (i = 0; i < wrapperResult.length; i += 1) { result[i] = _.text.IntoString(wrapperResult[i]); } return result; } /** * Returns names of all groups available for the user given by `UserID`. * * 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. * * @see `GetGroupsForSteamID()` / `GetGroupsForUser()`. * * @param id ID of the user. * @return Array with names of the groups of the specified user. * All array elements are guaranteed to be not-`none`, unique and in * lower case. * If data wasn't yet loaded - returns empty array. */ public final function array GetGroupsForUserID(UserID id) { local Text steamID; local array result; if (id == none) return result; steamID = id.GetUniqueID(); if (steamID == none) return result; result = GetGroupsForSteamID(steamID); steamID.FreeSelf(); return result; } /** * Returns names of all groups available for the user given by `UserID`. * * 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. * * @see `GetGroupsForSteamID()` / `GetGroupsForUser()`. * * @param id ID of the user. * @return Array with names of the groups of the specified user. * All array elements are guaranteed to be unique and in lower case. * If data wasn't yet loaded - returns empty array. */ public final /*unreal*/ function array GetGroupsForUserID_S(UserID id) { local int i; local array wrapperResult; local array result; wrapperResult = GetGroupsForUserID(id); for (i = 0; i < wrapperResult.length; i += 1) { result[i] = _.text.IntoString(wrapperResult[i]); } return result; } /** * Returns names of all groups available for the user. * * 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. * * @see `GetGroupsForSteamID()` / `GetGroupsForUserID()`. * * @param user Reference to `User` object that represent the user we are to * find groups for. * @return Array with names of the groups of the specified user. * All array elements are guaranteed to be not-`none`, unique and in * lower case. * If data wasn't yet loaded - returns empty array. */ public final function array GetGroupsForUser(User user) { local UserID id; local array result; if (user == none) { return result; } id = user.GetID(); result = GetGroupsForUserID(id); _.memory.Free(id); return result; } /** * Returns names of all groups available for the user. * * 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. * * @see `GetGroupsForSteamID()` / `GetGroupsForUserID()`. * * @param user Reference to `User` object that represent the user we are to * find groups for. * @return Array with names of the groups of the specified user. * All array elements are guaranteed to be unique and in lower case. * If data wasn't yet loaded - returns empty array. */ public final /*unreal*/ function array GetGroupsForUser_S(User user) { local int i; local array wrapperResult; local array result; wrapperResult = GetGroupsForUser(user); for (i = 0; i < wrapperResult.length; i += 1) { result[i] = _.text.IntoString(wrapperResult[i]); } return result; } /** * 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 * 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 GetGroupMembers(BaseText groupName) { local int i; local Text lowerCaseGroupName; local HashTable groupUsers; local array groupUsersNames; local UserID nextUserID; local array 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(); } _.memory.Free(groupUsers); for (i = 0; i < groupUsersNames.length; i += 1) { nextUserID = UserID(_.memory.Allocate(class'UserID')); nextUserID.Initialize(groupUsersNames[i]); if (nextUserID.IsInitialized()) { result[result.length] = nextUserID; } else { nextUserID.FreeSelf(); } } _.memory.FreeMany(groupUsersNames); 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. * NOTE: Same user can have different annotations in different groups. * * @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. * WARNING: References in fields of the returned `struct`s must be freed. */ public final function array GetAnnotatedGroupMembers( BaseText groupName) { local int i; local Text lowerCaseGroupName; local HashTable groupUsers; local array groupUsersNames; local AnnotatedUserID nextRecord; local array 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(); nextRecord.id = none; } } _.memory.FreeMany(groupUsersNames); _.memory.Free(groupUsers); return result; } /** * Returns annotation for user given by SteamID inside the group named * `groupName`. `UserID`s that are used to store belonging users into groups * aren't necessarily human-readable (e.g. SteamID) and to help organize * configs they can be annotated with a `Text` name. * This method returns these annotations, if they exists. * NOTE: Same user can have different annotations in different groups. * * 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. * @param steamID ID of the user, in whose annotation we are interested. * @return Annotation for the specified user inside the specified group. * `none` if either group doesn't exist, user doesn't belong to it or it is * not annotated. * If data wasn't yet loaded - returns `none`. */ public final function Text GetAnnotationForSteamID( BaseText groupName, BaseText steamID) { local Text result; local Text lowerCaseGroupName; local HashTable groupUsers; if (loadedGroupToUsersMap == none) return result; if (groupName == none) return result; if (steamID == none) return result; lowerCaseGroupName = groupName.LowerCopy(); groupUsers = loadedGroupToUsersMap.GetHashTable(lowerCaseGroupName); lowerCaseGroupName.FreeSelf(); if (groupUsers != none) { result = groupUsers.GetText(steamID); } _.memory.Free(groupUsers); return result; } /** * Returns annotation for user given by SteamID inside the group named * `groupName`. `UserID`s that are used to store belonging users into groups * aren't necessarily human-readable (e.g. SteamID) and to help organize * configs they can be annotated with a `Text` name. * This method returns these annotations, if they exists. * NOTE: Same user can have different annotations in different groups. * * 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. * @param steamID ID of the user, in whose annotation we are interested. * @return Annotation for the specified user inside the specified group. * Empty `string` if either group doesn't exist, user doesn't belong to it * or it is not annotated. * If data wasn't yet loaded - returns empty `string`. */ public final /*unreal*/ function string GetAnnotationForSteamID_S( string groupName, string steamID) { local Text result; local MutableText groupWrapper, idWrapper; groupWrapper = _.text.FromStringM(steamID); idWrapper = _.text.FromStringM(steamID); result = GetAnnotationForSteamID(groupWrapper, idWrapper); groupWrapper.FreeSelf(); idWrapper.FreeSelf(); return _.text.IntoString(result); } /** * Returns annotation for user given by `UserID` inside the group named * `groupName`. `UserID`s that are used to store belonging users into groups * aren't necessarily human-readable (e.g. SteamID) and to help organize * configs they can be annotated with a `Text` name. * This method returns these annotations, if they exists. * NOTE: Same user can have different annotations in different groups. * * 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. * @param steamID ID of the user, in whose annotation we are interested. * @return Annotation for the specified user inside the specified group. * `none` if either group doesn't exist, user doesn't belong to it or it is * not annotated. * If data wasn't yet loaded - returns `none`. */ public final function Text GetAnnotationForUserID(BaseText groupName, UserID id) { local Text steamID; local Text result; if (id == none) return result; steamID = id.GetUniqueID(); if (steamID == none) return result; result = GetAnnotationForSteamID(groupName, steamID); steamID.FreeSelf(); return result; } /** * Returns annotation for user given by `UserID` inside the group named * `groupName`. `UserID`s that are used to store belonging users into groups * aren't necessarily human-readable (e.g. SteamID) and to help organize * configs they can be annotated with a `Text` name. * This method returns these annotations, if they exists. * NOTE: Same user can have different annotations in different groups. * * 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. * @param steamID ID of the user, in whose annotation we are interested. * @return Annotation for the specified user inside the specified group. * Empty `string` if either group doesn't exist, user doesn't belong to it * or it is not annotated. * If data wasn't yet loaded - returns empty `string`. */ public final /*unreal*/ function string GetAnnotationForUserID_S( string groupName, UserID id) { local Text result; local MutableText wrapper; if (id == none) { return ""; } wrapper = _.text.FromStringM(groupName); result = GetAnnotationForUserID(wrapper, id); wrapper.FreeSelf(); return _.text.IntoString(result); } /** * Returns annotation for user given by `User` inside the group named * `groupName`. `UserID`s that are used to store belonging users into groups * aren't necessarily human-readable (e.g. SteamID) and to help organize * configs they can be annotated with a `Text` name. * This method returns these annotations, if they exists. * NOTE: Same user can have different annotations in different groups. * * 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. * @param steamID ID of the user, in whose annotation we are interested. * @return Annotation for the specified user inside the specified group. * `none` if either group doesn't exist, user doesn't belong to it or it is * not annotated. * If data wasn't yet loaded - returns `none`. */ public final function Text GetAnnotationForUser(BaseText groupName, User user) { local UserID id; local Text result; if (user == none) { return result; } id = user.GetID(); result = GetAnnotationForUserID(groupName, id); _.memory.Free(id); return result; } /** * Returns annotation for user given by `User` inside the group named * `groupName`. `UserID`s that are used to store belonging users into groups * aren't necessarily human-readable (e.g. SteamID) and to help organize * configs they can be annotated with a `Text` name. * This method returns these annotations, if they exists. * NOTE: Same user can have different annotations in different groups. * * 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. * @param steamID ID of the user, in whose annotation we are interested. * @return Annotation for the specified user inside the specified group. * Empty `string` if either group doesn't exist, user doesn't belong to it * or it is not annotated. * If data wasn't yet loaded - returns empty `string`. */ public final /*unreal*/ function string GetAnnotationForUser_S( string groupName, User user) { local Text result; local MutableText wrapper; if (user == none) { return ""; } wrapper = _.text.FromStringM(groupName); result = GetAnnotationForUser(wrapper, user); wrapper.FreeSelf(); return _.text.IntoString(result); } /** * Changes annotation for user given by SteamID inside the group named * `groupName`. `UserID`s that are used to store belonging users into groups * aren't necessarily human-readable (e.g. SteamID) and to help organize * configs they can be annotated with a `Text` name. * This method allows to change these annotations. * NOTE: Same user can have different annotations in different groups. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param groupName Name of the group. Case-insensitive. * @param steamID ID of the user, whose annotation we want to change. * @param annotation New annotation for the specified user. */ public final function SetAnnotationForSteamID( BaseText groupName, BaseText steamID, BaseText annotation) { local Text lowerCaseGroupName; local HashTable groupUsers; if (loadedGroupToUsersMap == none) return; if (groupName == none) return; if (steamID == none) return; lowerCaseGroupName = groupName.LowerCopy(); groupUsers = loadedGroupToUsersMap.GetHashTable(lowerCaseGroupName); lowerCaseGroupName.FreeSelf(); if (groupUsers != none && groupUsers.HasKey(steamID)) { groupUsers.SetItem(steamID, annotation); } _.memory.Free(groupUsers); } /** * Changes annotation for user given by SteamID inside the group named * `groupName`. `UserID`s that are used to store belonging users into groups * aren't necessarily human-readable (e.g. SteamID) and to help organize * configs they can be annotated with a `Text` name. * This method allows to change these annotations. * NOTE: Same user can have different annotations in different groups. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param groupName Name of the group. Case-insensitive. * @param steamID ID of the user, whose annotation we want to change. * @param annotation New annotation for the specified user. Empty annotation * means simply removing any existing annotation. */ public final /*unreal*/ function SetAnnotationForSteamID_S( string groupName, string steamID, string annotation) { local MutableText groupWrapper; local MutableText idWrapper; local MutableText annotationWrapper; groupWrapper = _.text.FromStringM(groupName); idWrapper = _.text.FromStringM(steamID); // Leave `annotationWrapper` as `none` for empty annotations if (annotation != "") { annotationWrapper = _.text.FromStringM(annotation); } SetAnnotationForSteamID(groupWrapper, idWrapper, annotationWrapper); groupWrapper.FreeSelf(); idWrapper.FreeSelf(); _.memory.Free(annotationWrapper); } /** * Changes annotation for user given by `UserID` inside the group named * `groupName`. `UserID`s that are used to store belonging users into groups * aren't necessarily human-readable (e.g. SteamID) and to help organize * configs they can be annotated with a `Text` name. * This method allows to change these annotations. * NOTE: Same user can have different annotations in different groups. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param groupName Name of the group. Case-insensitive. * @param steamID ID of the user, whose annotation we want to change. * @param annotation New annotation for the specified user. */ public final function SetAnnotationForUserID( BaseText groupName, UserID id, BaseText annotation) { local Text steamID; if (id == none) return; steamID = id.GetUniqueID(); if (steamID == none) return; SetAnnotationForSteamID(groupName, steamID, annotation); steamID.FreeSelf(); } /** * Changes annotation for user given by `UserID` inside the group named * `groupName`. `UserID`s that are used to store belonging users into groups * aren't necessarily human-readable (e.g. SteamID) and to help organize * configs they can be annotated with a `Text` name. * This method allows to change these annotations. * NOTE: Same user can have different annotations in different groups. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param groupName Name of the group. Case-insensitive. * @param steamID ID of the user, whose annotation we want to change. * @param annotation New annotation for the specified user. Empty annotation * means simply removing any existing annotation. */ public final /*unreal*/ function SetAnnotationForUserID_S( string groupName, UserID id, string annotation) { local MutableText groupWrapper; local MutableText annotationWrapper; groupWrapper = _.text.FromStringM(groupName); // Leave `annotationWrapper` as `none` for empty annotations if (annotation != "") { annotationWrapper = _.text.FromStringM(annotation); } SetAnnotationForUserID(groupWrapper, id, annotationWrapper); groupWrapper.FreeSelf(); _.memory.Free(annotationWrapper); } /** * Changes annotation for user given by `User` inside the group named * `groupName`. `UserID`s that are used to store belonging users into groups * aren't necessarily human-readable (e.g. SteamID) and to help organize * configs they can be annotated with a `Text` name. * This method allows to change these annotations. * NOTE: Same user can have different annotations in different groups. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param groupName Name of the group. Case-insensitive. * @param steamID ID of the user, whose annotation we want to change. * @param annotation New annotation for the specified user. */ public final function SetAnnotationForUser( BaseText groupName, User user, BaseText annotation) { local UserID id; if (user == none) { return; } id = user.GetID(); SetAnnotationForUserID(groupName, id, annotation); _.memory.Free(id); } /** * Changes annotation for user given by `User` inside the group named * `groupName`. `UserID`s that are used to store belonging users into groups * aren't necessarily human-readable (e.g. SteamID) and to help organize * configs they can be annotated with a `Text` name. * This method allows to change these annotations. * NOTE: Same user can have different annotations in different groups. * * In case this feature is configured to load user groups from a database * (either local or remote), changes are guaranteed to be made to the locally * cached copy that will persist for the duration of the game. Method will also * attempt to change the database value, but that is not guaranteed to succeed, * meaning that changes might not be saved for later matches. * * @param groupName Name of the group. Case-insensitive. * @param steamID ID of the user, whose annotation we want to change. * @param annotation New annotation for the specified user. Empty annotation * means simply removing any existing annotation. */ public final /*unreal*/ function SetAnnotationForUser_S( string groupName, User user, string annotation) { local MutableText groupWrapper; local MutableText annotationWrapper; groupWrapper = _.text.FromStringM(groupName); // Leave `annotationWrapper` as `none` for empty annotations if (annotation != "") { annotationWrapper = _.text.FromStringM(annotation); } SetAnnotationForUser(groupWrapper, user, annotationWrapper); groupWrapper.FreeSelf(); _.memory.Free(annotationWrapper); } /** * Checks whether user given by SteamID belongs to the group named `groupName`. * * 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 steamID SteamID of the user to remove to the group. * @param groupName Name of the group to remove user to. Case-insensitive. * @return `true` if user was removed to the group (including if her was * already removed to it) and `false` in any other case. */ public final function bool IsSteamIDInGroup( BaseText steamID, BaseText groupName) { local bool result; local Text lowerGroupName; local HashTable nextGroupUsers; if (loadedGroupToUsersMap == none) return false; if (groupName == none) return false; if (steamID == none) return false; lowerGroupName = groupName.LowerCopy(); nextGroupUsers = loadedGroupToUsersMap.GetHashTable(lowerGroupName); lowerGroupName.FreeSelf(); if (nextGroupUsers != none) { result = nextGroupUsers.HasKey(steamID); } _.memory.Free(nextGroupUsers); return result; } /** * Checks whether user given by SteamID belongs to the group named `groupName`. * * 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 steamID SteamID of the user to remove to the group. * @param groupName Name of the group to remove user to. Case-insensitive. * @return `true` if user was removed to the group (including if her was * already removed to it) and `false` in any other case. */ public final /*unreal*/ function bool IsSteamIDInGroup_S( string steamID, string groupName) { local bool result; local MutableText idWrapper, groupWrapper; idWrapper = _.text.FromStringM(steamID); groupWrapper = _.text.FromStringM(groupName); result = IsSteamIDInGroup(idWrapper, groupWrapper); idWrapper.FreeSelf(); groupWrapper.FreeSelf(); return result; } /** * Checks whether user given by `UserID` belongs to the group named * `groupName`. * * 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 id ID of the user to check. * @param groupName Name of the group. Case-insensitive. * @return `true` if user with an ID given by `id` belongs to the group named * `groupName` and false if: it does not, either of the parameters is * invalid or group data wasn't yet properly loaded. */ public final function bool IsUserIDInGroup(UserID id, BaseText groupName) { local bool result; local Text steamID; if (groupName == none) return false; if (id == none) return false; steamID = id.GetUniqueID(); if (steamID == none) return false; result = IsSteamIDInGroup(steamID, groupName); steamID.FreeSelf(); return result; } /** * Checks whether user given by `UserID` belongs to the group named * `groupName`. * * 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 id ID of the user to check. * @param groupName Name of the group. Case-insensitive. * @return `true` if user with an ID given by `id` belongs to the group named * `groupName` and false if: it does not, either of the parameters is * invalid or group data wasn't yet properly loaded. */ public final /*unreal*/ function bool IsUserIDInGroup_S( UserID id, string groupName) { local bool result; local MutableText wrapper; wrapper = _.text.FromStringM(groupName); result = IsUserIDInGroup(id, wrapper); wrapper.FreeSelf(); return result; } /** * Checks whether user belongs to the specified group. * * 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 user Reference to `User` object that represent the user we are to * check for belonging to the group. * @param groupName Name of the group. Case-insensitive. * @return `true` if user with an ID given by `id` belongs to the group named * `groupName` and false if: it does not, either of the parameters is * invalid or group data wasn't yet properly loaded. */ public final function bool IsUserInGroup(User user, BaseText groupName) { local UserID id; local bool result; if (user == none) { return false; } id = user.GetID(); result = IsUserIDInGroup(id, groupName); _.memory.Free(id); return result; } /** * Checks whether user belongs to the specified group. * * 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 user Reference to `User` object that represent the user we are to * check for belonging to the group. * @param groupName Name of the group. Case-insensitive. * @return `true` if user with an ID given by `id` belongs to the group named * `groupName` and false if: it does not, either of the parameters is * invalid or group data wasn't yet properly loaded. */ public final /*unreal*/ function bool IsUserInGroup_S( User user, string groupName) { local bool result; local MutableText wrapper; wrapper = _.text.FromStringM(groupName); result = IsUserInGroup(user, wrapper); wrapper.FreeSelf(); return result; } /** * Checks whether user groups' data was already loaded from the source * (either config file or local/remote database). * * Data loaded once is cached and this method returning `true` does not * guarantee that is isn't outdated. Additional, asynchronous queries must be * made to check for that. * * @return `true` if user groups' data was loaded and `false` otherwise. */ public final function bool IsUserGroupDataLoaded() { return true; } defaultproperties { configClass = class'Users' warnNoLocalGroup = (l=LOG_Warning,m="Expected config to contain `UserGroup` named \"%1\", but it is missing. \"AcediaUsers.ini\" might be misconfigured.") errCannotCreateLocalGroup = (l=LOG_Error,m="Failed to create config section for `UserGroup` named \"%1\".") }