Anton Tarasenko
2 years ago
15 changed files with 5020 additions and 184 deletions
@ -0,0 +1,35 @@
|
||||
; Acedia requires adding its own `GameRules` to listen to many different |
||||
; game events. |
||||
|
||||
; In this config you can setup Acedia's user groups and persistent data |
||||
; storage. Enabling this feature automatically enables user group support, |
||||
; while persistent data is optional. |
||||
; Databases can be configured in `AcediaDB.ini`. |
||||
[default Users] |
||||
; Configures whether to use database (and which) for storing user groups. |
||||
; Set `useDatabaseForGroupsData` to `false` if you want to define which users |
||||
; belong to what groups inside this config. |
||||
useDatabaseForGroupsData=true |
||||
groupsDatabaseLink=[local]Database:/group_data |
||||
; Configures whether persistent data should be additionally used. |
||||
; It can only be stored inside a database. |
||||
usePersistentData=true |
||||
persistentDataDatabaseLink=[local]Database:/user_data |
||||
; Available groups. Only used if `useDatabaseForGroupsData` is set to `false`. |
||||
localUserGroup=admin |
||||
localUserGroup=moderator |
||||
localUserGroup=trusted |
||||
|
||||
; These groups definitions only work in case you're using a config with |
||||
; `useDatabaseForGroupsData` set to `false`. Simply add new `user=` record, |
||||
; specifying SteamIDs of the players, e.g. `user=76561197960287930`. |
||||
; You can also optionally specify a human-readable lable for the SteamID after |
||||
; slash "/", e.g. `user=76561197960287930/gabe`. |
||||
[admin UserGroup] |
||||
;user= |
||||
|
||||
[moderator UserGroup] |
||||
;user= |
||||
|
||||
[trusted UserGroup] |
||||
;user= |
@ -0,0 +1,655 @@
|
||||
/** |
||||
* Command for displaying help information about registered Acedia's commands. |
||||
* Copyright 2022-2023 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 <https://www.gnu.org/licenses/>. |
||||
*/ |
||||
class ACommandUserGroups extends Command |
||||
dependson(Users_Feature); |
||||
|
||||
protected function BuildData(CommandDataBuilder builder) |
||||
{ |
||||
builder.Name(P("usergroups")) |
||||
.Group(P("admin")) |
||||
.Summary(P("User groups management.")) |
||||
.Describe(P("Allows to add/remove user groups and users to these:" |
||||
@ "groups. Changes made by it will always affect current session," |
||||
@ "but might fail to be saved in case user groups are stored in" |
||||
@ "a database that is either corrupted or in read-only mode.")); |
||||
builder.SubCommand(P("list")) |
||||
.Describe(P("Lists specified groups along with users that belong to" |
||||
@ "them. If no groups were specified at all - lists all available" |
||||
@ "groups.")) |
||||
.OptionalParams() |
||||
.ParamTextList(P("groups")); |
||||
builder.SubCommand(P("add")) |
||||
.Describe(P("Adds a new group")) |
||||
.ParamText(P("group_name")); |
||||
builder.SubCommand(P("remove")) |
||||
.Describe(P("Removes a group")) |
||||
.ParamText(P("group_name")); |
||||
builder.SubCommand(P("addplayer")) |
||||
.Describe(F("Adds new user to the group, specified by the player" |
||||
@ "selector. Can add several players at once." |
||||
@ "Allows to also optionally specify annotation" |
||||
@ "(human-readable name) that can be thought of as" |
||||
@ "a {$TextEmphasis comment}. If annotation isn't specified" |
||||
@ "current nickname will be used as one.")) |
||||
.ParamText(P("group_name")) |
||||
.ParamPlayers(P("player_selector")) |
||||
.OptionalParams() |
||||
.ParamText(P("annotation")); |
||||
builder.SubCommand(P("removeplayer")) |
||||
.Describe(P("Removes user from the group, specified by player selector." |
||||
@ "Can remove several players at once.")) |
||||
.ParamText(P("group_name")) |
||||
.ParamPlayers(P("player_selector")); |
||||
builder.SubCommand(P("adduser")) |
||||
.Describe(F("Adds new user to the group. Allows to also optionally" |
||||
@ "specify annotation (human-readable name) that can be thought of" |
||||
@ "as a {$TextEmphasis comment}.")) |
||||
.ParamText(P("group_name")) |
||||
.ParamText(P("user_id")) |
||||
.OptionalParams() |
||||
.ParamText(P("annotation")); |
||||
builder.SubCommand(P("removeuser")) |
||||
.Describe(P("Removes user from the group. User can be specified by both" |
||||
@ "user's id or annotation, with id taking priority.")) |
||||
.ParamText(P("group_name")) |
||||
.ParamText(P("user_name")); |
||||
builder.Option(P("force")) |
||||
.Describe(P("Allows to force usage of invalid user IDs.")); |
||||
} |
||||
|
||||
protected function Executed(CallData arguments, EPlayer instigator) |
||||
{ |
||||
local bool forceOption; |
||||
local Text groupName, userID, userName, annotation; |
||||
local ArrayList players, groups; |
||||
|
||||
groupName = arguments.parameters.GetText(P("group_name")); |
||||
// For parameters named `user_id`, can only be ID |
||||
userID = arguments.parameters.GetText(P("user_id")); |
||||
// For parameters named `user_id`, can be either ID or annotation |
||||
userName = arguments.parameters.GetText(P("user_name")); |
||||
annotation = arguments.parameters.GetText(P("annotation")); |
||||
// An array of players that can be specified for some commands |
||||
players = arguments.parameters.GetArrayList(P("player_selector")); |
||||
groups = arguments.parameters.GetArrayList(P("groups")); |
||||
forceOption = arguments.options.HasKey(P("force")); |
||||
if (arguments.subCommandName.IsEmpty()) { |
||||
DisplayUserGroups(); |
||||
} |
||||
else if (arguments.subCommandName.Compare(P("list"), SCASE_SENSITIVE)) { |
||||
DisplayUserGroupsWithUsers(groups); |
||||
} |
||||
else if (arguments.subCommandName.Compare(P("add"), SCASE_SENSITIVE)) { |
||||
AddGroup(groupName); |
||||
} |
||||
else if (arguments.subCommandName.Compare(P("remove"), SCASE_SENSITIVE)) { |
||||
RemoveGroup(groupName); |
||||
} |
||||
else if (arguments.subCommandName.Compare(P("adduser"), SCASE_SENSITIVE)) { |
||||
AddOrAnnotateUser(groupName, userID, annotation, forceOption); |
||||
} |
||||
else if (arguments.subCommandName.Compare(P("removeuser"), SCASE_SENSITIVE)) |
||||
{ |
||||
RemoveUser(groupName, userName); |
||||
} |
||||
else if (arguments.subCommandName.Compare(P("addplayer"), SCASE_SENSITIVE)) { |
||||
AddOrAnnotatePlayers(groupName, players, annotation); |
||||
} |
||||
else if (arguments.subCommandName |
||||
.Compare(P("removeplayer"), SCASE_SENSITIVE)) |
||||
{ |
||||
RemovePlayers(groupName, players); |
||||
} |
||||
_.memory.Free(groupName); |
||||
_.memory.Free(userID); |
||||
_.memory.Free(userName); |
||||
_.memory.Free(annotation); |
||||
_.memory.Free(players); |
||||
_.memory.Free(groups); |
||||
} |
||||
|
||||
private function bool ValidateGroupExistence(BaseText groupName) |
||||
{ |
||||
if (_.users.IsGroupExisting(groupName)) { |
||||
return true; |
||||
} |
||||
callerConsole |
||||
.Write(P("Group ")) |
||||
.UseColorOnce(_.color.TextEmphasis) |
||||
.Write(groupName) |
||||
.UseColorOnce(_.color.TextFailure) |
||||
.Write(P(" doesn't exists")) |
||||
.WriteLine(P("!")); |
||||
return false; |
||||
} |
||||
|
||||
private function bool ValidateUserID(BaseText textUserID) |
||||
{ |
||||
local int i; |
||||
|
||||
if (textUserID == none) { |
||||
return false; |
||||
} |
||||
if (textUserID.IsEmpty()) |
||||
{ |
||||
callerConsole.WriteLine(F("Valid User ID" |
||||
@ "{$TextFailure shouldn't be empty}," |
||||
@ "use {$TextEmphasis --force} flag if you want to enforce" |
||||
@ "using it.")); |
||||
return false; |
||||
} |
||||
for (i = 0; i < textUserID.GetLength(); i += 1) |
||||
{ |
||||
if (!_.text.IsDigit(textUserID.GetCharacter(i))) |
||||
{ |
||||
callerConsole.WriteLine(F("Valid User ID" |
||||
@ "{$TextFailure should consist only of digits}," |
||||
@ "use {$TextEmphasis --force} flag if you want" |
||||
@ "to enforce using it.")); |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
private function bool TryAddingUserID( |
||||
BaseText groupName, |
||||
UserID userID, |
||||
BaseText userSpecifiedID) |
||||
{ |
||||
if (_.users.IsUserIDInGroup(userID, groupName)) |
||||
{ |
||||
callerConsole |
||||
.Write(P("User id specified as ")) |
||||
.UseColorOnce(_.color.Gray) |
||||
.Write(userSpecifiedID) |
||||
.UseColorOnce(_.color.TextFailure) |
||||
.Write(P(" is already in the group ")) |
||||
.UseColorOnce(_.color.TextEmphasis) |
||||
.Write(groupName) |
||||
.WriteLine(P("!")); |
||||
} |
||||
else if (_.users.AddUserIDToGroup(userID, groupName)) |
||||
{ |
||||
callerConsole |
||||
.Write(F("{$TextPositive Added} user id specified as ")) |
||||
.UseColorOnce(_.color.Gray) |
||||
.Write(userSpecifiedID) |
||||
.Write(P(" to the group ")) |
||||
.UseColorOnce(_.color.TextEmphasis) |
||||
.Write(groupName) |
||||
.WriteLine(P("!")); |
||||
} |
||||
else |
||||
{ |
||||
callerConsole |
||||
.UseColorOnce(_.color.TextFailure) |
||||
.Write(P("Failed (for unknown reason)")) |
||||
.Write(P(" to add user id ")) |
||||
.UseColorOnce(_.color.Gray).Write(userSpecifiedID) |
||||
.Write(P(" to the group ")) |
||||
.UseColorOnce(_.color.TextEmphasis).Write(groupName) |
||||
.WriteLine(P("!")); |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
private function DisplayAnnotation( |
||||
BaseText userSpecifiedName, |
||||
BaseText groupName, |
||||
BaseText annotation) |
||||
{ |
||||
callerConsole |
||||
.Write(P("Annotation for user id specified as ")) |
||||
.UseColorOnce(_.color.Gray) |
||||
.Write(userSpecifiedName) |
||||
.UseColorOnce(_.color.TextPositive) |
||||
.Write(P(" in the group ")) |
||||
.UseColorOnce(_.color.TextEmphasis) |
||||
.Write(groupName) |
||||
.Write(P(" is set to ")) |
||||
.UseColorOnce(_.color.TextNeutral) |
||||
.WriteLine(annotation); |
||||
} |
||||
|
||||
private function AddOrAnnotateUser( |
||||
BaseText groupName, |
||||
BaseText textUserID, |
||||
BaseText annotation, |
||||
bool forceOption) |
||||
{ |
||||
local UserID id; |
||||
|
||||
if (groupName == none) return; |
||||
if (textUserID == none) return; |
||||
if (!ValidateGroupExistence(groupName)) return; |
||||
if (!forceOption && !ValidateUserID(textUserID)) return; |
||||
|
||||
id = UserID(_.memory.Allocate(class'UserID')); |
||||
id.Initialize(textUserID); |
||||
if (!TryAddingUserID(groupName, id, textUserID) || annotation == none) |
||||
{ |
||||
_.memory.Free(id); |
||||
return; |
||||
} |
||||
_.users.SetAnnotationForUserID(groupName, id, annotation); |
||||
_.memory.Free(id); |
||||
DisplayAnnotation(textUserID, groupName, annotation); |
||||
} |
||||
|
||||
private function AddOrAnnotatePlayers( |
||||
BaseText groupName, |
||||
ArrayList players, |
||||
BaseText annotation) |
||||
{ |
||||
local int i; |
||||
local BaseText playerName, nextAnnotation; |
||||
local EPlayer nextPlayer; |
||||
local UserID nextID; |
||||
|
||||
if (groupName == none) return; |
||||
if (players == none) return; |
||||
if (!ValidateGroupExistence(groupName)) return; |
||||
|
||||
for (i = 0; i < players.GetLength(); i += 1) |
||||
{ |
||||
nextPlayer = EPlayer(players.GetItem(i)); |
||||
if (nextPlayer == none) { |
||||
continue; |
||||
} |
||||
playerName = nextPlayer.GetName(); |
||||
nextID = nextPlayer.GetUserID(); |
||||
if (TryAddingUserID(groupName, nextID, playerName)) |
||||
{ |
||||
if (annotation == none) { |
||||
nextAnnotation = playerName; |
||||
} |
||||
else { |
||||
nextAnnotation = annotation; |
||||
} |
||||
_.users.SetAnnotationForUserID(groupName, nextID, nextAnnotation); |
||||
DisplayAnnotation(playerName, groupName, nextAnnotation); |
||||
_.memory.Free(nextID); |
||||
nextAnnotation = none; |
||||
} |
||||
_.memory.Free(nextPlayer); |
||||
_.memory.Free(playerName); |
||||
_.memory.Free(nextID); |
||||
nextPlayer = none; |
||||
playerName = none; |
||||
nextID = none; |
||||
} |
||||
} |
||||
|
||||
private function TryRemovingUserID( |
||||
BaseText groupName, |
||||
UserID idToRemove, |
||||
BaseText userSpecifiedName) |
||||
{ |
||||
local Text idAsText; |
||||
|
||||
idAsText = idToRemove.GetUniqueID(); |
||||
if (_.users.RemoveUserIDFromGroup(idToRemove, groupName)) |
||||
{ |
||||
callerConsole |
||||
.Write(F("{$TextNegative Removed} user ")) |
||||
.UseColorOnce(_.color.Gray) |
||||
.Write(userSpecifiedName) |
||||
.Write(P(" (with id ")) |
||||
.UseColorOnce(_.color.Gray) |
||||
.Write(idAsText) |
||||
.Write(P(") from the group ")) |
||||
.UseColorOnce(_.color.TextEmphasis) |
||||
.Write(groupName) |
||||
.WriteLine(P("!")); |
||||
} |
||||
else |
||||
{ |
||||
callerConsole |
||||
.UseColorOnce(_.color.TextFailure) |
||||
.Write(P("Failed (for unknown reason)")) |
||||
.Write(P("to remove user with id ")) |
||||
.UseColorOnce(_.color.Gray) |
||||
.Write(idAsText) |
||||
.Write(P(" from the group ")) |
||||
.UseColorOnce(_.color.TextEmphasis) |
||||
.Write(groupName) |
||||
.WriteLine(P(".")); |
||||
} |
||||
_.memory.Free(idAsText); |
||||
} |
||||
|
||||
private function bool RemoveUsersByAnnotation( |
||||
BaseText groupName, |
||||
BaseText userName) |
||||
{ |
||||
local int i; |
||||
local bool removedUser; |
||||
local array<Users_Feature.AnnotatedUserID> annotatedUsers; |
||||
|
||||
annotatedUsers = _.users.GetAnnotatedGroupMembers(groupName); |
||||
for (i = 0; i < annotatedUsers.length; i += 1) |
||||
{ |
||||
if (userName.Compare(annotatedUsers[i].annotation, SCASE_INSENSITIVE)) |
||||
{ |
||||
TryRemovingUserID(groupName, annotatedUsers[i].id, userName); |
||||
removedUser = true; |
||||
} |
||||
} |
||||
for (i = 0; i < annotatedUsers.length; i += 1) |
||||
{ |
||||
_.memory.Free(annotatedUsers[i].id); |
||||
_.memory.Free(annotatedUsers[i].annotation); |
||||
} |
||||
return removedUser; |
||||
} |
||||
|
||||
private function RemoveUser(BaseText groupName, BaseText userName) |
||||
{ |
||||
local bool matchedUserName; |
||||
local UserID idFromName; |
||||
|
||||
if (groupName == none) return; |
||||
if (userName == none) return; |
||||
if (!ValidateGroupExistence(groupName)) return; |
||||
|
||||
idFromName = UserID(_.memory.Allocate(class'UserID')); |
||||
idFromName.Initialize(userName); |
||||
if ( idFromName.IsInitialized() |
||||
&& _.users.IsUserIDInGroup(idFromName, groupName)) |
||||
{ |
||||
TryRemovingUserID(groupName, idFromName, userName); |
||||
matchedUserName = true; |
||||
} |
||||
else { |
||||
matchedUserName = RemoveUsersByAnnotation(groupName, userName); |
||||
} |
||||
_.memory.Free(idFromName); |
||||
if (!matchedUserName) |
||||
{ |
||||
callerConsole |
||||
.Write(P("User ")) |
||||
.UseColorOnce(_.color.Gray) |
||||
.Write(userName) |
||||
.UseColorOnce(_.color.TextFailure) |
||||
.Write(P(" doesn't belong to the group ")) |
||||
.UseColorOnce(_.color.TextEmphasis) |
||||
.Write(groupName) |
||||
.WriteLine(P("!")); |
||||
} |
||||
} |
||||
|
||||
private function RemovePlayers(BaseText groupName, ArrayList players) |
||||
{ |
||||
local int i; |
||||
local Text playerName; |
||||
local EPlayer nextPlayer; |
||||
local UserID nextID; |
||||
|
||||
if (groupName == none) return; |
||||
if (players == none) return; |
||||
if (!ValidateGroupExistence(groupName)) return; |
||||
|
||||
for (i = 0; i < players.GetLength(); i += 1) |
||||
{ |
||||
nextPlayer = EPlayer(players.GetItem(i)); |
||||
if (nextPlayer == none) { |
||||
continue; |
||||
} |
||||
playerName = nextPlayer.GetName(); |
||||
nextID = nextPlayer.GetUserID(); |
||||
if (!_.users.IsUserIDInGroup(nextID, groupName)) |
||||
{ |
||||
callerConsole |
||||
.Write(P("Player ")) |
||||
.UseColorOnce(_.color.Gray) |
||||
.Write(playerName) |
||||
.Write(F(" {$TextFailure doesn't belong} to the group ")) |
||||
.UseColorOnce(_.color.TextEmphasis) |
||||
.Write(groupName) |
||||
.WriteLine(P("!")); |
||||
} |
||||
else { |
||||
TryRemovingUserID(groupName, nextID, playerName); |
||||
} |
||||
_.memory.Free(nextPlayer); |
||||
_.memory.Free(playerName); |
||||
_.memory.Free(nextID); |
||||
nextPlayer = none; |
||||
playerName = none; |
||||
nextID = none; |
||||
} |
||||
} |
||||
|
||||
private function AddGroup(BaseText groupName) |
||||
{ |
||||
if (_.users.IsGroupExisting(groupName)) |
||||
{ |
||||
callerConsole |
||||
.Write(P("Group ")) |
||||
.UseColorOnce(_.color.TextEmphasis) |
||||
.Write(groupName) |
||||
.UseColorOnce(_.color.TextNegative) |
||||
.Write(P(" already exists")) |
||||
.WriteLine(P("!")); |
||||
return; |
||||
} |
||||
if (_.users.AddGroup(groupName)) |
||||
{ |
||||
callerConsole |
||||
.Write(P("Group ")) |
||||
.UseColorOnce(_.color.TextEmphasis) |
||||
.Write(groupName) |
||||
.UseColorOnce(_.color.TextPositive) |
||||
.Write(P(" was added")) |
||||
.WriteLine(P("!")); |
||||
} |
||||
else |
||||
{ |
||||
callerConsole |
||||
.UseColorOnce(_.color.TextFailure) |
||||
.Write(P("Cannot add")) |
||||
.Write(P(" group with a name ")) |
||||
.UseColorOnce(_.color.TextEmphasis) |
||||
.Write(groupName) |
||||
.WriteLine(P(" for unknown reason.")); |
||||
} |
||||
} |
||||
|
||||
private function RemoveGroup(BaseText groupName) |
||||
{ |
||||
if (!_.users.IsGroupExisting(groupName)) |
||||
{ |
||||
callerConsole |
||||
.Write(P("Group ")) |
||||
.UseColorOnce(_.color.TextEmphasis) |
||||
.Write(groupName) |
||||
.UseColorOnce(_.color.TextNegative) |
||||
.Write(P(" doesn't exists")) |
||||
.WriteLine(P("!")); |
||||
return; |
||||
} |
||||
if (_.users.RemoveGroup(groupName)) |
||||
{ |
||||
callerConsole |
||||
.Write(P("Group ")) |
||||
.UseColorOnce(_.color.TextEmphasis) |
||||
.Write(groupName) |
||||
.UseColorOnce(_.color.TextPositive) |
||||
.Write(P(" was removed")) |
||||
.WriteLine(P("!")); |
||||
} |
||||
else |
||||
{ |
||||
callerConsole |
||||
.UseColorOnce(_.color.TextFailure) |
||||
.Write(P("Cannot remove")) |
||||
.Write(P(" group ")) |
||||
.UseColorOnce(_.color.TextEmphasis) |
||||
.Write(groupName) |
||||
.WriteLine(P(" for unknown reason.")); |
||||
} |
||||
} |
||||
|
||||
private function DisplayUserGroups() |
||||
{ |
||||
local int i; |
||||
local array<Text> availableGroups; |
||||
|
||||
if (!ValidateUsersFeature()) { |
||||
return; |
||||
} |
||||
availableGroups = _.users.GetAvailableGroups(); |
||||
if (availableGroups.length <= 0) |
||||
{ |
||||
callerConsole.WriteLine(F("{$TextNegative No user groups}" |
||||
@ "currently available.")); |
||||
return; |
||||
} |
||||
callerConsole |
||||
.UseColorOnce(_.color.TextEmphasis) |
||||
.Write(P("Available user groups")) |
||||
.Write(P(": ")); |
||||
for (i = 0; i < availableGroups.length; i += 1) |
||||
{ |
||||
if (i > 0) { |
||||
callerConsole.Write(P(", ")); |
||||
} |
||||
callerConsole.Write(availableGroups[i]); |
||||
} |
||||
callerConsole.Flush(); |
||||
_.memory.FreeMany(availableGroups); |
||||
} |
||||
|
||||
private function bool ValidateUsersFeature() |
||||
{ |
||||
if (class'Users_Feature'.static.IsEnabled()) { |
||||
return true; |
||||
} |
||||
callerConsole |
||||
.UseColorOnce(_.color.TextFailure) |
||||
.WriteLine(P("`Users_Feature` is currently disabled.")); |
||||
return false; |
||||
} |
||||
|
||||
private function bool IsGroupSpecified( |
||||
ArrayList specifiedGroups, |
||||
BaseText groupToCheck) |
||||
{ |
||||
local int i; |
||||
local int length; |
||||
local Text nextGroup; |
||||
|
||||
if (groupToCheck == none) return false; |
||||
if (specifiedGroups == none) return true; |
||||
length = groupToCheck.GetLength(); |
||||
if (length <= 0) return true; |
||||
|
||||
for (i = 0; i < length; i += 1) |
||||
{ |
||||
nextGroup = specifiedGroups.GetText(i); |
||||
if (groupToCheck.Compare(nextGroup, SCASE_INSENSITIVE)) |
||||
{ |
||||
nextGroup.FreeSelf(); |
||||
return true; |
||||
} |
||||
_.memory.Free(nextGroup); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
private function DisplayUserGroupsWithUsers(ArrayList specifiedGroups) |
||||
{ |
||||
local int i; |
||||
local bool displayedGroup; |
||||
local array<Text> availableGroups; |
||||
|
||||
if (!ValidateUsersFeature()) { |
||||
return; |
||||
} |
||||
availableGroups = _.users.GetAvailableGroups(); |
||||
if (availableGroups.length <= 0) |
||||
{ |
||||
callerConsole.WriteLine(F("{$TextNegative No user groups}" |
||||
@ "currently available.")); |
||||
return; |
||||
} |
||||
for (i = 0; i < availableGroups.length; i += 1) |
||||
{ |
||||
if (IsGroupSpecified(specifiedGroups, availableGroups[i])) |
||||
{ |
||||
displayedGroup = true; |
||||
callerConsole |
||||
.Write(P("User group ")) |
||||
.UseColorOnce(_.color.TextEmphasis) |
||||
.Write(availableGroups[i]) |
||||
.WriteLine(P(":")); |
||||
DisplayUsersFor(availableGroups[i]); |
||||
} |
||||
} |
||||
callerConsole.Flush(); |
||||
_.memory.FreeMany(availableGroups); |
||||
if (!displayedGroup && specifiedGroups != none) { |
||||
callerConsole.WriteLine(F("{$TextFailure No valid groups} specified!")); |
||||
} |
||||
} |
||||
|
||||
private function DisplayUsersFor(Text groupName) |
||||
{ |
||||
local int i; |
||||
local Text nextID; |
||||
local array<Users_Feature.AnnotatedUserID> annotatedUsers; |
||||
|
||||
annotatedUsers = _.users.GetAnnotatedGroupMembers(groupName); |
||||
if (annotatedUsers.length <= 0) |
||||
{ |
||||
callerConsole.WriteBlock(P("No users")); |
||||
return; |
||||
} |
||||
for (i = 0; i < annotatedUsers.length; i += 1) |
||||
{ |
||||
if (annotatedUsers[i].id == none) { |
||||
continue; |
||||
} |
||||
nextID = annotatedUsers[i].id.GetUniqueID(); |
||||
if (annotatedUsers[i].annotation != none) |
||||
{ |
||||
callerConsole |
||||
.Write(nextID) |
||||
.UseColorOnce(_.color.TextNeutral) |
||||
.Write(P(" aka ")) |
||||
.WriteBlock(annotatedUsers[i].annotation); |
||||
} |
||||
else { |
||||
callerConsole.WriteBlock(nextID); |
||||
} |
||||
_.memory.Free(nextID); |
||||
} |
||||
for (i = 0; i < annotatedUsers.length; i += 1) |
||||
{ |
||||
_.memory.Free(annotatedUsers[i].id); |
||||
_.memory.Free(annotatedUsers[i].annotation); |
||||
} |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
} |
@ -0,0 +1,40 @@
|
||||
/** |
||||
* Signal class for `PersistentDataManager`'s `OnPersistentDataReady()` signal. |
||||
* Copyright 2023 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 <https://www.gnu.org/licenses/>. |
||||
*/ |
||||
class PersistentDataManager_OnPersistentDataReady_Signal extends Signal |
||||
dependson(DBConnection); |
||||
|
||||
public final function Emit(UserID id, bool online) |
||||
{ |
||||
local Slot nextSlot; |
||||
StartIterating(); |
||||
nextSlot = GetNextSlot(); |
||||
while (nextSlot != none) |
||||
{ |
||||
PersistentDataManager_OnPersistentDataReady_Slot(nextSlot) |
||||
.connect(id, online); |
||||
nextSlot = GetNextSlot(); |
||||
} |
||||
CleanEmptySlots(); |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
relatedSlotClass = class'PersistentDataManager_OnPersistentDataReady_Slot' |
||||
} |
@ -0,0 +1,41 @@
|
||||
/** |
||||
* Slot class for `PersistentDataManager`'s `OnPersistentDataReady()` signal. |
||||
* Copyright 2023 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 <https://www.gnu.org/licenses/>. |
||||
*/ |
||||
class PersistentDataManager_OnPersistentDataReady_Slot extends Slot |
||||
dependson(DBConnection); |
||||
|
||||
delegate connect(UserID id, bool online) |
||||
{ |
||||
DummyCall(); |
||||
} |
||||
|
||||
protected function Constructor() |
||||
{ |
||||
connect = none; |
||||
} |
||||
|
||||
protected function Finalizer() |
||||
{ |
||||
super.Finalizer(); |
||||
connect = none; |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
} |
@ -0,0 +1,407 @@
|
||||
/** |
||||
* This tool is for simplifying writing and reading persistent user data. |
||||
* All it requires is a setup of database + json pointer to data and it will |
||||
* take care of data caching and database connection. |
||||
* Copyright 2023 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 <https://www.gnu.org/licenses/>. |
||||
*/ |
||||
class PersistentDataManager extends AcediaObject; |
||||
|
||||
/** |
||||
* # `PersistentDataManager` |
||||
* |
||||
* This tool is for simplifying writing and reading persistent user data. |
||||
* All it requires is a setup of database + json pointer to data and it will |
||||
* take care of data caching and database connection. |
||||
* |
||||
* ## Usage |
||||
* |
||||
* Create an instance and use `Setup()` to connect to the database with |
||||
* persistent data. You can use `Setup()` again on the same object to setup |
||||
* a different database as a source. All data will be automatically reloaded. |
||||
* After that you can use `GetPersistentData()`/`SetPersistentData()` to |
||||
* read/write persistent data for the particular user. |
||||
* Since loading data from the database takes time, you don't have an |
||||
* immediate access to it. |
||||
* But you can use `_.users.OnPersistentDataAvailable()` signal to track |
||||
* whenever new user data from database becomes available. However, you can |
||||
* start writing persistent data (and reading what you've wrote) at any time it |
||||
* - these changes will be reapplied whenever data is actually loaded from |
||||
* database. |
||||
* |
||||
* ## Implementation |
||||
* |
||||
* Implementation consists of simply creating `DBConnection` for every user |
||||
* and storing them in the `HashTable` that maps user IDs into those |
||||
* `DBConnection`s. |
||||
* We also maintain a reverse map to figure out what `DBConnection` belongs |
||||
* to what user when connection signals an update. We borrow the signal that |
||||
* `UsersAPI` provides to inform everyone interested about which users |
||||
* have updated. |
||||
*/ |
||||
|
||||
var private bool initialized; |
||||
var private Database database; |
||||
var private JSONPointer rootPointer; |
||||
var private HashTable userToConnection, connectionToUser; |
||||
|
||||
var private PersistentDataManager_OnPersistentDataReady_Signal onPersistentDataReadySignal; |
||||
|
||||
protected function Constructor() |
||||
{ |
||||
_.players.OnNewPlayer(self).connect = ConnectPersistentData; |
||||
onPersistentDataReadySignal = _.users._getOnReadySignal(); |
||||
} |
||||
|
||||
protected function Finalizer() |
||||
{ |
||||
Reset(); |
||||
_.players.OnNewPlayer(self).Disconnect(); |
||||
} |
||||
|
||||
private final function Reset() |
||||
{ |
||||
_.memory.Free(database); |
||||
_.memory.Free(rootPointer); |
||||
_.memory.Free(userToConnection); |
||||
_.memory.Free(connectionToUser); |
||||
_.memory.Free(onPersistentDataReadySignal); |
||||
database = none; |
||||
rootPointer = none; |
||||
userToConnection = none; |
||||
connectionToUser = none; |
||||
onPersistentDataReadySignal = none; |
||||
initialized = false; |
||||
} |
||||
|
||||
/** |
||||
* Sets up database and location inside it as a source of users' persistent |
||||
* data. |
||||
* |
||||
* Must be successfully called at least once for the caller |
||||
* `PersistentDataManager` to be usable. |
||||
* |
||||
* @param db Database inside which persistent data is stored. |
||||
* @param location Location inside specified database to the root of |
||||
* persistent data. |
||||
* @return `true` if setup was successful (requires both arguments to be not |
||||
* `none`) and `false` otherwise. |
||||
*/ |
||||
public final function bool Setup(Database db, JSONPointer location) |
||||
{ |
||||
if (db == none) return false; |
||||
if (location == none) return false; |
||||
|
||||
Reset(); |
||||
database = db; |
||||
database.NewRef(); |
||||
rootPointer = location.Copy(); |
||||
userToConnection = _.collections.EmptyHashTable(); |
||||
connectionToUser = _.collections.EmptyHashTable(); |
||||
// Using `userToConnection` as an empty hash table, not related to its |
||||
// actual meaning |
||||
database.IncrementData(location, userToConnection); |
||||
initialized = true; |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Reads specified named persistent data for the specified group. |
||||
* |
||||
* @param id ID of the user to read persistent data from. |
||||
* @param groupName Group to which this persistent data belongs to. |
||||
* Groups are used as namespaces to avoid duplicate persistent variables |
||||
* between mods. If your mod needs several subgroups, its recommended to |
||||
* use the same prefix for them, e.g. "MyAwesomeMod.economy" and |
||||
* "MyAwesomeMod.enemies". |
||||
* @param dataName Name of persistent data variable to read inside |
||||
* `groupName` persistent data group. Not `none` value must be provided. |
||||
* @param data Data to set as persistent value. Must be |
||||
* JSON-compatible. If `none` is passed, returns the all data for |
||||
* the given group. |
||||
* @return Data read from the persistent variable. `none` in case of any kind |
||||
* of failure. |
||||
*/ |
||||
public final function AcediaObject GetPersistentData( |
||||
UserID id, |
||||
BaseText groupName, |
||||
optional BaseText dataName) |
||||
{ |
||||
local AcediaObject result; |
||||
local Text textID; |
||||
local JSONPointer location; |
||||
local DBConnection relevantConnection; |
||||
|
||||
if (!initialized) return none; |
||||
if (id == none) return none; |
||||
if (groupName == none) return none; |
||||
|
||||
textID = id.GetUniqueID(); |
||||
relevantConnection = DBConnection(userToConnection.GetItem(textID)); |
||||
textID.FreeSelf(); |
||||
if (relevantConnection != none) |
||||
{ |
||||
location = _.json.Pointer(); |
||||
location.Push(groupName); |
||||
if (dataName != none) { |
||||
location.Push(dataName); |
||||
} |
||||
result = relevantConnection.ReadDataByJSON(location); |
||||
relevantConnection.FreeSelf(); |
||||
location.FreeSelf(); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Writes specified named persistent data for the specified group. |
||||
* |
||||
* @param id ID of the user to change persistent data of. |
||||
* @param groupName Group to which this persistent data belongs to. |
||||
* Groups are used as namespaces to avoid duplicate persistent variables |
||||
* between mods. If your mod needs several subgroups, its recommended to |
||||
* use the same prefix for them, e.g. "MyAwesomeMod.economy" and |
||||
* "MyAwesomeMod.enemies". |
||||
* @param dataName Name of persistent data variable to change inside |
||||
* `groupName` persistent data group. |
||||
* @param data Data to set as persistent value. Must be |
||||
* JSON-compatible. |
||||
* @return `true` if change succeeded in local cached version of database with |
||||
* persistent values and `false` otherwise. Such local changes can |
||||
* potentially be not applied to the actual database. But successful local |
||||
* changes should persist for the game session. |
||||
*/ |
||||
public final function bool WritePersistentData( |
||||
UserID id, |
||||
BaseText groupName, |
||||
BaseText dataName, |
||||
AcediaObject data) |
||||
{ |
||||
local bool result; |
||||
local Text textID; |
||||
local JSONPointer location; |
||||
local DBConnection relevantConnection; |
||||
local HashTable emptyObject; |
||||
|
||||
if (!initialized) return false; |
||||
if (id == none) return false; |
||||
if (groupName == none) return false; |
||||
if (dataName == none) return false; |
||||
|
||||
textID = id.GetUniqueID(); |
||||
relevantConnection = DBConnection(userToConnection.GetItem(textID)); |
||||
textID.FreeSelf(); |
||||
if (relevantConnection != none) |
||||
{ |
||||
emptyObject = _.collections.EmptyHashTable(); |
||||
location = _.json.Pointer(); |
||||
location.Push(groupName); |
||||
relevantConnection.IncrementDataByJSON(location, emptyObject); |
||||
location.Push(dataName); |
||||
result = relevantConnection.WriteDataByJSON(location, data); |
||||
relevantConnection.FreeSelf(); |
||||
location.FreeSelf(); |
||||
emptyObject.FreeSelf(); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Increments specified named persistent data for the specified group. |
||||
* |
||||
* @param id ID of the user to change persistent data of. |
||||
* @param groupName Group to which this persistent data belongs to. |
||||
* Groups are used as namespaces to avoid duplicate persistent variables |
||||
* between mods. If your mod needs several subgroups, its recommended to |
||||
* use the same prefix for them, e.g. "MyAwesomeMod.economy" and |
||||
* "MyAwesomeMod.enemies". |
||||
* @param dataName Name of persistent data variable to change inside |
||||
* `groupName` persistent data group. |
||||
* @param data Data by which to increment existing persistent value. |
||||
* Must be JSON-compatible. |
||||
* @return `true` if change succeeded in local cached version of database with |
||||
* persistent values and `false` otherwise. Such local changes can |
||||
* potentially be not applied to the actual database. But successful local |
||||
* changes should persist for the game session. |
||||
*/ |
||||
public final function bool IncrementPersistentData( |
||||
UserID id, |
||||
BaseText groupName, |
||||
BaseText dataName, |
||||
AcediaObject data) |
||||
{ |
||||
local bool result; |
||||
local Text textID; |
||||
local JSONPointer location; |
||||
local DBConnection relevantConnection; |
||||
|
||||
if (!initialized) return false; |
||||
if (id == none) return false; |
||||
if (groupName == none) return false; |
||||
if (dataName == none) return false; |
||||
|
||||
textID = id.GetUniqueID(); |
||||
relevantConnection = DBConnection(userToConnection.GetItem(textID)); |
||||
textID.FreeSelf(); |
||||
if (relevantConnection != none) |
||||
{ |
||||
location = _.json.Pointer(); |
||||
location.Push(groupName).Push(dataName); |
||||
result = relevantConnection.IncrementDataByJSON(location, data); |
||||
relevantConnection.FreeSelf(); |
||||
location.FreeSelf(); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Removes specified named persistent data for the specified group. |
||||
* |
||||
* @param id ID of the user to remove persistent data of. |
||||
* @param groupName Group to which this persistent data belongs to. |
||||
* Groups are used as namespaces to avoid duplicate persistent variables |
||||
* between mods. If your mod needs several subgroups, its recommended to |
||||
* use the same prefix for them, e.g. "MyAwesomeMod.economy" and |
||||
* "MyAwesomeMod.enemies". |
||||
* @param dataName Name of persistent data variable to remove inside |
||||
* `groupName` persistent data group. |
||||
* @return `true` if removal succeeded in local cached version of database with |
||||
* persistent values and `false` otherwise. Such local changes can |
||||
* potentially be not applied to the actual database. But successful local |
||||
* changes should persist for the game session. |
||||
*/ |
||||
public final function bool RemovePersistentData( |
||||
UserID id, |
||||
BaseText groupName, |
||||
BaseText dataName) |
||||
{ |
||||
local bool result; |
||||
local Text textID; |
||||
local JSONPointer location; |
||||
local DBConnection relevantConnection; |
||||
|
||||
if (!initialized) return false; |
||||
if (id == none) return false; |
||||
if (groupName == none) return false; |
||||
if (dataName == none) return false; |
||||
|
||||
textID = id.GetUniqueID(); |
||||
relevantConnection = DBConnection(userToConnection.GetItem(textID)); |
||||
textID.FreeSelf(); |
||||
if (relevantConnection != none) |
||||
{ |
||||
location = _.json.Pointer(); |
||||
location.Push(groupName).Push(dataName); |
||||
result = relevantConnection.RemoveDataByJSON(location); |
||||
relevantConnection.FreeSelf(); |
||||
location.FreeSelf(); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Connects and starts synchronizing persistent data for the given player. |
||||
* |
||||
* @param player Player to synchronize persistent data for. |
||||
*/ |
||||
public final function ConnectPersistentData(EPlayer player) |
||||
{ |
||||
local UserID playerID; |
||||
|
||||
if (initialized && player != none) |
||||
{ |
||||
playerID = player.GetUserID(); |
||||
ConnectPersistentDataByID(playerID); |
||||
_.memory.Free(playerID); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Connects and starts synchronizing persistent data for the player given by |
||||
* their ID. |
||||
* |
||||
* @param id User ID for which to synchronize persistent data from |
||||
* the database. |
||||
*/ |
||||
public final function ConnectPersistentDataByID(UserID id) |
||||
{ |
||||
local Text textID; |
||||
local DBConnection newConnection; |
||||
|
||||
if (!initialized) return; |
||||
if (id == none) return; |
||||
|
||||
textID = id.GetUniqueID(); |
||||
if (userToConnection.HasKey(textID)) |
||||
{ |
||||
_.memory.Free(textID); |
||||
return; |
||||
} |
||||
rootPointer.Push(textID); |
||||
newConnection = DBConnection(_.memory.Allocate(class'DBConnection')); |
||||
newConnection.Initialize(database, rootPointer); |
||||
_.memory.Free(rootPointer.Pop()); |
||||
newConnection.Connect(); |
||||
userToConnection.SetItem(textID, newConnection); |
||||
connectionToUser.SetItem(newConnection, textID); |
||||
newConnection.OnStateChanged(self).connect = UserUpdated; |
||||
textID.FreeSelf(); |
||||
newConnection.FreeSelf(); |
||||
} |
||||
|
||||
private final function UserUpdated( |
||||
DBConnection instance, |
||||
DBConnection.DBConnectionState oldState, |
||||
DBConnection.DBConnectionState newState) |
||||
{ |
||||
local UserID id; |
||||
|
||||
if (!initialized) return; |
||||
if (newState == DBCS_Connecting) return; |
||||
if (onPersistentDataReadySignal == none) return; |
||||
if (!onPersistentDataReadySignal.IsAllocated()) return; |
||||
|
||||
id = UserID(connectionToUser.GetItem(instance)); |
||||
if (id != none) |
||||
{ |
||||
onPersistentDataReadySignal.Emit(id, newState == DBCS_Connected); |
||||
id.FreeSelf(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Attempts to start persistent data synchronization for all players currently |
||||
* on the server. |
||||
*/ |
||||
public final function LoadCurrentPlayers() |
||||
{ |
||||
local int i; |
||||
local array<EPlayer> currentPlayers; |
||||
|
||||
if (initialized) |
||||
{ |
||||
currentPlayers = _.players.GetAll(); |
||||
for (i = 0; i < currentPlayers.length; i += 1) { |
||||
ConnectPersistentData(currentPlayers[i]); |
||||
} |
||||
_.memory.FreeMany(currentPlayers); |
||||
} |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
} |
@ -0,0 +1,70 @@
|
||||
/** |
||||
* Acedia's class for defining user group in config files. |
||||
* 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 <https://www.gnu.org/licenses/>. |
||||
*/ |
||||
class UserGroup extends AcediaConfig |
||||
perobjectconfig |
||||
config(AcediaUsers); |
||||
|
||||
var public config array<string> user; |
||||
|
||||
protected function HashTable ToData() |
||||
{ |
||||
local int i; |
||||
local HashTable data; |
||||
local ArrayList wrappedUserArray; |
||||
|
||||
data = __().collections.EmptyHashTable(); |
||||
wrappedUserArray = __().collections.EmptyArrayList(); |
||||
for (i = 0; i < user.length; i += 1) { |
||||
wrappedUserArray.AddString(user[i]); |
||||
} |
||||
data.SetItem(P("user"), wrappedUserArray); |
||||
wrappedUserArray.FreeSelf(); |
||||
return data; |
||||
} |
||||
|
||||
protected function FromData(HashTable source) |
||||
{ |
||||
local int i; |
||||
local ArrayList wrappedUserArray; |
||||
|
||||
DefaultIt(); |
||||
if (source == none) { |
||||
return; |
||||
} |
||||
wrappedUserArray = source.GetArrayList(P("user")); |
||||
if (wrappedUserArray == none) { |
||||
return; |
||||
} |
||||
for (i = 0; i < wrappedUserArray.GetLength(); i += 1) { |
||||
user[user.length] = wrappedUserArray.GetString(i); |
||||
} |
||||
wrappedUserArray.FreeSelf(); |
||||
} |
||||
|
||||
protected function DefaultIt() |
||||
{ |
||||
user.length = 0; |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
configName = "AcediaUsers" |
||||
supportsDataConversion = true |
||||
} |
@ -0,0 +1,99 @@
|
||||
/** |
||||
* Config object for `Users_Feature`. |
||||
* 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 <https://www.gnu.org/licenses/>. |
||||
*/ |
||||
class Users extends FeatureConfig |
||||
perobjectconfig |
||||
config(AcediaUsers); |
||||
|
||||
var public config bool usePersistentData; |
||||
var public config string persistentDataDatabaseLink; |
||||
var public config bool useDatabaseForGroupsData; |
||||
var public config string groupsDatabaseLink; |
||||
var public config array<string> localUserGroup; |
||||
|
||||
protected function HashTable ToData() |
||||
{ |
||||
local int i; |
||||
local HashTable data; |
||||
local ArrayList userGroupList; |
||||
|
||||
data = __().collections.EmptyHashTable(); |
||||
data.SetBool(P("usePersistentData"), usePersistentData); |
||||
data.SetString(P("persistentDataDatabaseLink"), persistentDataDatabaseLink); |
||||
data.SetBool(P("useDatabaseForGroupsData"), useDatabaseForGroupsData); |
||||
data.SetString(P("groupsDatabaseLink"), groupsDatabaseLink); |
||||
userGroupList = _.collections.EmptyArrayList(); |
||||
for (i = 0; i < localUserGroup.length; i += 1) { |
||||
userGroupList.AddString(localUserGroup[i]); |
||||
} |
||||
data.SetItem(P("userGroups"), userGroupList); |
||||
userGroupList.FreeSelf(); |
||||
return data; |
||||
} |
||||
|
||||
protected function FromData(HashTable source) |
||||
{ |
||||
local int i; |
||||
local ArrayList userGroupList; |
||||
|
||||
if (source == none) { |
||||
return; |
||||
} |
||||
usePersistentData = source.GetBool(P("usePersistentData")); |
||||
persistentDataDatabaseLink = source.GetString( |
||||
P("persistentDataDatabaseLink"), |
||||
"[local]database:/persistent_data"); |
||||
useDatabaseForGroupsData = source.GetBool(P("useDatabaseForGroupsData")); |
||||
groupsDatabaseLink = source.GetString( |
||||
P("groupsDatabaseLink"), |
||||
"[local]database:/groups_data"); |
||||
userGroupList = source.GetArrayList(P("userGroups")); |
||||
localUserGroup.length = 0; |
||||
if (userGroupList == none) { |
||||
return; |
||||
} |
||||
for (i = 0; i < userGroupList.GetLength(); i += 1) { |
||||
localUserGroup[localUserGroup.length] = userGroupList.GetString(i); |
||||
} |
||||
userGroupList.FreeSelf(); |
||||
} |
||||
|
||||
protected function DefaultIt() |
||||
{ |
||||
usePersistentData = false; |
||||
persistentDataDatabaseLink = "[local]database:/persistent_data"; |
||||
useDatabaseForGroupsData = false; |
||||
groupsDatabaseLink = "[local]database:/groups_data"; |
||||
localUserGroup.length = 0; |
||||
localUserGroup[0] = "admin"; |
||||
localUserGroup[1] = "moderator"; |
||||
localUserGroup[2] = "trusted"; |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
configName = "AcediaUsers" |
||||
usePersistentData = false |
||||
persistentDataDatabaseLink = "[local]database:/persistent_data" |
||||
useDatabaseForGroupsData = false |
||||
groupsDatabaseLink = "[local]database:/groups_data" |
||||
localUserGroup(0) = "admin" |
||||
localUserGroup(1) = "moderator" |
||||
localUserGroup(2) = "trusted" |
||||
} |
Loading…
Reference in new issue