From 0cdc77fe098cf2095e8aae984dbd7d6e7ac19c6b Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Mon, 12 Dec 2022 04:25:28 +0700 Subject: [PATCH] Add new `UserAPI` methods --- sources/Users/UserAPI.uc | 734 ++++++++++++++++++++++- sources/Users/Users_Feature.uc | 1021 ++++++++++++++++++++++++++++++-- 2 files changed, 1697 insertions(+), 58 deletions(-) diff --git a/sources/Users/UserAPI.uc b/sources/Users/UserAPI.uc index a34b0d1..fbfdd92 100644 --- a/sources/Users/UserAPI.uc +++ b/sources/Users/UserAPI.uc @@ -277,6 +277,29 @@ public final function array GetAvailableGroups() return emptyResult; } +/** + * Returns names of all available groups that users can belong to. + * + * Will only work if `Users_Feature` is active. + * In case active config of `Users_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 array emptyResult; + + if (usersFeature != none) { + return usersFeature.GetAvailableGroups_S(); + } + return emptyResult; +} + /** * Adds a new user group. * @@ -303,6 +326,32 @@ public final function bool AddGroup(BaseText groupName) return false; } +/** + * Adds a new user group. + * + * Will only work if `Users_Feature` is active. + * In case active config of `Users_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) +{ + if (usersFeature != none) { + return usersFeature.AddGroup_S(groupName); + } + return false; +} + /** * Removes existing user group. * @@ -329,6 +378,32 @@ public final function bool RemoveGroup(BaseText groupName) return false; } +/** + * Removes existing user group. + * + * Will only work if `Users_Feature` is active. + * In case active config of `Users_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) +{ + if (usersFeature != none) { + return usersFeature.RemoveGroup_S(groupName); + } + return false; +} + /** * Checks whether group with specified name exists. * @@ -351,6 +426,28 @@ public final function bool IsGroupExisting(BaseText groupName) return false; } +/** + * Checks whether group with specified name exists. + * + * Will only work if `Users_Feature` is active. + * In case active config of `Users_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) +{ + if (usersFeature != none) { + return usersFeature.IsGroupExisting_S(groupName); + } + return false; +} + /** * Adds user with the given SteamID into the specified group. * @@ -429,6 +526,32 @@ public final function bool AddUserIDToGroup( return false; } +/** + * Adds user (given by the `UserID`) into the specified group. + * + * Will only work if `Users_Feature` is active. + * In case active config of `Users_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) +{ + if (usersFeature != none) { + return usersFeature.AddUserIDToGroup_S(id, groupName); + } + return false; +} + /** * Adds given user into the specified group. * @@ -453,6 +576,32 @@ public final function bool AddUserToGroup(User user, BaseText groupName) return false; } +/** + * Adds given user into the specified group. + * + * Will only work if `Users_Feature` is active. + * In case active config of `Users_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) +{ + if (usersFeature != none) { + return usersFeature.AddUserToGroup_S(user, groupName); + } + return false; +} + /** * Removes user with the given SteamID from the specified group. * @@ -531,6 +680,32 @@ public final function bool RemoveUserIDFromGroup( return false; } +/** + * Removes user (given by the `UserID`) from the specified group. + * + * Will only work if `Users_Feature` is active. + * In case active config of `Users_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) +{ + if (usersFeature != none) { + return usersFeature.RemoveUserIDFromGroup_S(id, groupName); + } + return false; +} + /** * Removes user from the specified group. * @@ -555,6 +730,32 @@ public final function bool RemoveUserFromGroup(User user, BaseText groupName) return false; } +/** + * Removes user from the specified group. + * + * Will only work if `Users_Feature` is active. + * In case active config of `Users_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) +{ + if (usersFeature != none) { + return usersFeature.RemoveUserFromGroup_S(user, groupName); + } + return false; +} + /** * Returns names of all groups available for the user given by the SteamID. * @@ -601,14 +802,13 @@ public final function array GetGroupsForSteamID( * @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. + * 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( +public final /*unreal*/ function array GetGroupsForSteamID_S( string steamID) { - local array emptyResult; + local array emptyResult; if (usersFeature != none) { return usersFeature.GetGroupsForSteamID_S(steamID); @@ -645,6 +845,33 @@ public final function array GetGroupsForUserID(UserID id) return emptyResult; } +/** + * Returns names of all groups available for the user given by the `UserID`. + * + * 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. + * + * @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 array emptyResult; + + if (usersFeature != none) { + return usersFeature.GetGroupsForUserID_S(id); + } + return emptyResult; +} + /** * Returns names of all groups available for the user. * @@ -674,6 +901,34 @@ public final function array GetGroupsForUser(User user) return emptyResult; } +/** + * Returns names of all groups available for the user. + * + * 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. + * + * @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 array emptyResult; + + if (usersFeature != none) { + return usersFeature.GetGroupsForUser_S(user); + } + return emptyResult; +} + /** * Returns `UserID`s of all users that belong into the group named `groupName`. * @@ -708,6 +963,7 @@ public final function array GetGroupMembers(BaseText groupName) * `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()`. * @@ -723,6 +979,7 @@ public final function array GetGroupMembers(BaseText groupName) * `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) @@ -735,6 +992,417 @@ public final function array GetAnnotatedGroupMemb return emptyResult; } +/** + * 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. + * + * 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. + * @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) +{ + if (usersFeature != none) { + return usersFeature.GetAnnotationForSteamID(groupName, steamID); + } + return none; +} + +/** + * 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. + * + * 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. + * @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) +{ + if (usersFeature != none) { + return usersFeature.GetAnnotationForSteamID_S(groupName, steamID); + } + return ""; +} + +/** + * 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. + * + * 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. + * @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) +{ + if (usersFeature != none) { + return usersFeature.GetAnnotationForUserID(groupName, id); + } + return none; +} + +/** + * 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. + * + * 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. + * @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) +{ + if (usersFeature != none) { + return usersFeature.GetAnnotationForUserID_S(groupName, id); + } + return ""; +} + +/** + * 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. + * + * 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. + * @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) +{ + if (usersFeature != none) { + return usersFeature.GetAnnotationForUser(groupName, user); + } + return none; +} + +/** + * 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. + * + * 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. + * @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) +{ + if (usersFeature != none) { + return usersFeature.GetAnnotationForUser_S(groupName, user); + } + return ""; +} + +/** + * 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) +{ + if (usersFeature != none) { + usersFeature.SetAnnotationForSteamID(groupName, steamID, annotation); + } +} + +/** + * 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) +{ + if (usersFeature != none) { + usersFeature.SetAnnotationForSteamID_S(groupName, steamID, annotation); + } +} + +/** + * 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) +{ + if (usersFeature != none) { + usersFeature.SetAnnotationForUserID(groupName, id, annotation); + } +} + +/** + * 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) +{ + if (usersFeature != none) { + usersFeature.SetAnnotationForUserID_S(groupName, id, annotation); + } +} + +/** + * 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) +{ + if (usersFeature != none) { + usersFeature.SetAnnotationForUser(groupName, user, annotation); + } +} + +/** + * 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) +{ + if (usersFeature != none) { + usersFeature.SetAnnotationForUser_S(groupName, user, annotation); + } +} + +/** + * Checks whether user given by SteamID belongs to the group named `groupName`. + * + * 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 steamID 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 IsSteamIDInGroup( + BaseText steamID, + BaseText groupName) +{ + if (usersFeature != none) { + return usersFeature.IsSteamIDInGroup(steamID, groupName); + } + return false; +} + +/** + * Checks whether user given by SteamID belongs to the group named `groupName`. + * + * 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 steamID 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 IsSteamIDInGroup_S( + string id, + string groupName) +{ + if (usersFeature != none) { + return usersFeature.IsSteamIDInGroup_S(id, groupName); + } + return false; +} + /** * Checks whether user given by the `UserID` belongs to the group named * `groupName`. @@ -752,7 +1420,7 @@ public final function array GetAnnotatedGroupMemb * `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, Text groupName) +public final function bool IsUserIDInGroup(UserID id, BaseText groupName) { if (usersFeature != none) { return usersFeature.IsUserIDInGroup(id, groupName); @@ -760,6 +1428,33 @@ public final function bool IsUserIDInGroup(UserID id, Text groupName) return false; } +/** + * Checks whether user given by the `UserID` belongs to the group named + * `groupName`. + * + * 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 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) +{ + if (usersFeature != none) { + return usersFeature.IsUserIDInGroup_S(id, groupName); + } + return false; +} + /** * Checks whether user belongs to the given group. * @@ -777,7 +1472,7 @@ public final function bool IsUserIDInGroup(UserID id, Text groupName) * `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, Text groupName) +public final function bool IsUserInGroup(User user, BaseText groupName) { if (usersFeature != none) { return usersFeature.IsUserInGroup(user, groupName); @@ -785,6 +1480,33 @@ public final function bool IsUserInGroup(User user, Text groupName) return false; } +/** + * Checks whether user belongs to the given group. + * + * 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 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) +{ + if (usersFeature != none) { + return usersFeature.IsUserInGroup_S(user, groupName); + } + return false; +} + /** * Checks whether user groups' data was already loaded from the source * (either config file or local/remote database). diff --git a/sources/Users/Users_Feature.uc b/sources/Users/Users_Feature.uc index df0bbf4..29f22df 100644 --- a/sources/Users/Users_Feature.uc +++ b/sources/Users/Users_Feature.uc @@ -45,16 +45,34 @@ var private array loadedUserGroups; // a set data structure (has user id as keys and always `none` as a value). var private HashTable loadedGroupToUsersMap; -var private LoggerAPI.Definition warnNoLocalGroup; +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) @@ -68,6 +86,7 @@ protected function SwapConfig(FeatureConfig config) useDatabase = newConfig.useDatabase; databaseLink = newConfig.databaseLink; availableUserGroups = newConfig.localUserGroup; + class'UserGroup'.static.Initialize(); LoadLocalData(); } @@ -114,7 +133,7 @@ private final function LoadLocalGroupToUserMap() } } -private final function LoadLocalGroup( +private final function bool LoadLocalGroup( BaseText groupName, optional bool localGroupIsExpected) { @@ -126,16 +145,25 @@ private final function LoadLocalGroup( local array groupUserArray; if (groupName == none) { - return; + return false; } groupConfig = UserGroup( - class'UserGroup'.static.GetConfigInstance(loadedUserGroups[i])); + class'UserGroup'.static.GetConfigInstance(groupName)); if (groupConfig == none) { - if (localGroupIsExpected) { + 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; } - return; } // Copy player IDs from `string` array into `HashTable` // that is serving as a set data structure @@ -153,6 +181,7 @@ private final function LoadLocalGroup( lowerCaseGroupName.FreeSelf(); newPlayerSet.FreeSelf(); groupConfig.FreeSelf(); + return true; } private final function IDAnnotationPair ParseConfigUserName( @@ -196,6 +225,7 @@ private final function SaveLocalData() { availableUserGroups[availableUserGroups.length] = nextGroup.ToString(); + SaveLocalGroup(nextGroup); nextGroup.FreeSelf(); } iter.Next(); @@ -210,19 +240,67 @@ private final function SaveLocalData() if (currentConfig != none) { currentConfig.localUserGroup = availableUserGroups; - // !!! save config !!! + 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 = true; + diskSaveScheduled = false; } /** @@ -249,6 +327,29 @@ public final function array GetAvailableGroups() 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. * @@ -268,24 +369,57 @@ public final function array GetAvailableGroups() */ public final function bool AddGroup(BaseText groupName) { - local bool groupExists; - local Text lowerCaseGroupName; + local Text lowerCaseGroupName; if (groupName == none) { return false; } lowerCaseGroupName = groupName.LowerCopy(); - groupExists = loadedGroupToUsersMap.HasKey(lowerCaseGroupName); - if (groupExists) + if (loadedGroupToUsersMap.HasKey(lowerCaseGroupName)) { lowerCaseGroupName.FreeSelf(); return false; } - loadedUserGroups[loadedUserGroups.length] = lowerCaseGroupName; // Try loading local `UserGroup`? - LoadLocalGroup(lowerCaseGroupName); - ScheduleConfigSave(); - return true; + 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; } /** @@ -336,6 +470,34 @@ public final function bool RemoveGroup(BaseText groupName) 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. * @@ -363,6 +525,30 @@ public final function bool IsGroupExisting(BaseText groupName) 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. * @@ -460,6 +646,33 @@ public final function bool AddUserIDToGroup( 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. * @@ -488,6 +701,33 @@ public final function bool AddUserToGroup(User user, BaseText groupName) 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. * @@ -569,9 +809,7 @@ public final /*unreal*/ function bool RemoveSteamIDFromGroup_S( * @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) +public final function bool RemoveUserIDFromGroup(UserID id, BaseText groupName) { local bool result; local Text steamID; @@ -586,6 +824,33 @@ public final function bool RemoveUserIDFromGroup( 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. * @@ -614,6 +879,33 @@ public final function bool RemoveUserFromGroup(User user, BaseText groupName) 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. * @@ -659,13 +951,13 @@ public final function array GetGroupsForSteamID(BaseText steamID) _.memory.Free(nextGroup); _.memory.Free(nextGroupUsers); } + iter.FreeSelf(); immutableSteamID.FreeSelf(); return result; } /** - * Returns names of all groups available for the user with a SteamID given by - * `UserID`. + * 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. @@ -678,19 +970,23 @@ public final function array GetGroupsForSteamID(BaseText steamID) * @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. + * 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( +public final /*unreal*/ function array GetGroupsForSteamID_S( string steamID) { - local array result; - local MutableText wrapper; + local int i; + local array wrapperResult; + local array result; + local MutableText wrapper; wrapper = _.text.FromStringM(steamID); - result = GetGroupsForSteamID(wrapper); + wrapperResult = GetGroupsForSteamID(wrapper); wrapper.FreeSelf(); + for (i = 0; i < wrapperResult.length; i += 1) { + result[i] = _.text.IntoString(wrapperResult[i]); + } return result; } @@ -725,6 +1021,35 @@ public final function array GetGroupsForUserID(UserID id) 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. * @@ -758,10 +1083,7 @@ public final function array 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()`. + * 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. @@ -769,14 +1091,47 @@ public final function array GetGroupsForUser(User user) * 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) -{ + * @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; @@ -814,6 +1169,7 @@ public final function array GetGroupMembers(BaseText groupName) * `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()`. * @@ -828,6 +1184,7 @@ public final function array GetGroupMembers(BaseText groupName) * `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) @@ -845,7 +1202,7 @@ public final function array GetAnnotatedGroupMembers( lowerCaseGroupName = groupName.LowerCopy(); groupUsers = loadedGroupToUsersMap.GetHashTable(lowerCaseGroupName); lowerCaseGroupName.FreeSelf(); - if (groupUsers == none) { + if (groupUsers != none) { groupUsersNames = groupUsers.GetTextKeys(); } for (i = 0; i < groupUsersNames.length; i += 1) @@ -857,8 +1214,10 @@ public final function array GetAnnotatedGroupMembers( nextRecord.annotation = groupUsers.GetText(groupUsersNames[i]); result[result.length] = nextRecord; } - else { + else + { nextRecord.id.FreeSelf(); + nextRecord.id = none; } } _.memory.FreeMany(groupUsersNames); @@ -867,8 +1226,12 @@ public final function array GetAnnotatedGroupMembers( } /** - * Checks whether user given by `UserID` belongs to the group named - * `groupName`. + * 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. @@ -876,23 +1239,458 @@ public final function array GetAnnotatedGroupMembers( * 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. + * @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 bool IsUserIDInGroup(UserID id, Text groupName) +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 steamID; local Text lowerGroupName; local HashTable nextGroupUsers; if (loadedGroupToUsersMap == none) return false; if (groupName == none) return false; - if (id == none) return false; - steamID = id.GetUniqueID(); if (steamID == none) return false; lowerGroupName = groupName.LowerCopy(); @@ -906,6 +1704,95 @@ public final function bool IsUserIDInGroup(UserID id, Text groupName) 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. * @@ -922,7 +1809,7 @@ public final function bool IsUserIDInGroup(UserID id, Text groupName) * `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, Text groupName) +public final function bool IsUserInGroup(User user, BaseText groupName) { local UserID id; local bool result; @@ -936,6 +1823,35 @@ public final function bool IsUserInGroup(User user, Text groupName) 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). @@ -955,4 +1871,5 @@ 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\".") } \ No newline at end of file