Compare commits

..

No commits in common. 'ff31ef24728557cbfecc4f3d109f5e317af80e1d' and '80cecd1d20cd152dee6ea0069fa6284d8afbe897' have entirely different histories.

  1. 4
      config/AcediaAliases_Commands.ini
  2. 117
      config/AcediaCommands.ini
  3. 563
      sources/BaseAPI/API/Commands/Voting/TEST_Voting.uc
  4. 35
      sources/InfoQueryHandler/InfoQueryHandler.uc
  5. 7
      sources/Users/ACommandUserGroups.uc
  6. 55
      sources/Users/Users_Feature.uc

4
config/AcediaAliases_Commands.ini

@ -1,8 +1,8 @@
; This config file allows you to configure command aliases.
; Remember that aliases are case-insensitive.
[AcediaCore.CommandAliasSource]
record=(alias="yes",value="vote.yes")
record=(alias="no",value="vote.no")
record=(alias="yes",value="vote yes")
record=(alias="no",value="vote no")
[help CommandAliases]
Alias="hlp"

117
config/AcediaCommands.ini

@ -1,117 +0,0 @@
[default Commands]
autoEnable=true
;= Setting this to `true` enables players to input commands with "mutate"
;= console command.
;= Default is `true`.
useMutateInput=true
;= Setting this to `true` enables players to input commands right in the chat
;= by prepending them with [`chatCommandPrefix`].
;= Default is `true`.
useChatInput=true
;= Chat messages, prepended by this prefix will be treated as commands.
;= Default is "!". Empty values are also treated as "!".
chatCommandPrefix=!
;= Allows to specify which user groups are used in determining command/votings
;= permission.
;= They must be specified in the order of importance: from the group with
;= highest level of permissions to the lowest. When determining player's
;= permission to use a certain command/voting, his group with the highest
;= available permissions will be used.
commandGroup=admin
commandGroup=moderator
commandGroup=trusted
commandGroup=all
;= Add a specified `CommandList` to the specified user group
addCommandList=(name="default",for="all")
addCommandList=(name="moderator",for="moderator")
addCommandList=(name="admin",for="admin")
addCommandList=(name="debug",for="admin")
;= Allows to specify a name for a certain command class
;=
;= NOTE:By default command choses that name by itself and its not recommended
;= to override it. You should only use this setting in case there is naming
;= conflict between commands from different packages.
;=renamingRule=(rename=class'ACommandHelp',to="lol")
;= Allows to specify a name for a certain voting class
;=
;= NOTE:By default voting choses that name by itself and its not recommended
;= to override it. You should only use this setting in case there is naming
;= conflict between votings from different packages.
;=votingRenamingRule=(rename=class'Voting',to="lol")
;= `CommandList` describes a set of commands and votings that can be made
;= available to users inside Commands feature
;=
;= Optionally, permission configs can be specified for commands and votings,
;= allowing server admins to create command lists for different groups player
;= with the same commands, but different permissions.
[default CommandList]
;= Allows to specify if this list should only be added when server is running
;= in debug mode.
;= `true` means yes, `false` means that list will always be available.
debugOnly=false
;= Adds a command of specified class with a "default" permissions config
command=class'ACommandHelp'
command=class'ACommandVote'
;= Adds a voting of specified class with a "default" permissions config
voting=class'Voting'
;= Adds a command of specified class with specified permissions config
;=commandWith=(cmd=,config="")
;= Adds a voting of specified class with specified permissions config
;=commandWith=(vtn=,config="")
[debug CommandList]
debugOnly=true
command=class'ACommandFakers'
[moderator CommandList]
command=class'ACommandNotify'
[admin CommandList]
command=class'ACommandSideEffects'
;= `VotingPermissions` describe use permission settings for a voting
[default VotingPermissions]
;= Determines the duration of the voting period, specified in seconds.
;= Zero or negative values mean unlimited voting period.
votingTime=30
;= Determines how draw will be interpreted.
;= `true` means draw counts as a vote's success, `false` means draw counts as a vote's failure.
drawEqualsSuccess=false
;= Determines whether spectators are allowed to vote.
allowSpectatorVoting=false
;= Specifies which group(s) of players are allowed to see who makes what vote.
allowedToVoteGroup=all
;= Specifies which group(s) of players are allowed to see who makes what vote.
allowedToSeeVotesGroup=all
;= Specifies which group(s) of players are allowed to forcibly end voting.
allowedToForceGroup=admin
allowedToForceGroup=moderator
[anonymous VotingPermissions]
votingTime=30
drawEqualsSuccess=false
allowSpectatorVoting=false
allowedToVoteGroup=all
allowedToSeeVotesGroup=admin
allowedToSeeVotesGroup=moderator
allowedToForceGroup=admin
allowedToForceGroup=moderator
[moderator VotingPermissions]
votingTime=60
drawEqualsSuccess=false
allowSpectatorVoting=false
allowedToVoteGroup=admin
allowedToVoteGroup=moderator
allowedToSeeVotesGroup=admin
allowedToForceGroup=admin
[admin VotingPermissions]
votingTime=60
drawEqualsSuccess=false
allowSpectatorVoting=true
allowedToVoteGroup=admin
allowedToSeeVotesGroup=admin
allowedToForceGroup=admin

563
sources/BaseAPI/API/Commands/Voting/TEST_Voting.uc

@ -0,0 +1,563 @@
/**
* Author: dkanus
* Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore
* License: GPL
* 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 TEST_Voting extends TestCase
abstract
dependsOn(VotingModel);
enum ExpectedOutcome {
TEST_EO_Continue,
TEST_EO_End,
TEST_EO_EndDraw,
};
protected static function VotingModel MakeVotingModel(VotingModel.VotingPolicies policies) {
local VotingModel model;
model = VotingModel(__().memory.Allocate(class'VotingModel'));
model.Initialize(policies);
return model;
}
protected static function SetVoters(
VotingModel model,
optional string voterID0,
optional string voterID1,
optional string voterID2,
optional string voterID3,
optional string voterID4,
optional string voterID5,
optional string voterID6,
optional string voterID7,
optional string voterID8,
optional string voterID9
) {
local UserID nextID;
local array<UserID> voterIDs;
if (voterID0 != "") {
nextID = UserID(__().memory.Allocate(class'UserID'));
nextID.Initialize(__().text.FromString(voterID0));
voterIDs[voterIDs.length] = nextID;
}
if (voterID1 != "") {
nextID = UserID(__().memory.Allocate(class'UserID'));
nextID.Initialize(__().text.FromString(voterID1));
voterIDs[voterIDs.length] = nextID;
}
if (voterID2 != "") {
nextID = UserID(__().memory.Allocate(class'UserID'));
nextID.Initialize(__().text.FromString(voterID2));
voterIDs[voterIDs.length] = nextID;
}
if (voterID3 != "") {
nextID = UserID(__().memory.Allocate(class'UserID'));
nextID.Initialize(__().text.FromString(voterID3));
voterIDs[voterIDs.length] = nextID;
}
if (voterID4 != "") {
nextID = UserID(__().memory.Allocate(class'UserID'));
nextID.Initialize(__().text.FromString(voterID4));
voterIDs[voterIDs.length] = nextID;
}
if (voterID5 != "") {
nextID = UserID(__().memory.Allocate(class'UserID'));
nextID.Initialize(__().text.FromString(voterID5));
voterIDs[voterIDs.length] = nextID;
}
if (voterID6 != "") {
nextID = UserID(__().memory.Allocate(class'UserID'));
nextID.Initialize(__().text.FromString(voterID6));
voterIDs[voterIDs.length] = nextID;
}
if (voterID7 != "") {
nextID = UserID(__().memory.Allocate(class'UserID'));
nextID.Initialize(__().text.FromString(voterID7));
voterIDs[voterIDs.length] = nextID;
}
if (voterID8 != "") {
nextID = UserID(__().memory.Allocate(class'UserID'));
nextID.Initialize(__().text.FromString(voterID8));
voterIDs[voterIDs.length] = nextID;
}
if (voterID9 != "") {
nextID = UserID(__().memory.Allocate(class'UserID'));
nextID.Initialize(__().text.FromString(voterID9));
voterIDs[voterIDs.length] = nextID;
}
model.UpdatePotentialVoters(voterIDs);
}
protected static function MakeFaultyYesVote(
VotingModel model,
string voterID,
VotingModel.VotingResult expected) {
local UserID id;
id = UserID(__().memory.Allocate(class'UserID'));
id.Initialize(__().text.FromString(voterID));
Issue("Illegal vote had unexpected result.");
TEST_ExpectTrue(model.CastVote(id, true) == expected);
}
protected static function MakeFaultyNoVote(
VotingModel model,
string voterID,
VotingModel.VotingResult expected) {
local UserID id;
id = UserID(__().memory.Allocate(class'UserID'));
id.Initialize(__().text.FromString(voterID));
Issue("Illegal vote had unexpected result.");
TEST_ExpectTrue(model.CastVote(id, false) == expected);
}
protected static function VoteYes(VotingModel model, string voterID, ExpectedOutcome expected) {
local UserID id;
id = UserID(__().memory.Allocate(class'UserID'));
id.Initialize(__().text.FromString(voterID));
Issue("Failed to add legitimate vote.");
TEST_ExpectTrue(model.CastVote(id, true) == VFR_Success);
if (expected == TEST_EO_Continue) {
Issue("Vote, that shouldn't have ended voting, ended it.");
TEST_ExpectTrue(model.GetStatus() == VPM_InProgress);
} else if (expected == TEST_EO_End) {
Issue("Vote, that should've ended voting with one side's victory, didn't do it.");
TEST_ExpectTrue(model.GetStatus() == VPM_Success);
} else if (expected == TEST_EO_EndDraw) {
Issue("Vote, that should've ended voting with a draw, didn't do it.");
TEST_ExpectTrue(model.GetStatus() == VPM_Draw);
}
}
protected static function VoteNo(VotingModel model, string voterID, ExpectedOutcome expected) {
local UserID id;
id = UserID(__().memory.Allocate(class'UserID'));
id.Initialize(__().text.FromString(voterID));
Issue("Failed to add legitimate vote.");
TEST_ExpectTrue(model.CastVote(id, false) == VFR_Success);
if (expected == TEST_EO_Continue) {
Issue("Vote, that shouldn't have ended voting, ended it.");
TEST_ExpectTrue(model.GetStatus() == VPM_InProgress);
} else if (expected == TEST_EO_End) {
Issue("Vote, that should've ended voting with one side's victory, didn't do it.");
TEST_ExpectTrue(model.GetStatus() == VPM_Failure);
} else if (expected == TEST_EO_EndDraw) {
Issue("Vote, that should've ended voting with a draw, didn't do it.");
TEST_ExpectTrue(model.GetStatus() == VPM_Draw);
}
}
protected static function TESTS() {
Test_RestrictiveVoting();
Test_CanLeaveVoting();
Test_CanChangeVoting();
Test_All();
}
protected static function Test_RestrictiveVoting() {
SubTest_RestrictiveYesVoting();
SubTest_RestrictiveNoVoting();
SubTest_RestrictiveDrawVoting();
SubTest_RestrictiveFaultyVoting();
SubTest_RestrictiveDisconnectVoting();
SubTest_RestrictiveReconnectVoting();
}
protected static function SubTest_RestrictiveYesVoting() {
local VotingModel model;
Context("Testing restrictive \"yes\" voting.");
model = MakeVotingModel(VP_Restrictive);
SetVoters(model, "1", "2", "3", "4", "5", "6");
VoteYes(model, "2", TEST_EO_Continue);
VoteYes(model, "4", TEST_EO_Continue);
VoteYes(model, "3", TEST_EO_Continue);
VoteYes(model, "1", TEST_EO_End);
}
protected static function SubTest_RestrictiveNoVoting() {
local VotingModel model;
Context("Testing restrictive \"no\" voting.");
model = MakeVotingModel(VP_Restrictive);
SetVoters(model, "1", "2", "3", "4", "5", "6");
VoteNo(model, "1", TEST_EO_Continue);
VoteNo(model, "2", TEST_EO_Continue);
VoteNo(model, "6", TEST_EO_Continue);
VoteNo(model, "4", TEST_EO_End);
}
protected static function SubTest_RestrictiveDrawVoting() {
local VotingModel model;
Context("Testing restrictive \"draw\" voting.");
model = MakeVotingModel(VP_Restrictive);
SetVoters(model, "1", "2", "3", "4", "5", "6");
VoteYes(model, "3", TEST_EO_Continue);
VoteYes(model, "2", TEST_EO_Continue);
VoteNo(model, "5", TEST_EO_Continue);
VoteYes(model, "1", TEST_EO_Continue);
VoteNo(model, "6", TEST_EO_Continue);
VoteNo(model, "4", TEST_EO_EndDraw);
}
protected static function SubTest_RestrictiveFaultyVoting() {
local VotingModel model;
Context("Testing restrictive \"faulty\" voting.");
model = MakeVotingModel(VP_Restrictive);
SetVoters(model, "1", "2", "3", "4", "5", "6");
VoteYes(model, "3", TEST_EO_Continue);
MakeFaultyYesVote(model, "3", VFR_AlreadyVoted);
VoteYes(model, "2", TEST_EO_Continue);
MakeFaultyNoVote(model, "7", VFR_NotAllowed);
VoteNo(model, "5", TEST_EO_Continue);
MakeFaultyNoVote(model, "7", VFR_NotAllowed);
MakeFaultyNoVote(model, "5", VFR_AlreadyVoted);
VoteYes(model, "1", TEST_EO_Continue);
MakeFaultyNoVote(model, "3", VFR_CannotChangeVote);
VoteYes(model, "6", TEST_EO_End);
MakeFaultyYesVote(model, "4", VFR_VotingEnded);
}
protected static function SubTest_RestrictiveDisconnectVoting() {
local VotingModel model;
Context("Testing restrictive \"disconnect\" voting.");
model = MakeVotingModel(VP_Restrictive);
SetVoters(model, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
VoteYes(model, "1", TEST_EO_Continue);
VoteYes(model, "2", TEST_EO_Continue);
VoteYes(model, "3", TEST_EO_Continue);
VoteYes(model, "4", TEST_EO_Continue);
VoteNo(model, "5", TEST_EO_Continue);
VoteNo(model, "6", TEST_EO_Continue);
VoteYes(model, "7", TEST_EO_Continue);
SetVoters(model, "2", "4", "5", "6", "8", "9", "10"); // remove 1, 3, 7 - 3 "yes" votes
MakeFaultyNoVote(model, "1", VFR_NotAllowed);
VoteNo(model, "8", TEST_EO_Continue);
VoteYes(model, "9", TEST_EO_Continue);
// Here we're at 3 "no" votes, 3 "yes" votes out of 7 total;
// disconnect "2" and "9" for "no" to win
SetVoters(model, "4", "5", "6", "8", "10");
Issue("Unexpected result after voting users disconnected.");
TEST_ExpectTrue(model.GetStatus() == VPM_Failure);
}
protected static function SubTest_RestrictiveReconnectVoting() {
local VotingModel model;
Context("Testing restrictive \"reconnecting\" voting.");
model = MakeVotingModel(VP_Restrictive);
SetVoters(model, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
VoteYes(model, "1", TEST_EO_Continue);
VoteNo(model, "2", TEST_EO_Continue);
VoteYes(model, "3", TEST_EO_Continue);
VoteNo(model, "4", TEST_EO_Continue);
VoteYes(model, "5", TEST_EO_Continue);
VoteNo(model, "6", TEST_EO_Continue);
// Disconnect 1 3 "yes" voters
SetVoters(model, "2", "4", "5", "6", "7", "8", "9", "10");
VoteYes(model, "7", TEST_EO_Continue);
VoteNo(model, "8", TEST_EO_Continue);
VoteYes(model, "9", TEST_EO_Continue);
MakeFaultyNoVote(model, "1", VFR_NotAllowed);
MakeFaultyNoVote(model, "3", VFR_NotAllowed);
SetVoters(model, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
VoteNo(model, "10", TEST_EO_EndDraw);
}
/* Testing restrictive "reconnecting" voting.
Unexpected result after voting users reconnected. [1] */
protected static function Test_CanLeaveVoting() {
SubTest_CanLeaveYesVoting();
SubTest_CanLeaveNoVoting();
SubTest_CanLeaveDrawVoting();
SubTest_CanLeaveFaultyVoting();
SubTest_CanLeaveDisconnectVoting();
SubTest_CanLeaveReconnectVoting();
}
protected static function SubTest_CanLeaveYesVoting() {
local VotingModel model;
Context("Testing \"yes\" voting where users are allowed to leave.");
model = MakeVotingModel(VP_CanLeave);
SetVoters(model, "1", "2", "3", "4", "5", "6");
VoteYes(model, "2", TEST_EO_Continue);
VoteYes(model, "4", TEST_EO_Continue);
VoteYes(model, "3", TEST_EO_Continue);
SetVoters(model, "1", "5", "6");
Issue("Unexpected result after voting users leaves.");
TEST_ExpectTrue(model.GetStatus() == VPM_InProgress);
VoteYes(model, "1", TEST_EO_End);
}
protected static function SubTest_CanLeaveNoVoting() {
local VotingModel model;
Context("Testing \"no\" voting where users are allowed to leave.");
model = MakeVotingModel(VP_CanLeave);
SetVoters(model, "1", "2", "3", "4", "5", "6");
VoteNo(model, "1", TEST_EO_Continue);
VoteNo(model, "2", TEST_EO_Continue);
VoteNo(model, "6", TEST_EO_Continue);
SetVoters(model, "3", "4", "5");
Issue("Unexpected result after voting users leaves.");
TEST_ExpectTrue(model.GetStatus() == VPM_InProgress);
VoteNo(model, "4", TEST_EO_End);
}
protected static function SubTest_CanLeaveDrawVoting() {
local VotingModel model;
Context("Testing \"draw\" voting where users are allowed to leave.");
model = MakeVotingModel(VP_CanLeave);
SetVoters(model, "1", "2", "3", "4", "5", "6");
VoteYes(model, "3", TEST_EO_Continue);
VoteYes(model, "2", TEST_EO_Continue);
VoteNo(model, "5", TEST_EO_Continue);
VoteYes(model, "1", TEST_EO_Continue);
VoteNo(model, "6", TEST_EO_Continue);
SetVoters(model, "4");
Issue("Unexpected result after voting users leaves.");
TEST_ExpectTrue(model.GetStatus() == VPM_InProgress);
VoteNo(model, "4", TEST_EO_EndDraw);
}
protected static function SubTest_CanLeaveFaultyVoting() {
local VotingModel model;
Context("Testing \"faulty\" voting where users are allowed to leave.");
model = MakeVotingModel(VP_CanLeave);
SetVoters(model, "1", "2", "3", "4", "5", "6");
VoteYes(model, "3", TEST_EO_Continue);
MakeFaultyYesVote(model, "3", VFR_AlreadyVoted);
VoteYes(model, "2", TEST_EO_Continue);
MakeFaultyNoVote(model, "7", VFR_NotAllowed);
VoteNo(model, "5", TEST_EO_Continue);
MakeFaultyNoVote(model, "7", VFR_NotAllowed);
MakeFaultyNoVote(model, "5", VFR_AlreadyVoted);
VoteYes(model, "1", TEST_EO_Continue);
MakeFaultyNoVote(model, "3", VFR_CannotChangeVote);
SetVoters(model, "4", "5", "6");
Issue("Unexpected result after voting users leaves.");
TEST_ExpectTrue(model.GetStatus() == VPM_InProgress);
VoteYes(model, "6", TEST_EO_End);
MakeFaultyYesVote(model, "4", VFR_VotingEnded);
}
protected static function SubTest_CanLeaveDisconnectVoting() {
local VotingModel model;
Context("Testing \"leave\" voting where users are allowed to leave.");
model = MakeVotingModel(VP_CanLeave);
SetVoters(model, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
VoteYes(model, "1", TEST_EO_Continue);
VoteYes(model, "2", TEST_EO_Continue);
VoteYes(model, "3", TEST_EO_Continue);
VoteYes(model, "4", TEST_EO_Continue);
VoteNo(model, "5", TEST_EO_Continue);
VoteNo(model, "6", TEST_EO_Continue);
VoteYes(model, "7", TEST_EO_Continue);
SetVoters(model, "2", "4", "5", "6", "8", "9", "10");
MakeFaultyNoVote(model, "1", VFR_NotAllowed);
VoteNo(model, "8", TEST_EO_Continue);
VoteYes(model, "10", TEST_EO_End);
}
protected static function SubTest_CanLeaveReconnectVoting() {
local VotingModel model;
Context("Testing \"reconnecting\" voting where users are allowed to leave.");
model = MakeVotingModel(VP_CanLeave);
SetVoters(model, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
VoteYes(model, "1", TEST_EO_Continue);
VoteNo(model, "2", TEST_EO_Continue);
VoteYes(model, "3", TEST_EO_Continue);
VoteNo(model, "4", TEST_EO_Continue);
VoteYes(model, "5", TEST_EO_Continue);
VoteNo(model, "6", TEST_EO_Continue);
// Disconnect 1 3 "yes" voters
SetVoters(model, "2", "4", "5", "6", "7", "8", "9", "10");
VoteYes(model, "7", TEST_EO_Continue);
VoteNo(model, "8", TEST_EO_Continue);
VoteNo(model, "9", TEST_EO_Continue);
MakeFaultyNoVote(model, "1", VFR_NotAllowed);
MakeFaultyNoVote(model, "3", VFR_NotAllowed);
VoteYes(model, "10", TEST_EO_EndDraw);
}
protected static function Test_CanChangeVoting() {
SubTest_CanChangeYesVoting();
SubTest_CanChangeNoVoting();
SubTest_CanChangeDrawVoting();
SubTest_CanChangeFaultyVoting();
SubTest_CanChangeDisconnectVoting();
SubTest_CanChangeReconnectVoting();
}
protected static function SubTest_CanChangeYesVoting() {
local VotingModel model;
Context("Testing \"yes\" voting where users are allowed to change their vote.");
model = MakeVotingModel(VP_CanChangeVote);
SetVoters(model, "1", "2", "3", "4", "5", "6");
VoteYes(model, "2", TEST_EO_Continue);
VoteYes(model, "4", TEST_EO_Continue);
VoteYes(model, "3", TEST_EO_Continue);
VoteNo(model, "6", TEST_EO_Continue);
VoteNo(model, "1", TEST_EO_Continue);
VoteYes(model, "6", TEST_EO_End);
}
protected static function SubTest_CanChangeNoVoting() {
local VotingModel model;
Context("Testing \"no\" voting where users are allowed to change their vote.");
model = MakeVotingModel(VP_CanChangeVote);
SetVoters(model, "1", "2", "3", "4", "5", "6");
VoteNo(model, "1", TEST_EO_Continue);
VoteNo(model, "2", TEST_EO_Continue);
VoteNo(model, "6", TEST_EO_Continue);
VoteYes(model, "5", TEST_EO_Continue);
VoteYes(model, "4", TEST_EO_Continue);
VoteNo(model, "5", TEST_EO_End);
}
protected static function SubTest_CanChangeDrawVoting() {
local VotingModel model;
Context("Testing \"draw\" voting where users are allowed to change their vote.");
model = MakeVotingModel(VP_CanChangeVote);
SetVoters(model, "1", "2", "3", "4", "5", "6");
VoteYes(model, "3", TEST_EO_Continue);
VoteYes(model, "2", TEST_EO_Continue);
VoteNo(model, "5", TEST_EO_Continue);
VoteYes(model, "1", TEST_EO_Continue);
VoteNo(model, "1", TEST_EO_Continue);
VoteYes(model, "1", TEST_EO_Continue);
VoteNo(model, "6", TEST_EO_Continue);
VoteNo(model, "4", TEST_EO_EndDraw);
}
protected static function SubTest_CanChangeFaultyVoting() {
local VotingModel model;
Context("Testing \"faulty\" voting where users are allowed to change their vote.");
model = MakeVotingModel(VP_CanChangeVote);
SetVoters(model, "1", "2", "3", "4", "5", "6");
VoteYes(model, "3", TEST_EO_Continue);
VoteYes(model, "5", TEST_EO_Continue);
VoteYes(model, "2", TEST_EO_Continue);
VoteNo(model, "5", TEST_EO_Continue);
VoteYes(model, "1", TEST_EO_Continue);
VoteYes(model, "6", TEST_EO_End);
MakeFaultyYesVote(model, "4", VFR_VotingEnded);
}
protected static function SubTest_CanChangeDisconnectVoting() {
local VotingModel model;
Context("Testing \"disconnect\" voting where users are allowed to change their vote.");
model = MakeVotingModel(VP_CanChangeVote);
SetVoters(model, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
VoteYes(model, "1", TEST_EO_Continue);
VoteYes(model, "2", TEST_EO_Continue);
VoteYes(model, "6", TEST_EO_Continue);
VoteYes(model, "3", TEST_EO_Continue);
VoteNo(model, "5", TEST_EO_Continue);
VoteYes(model, "5", TEST_EO_Continue);
VoteNo(model, "5", TEST_EO_Continue);
VoteYes(model, "4", TEST_EO_Continue);
VoteNo(model, "6", TEST_EO_Continue);
VoteYes(model, "7", TEST_EO_Continue);
SetVoters(model, "2", "4", "5", "6", "8", "9", "10"); // remove 1, 3, 7 - 3 "yes" votes
MakeFaultyNoVote(model, "1", VFR_NotAllowed);
VoteNo(model, "8", TEST_EO_Continue);
VoteYes(model, "5", TEST_EO_Continue);
VoteNo(model, "9", TEST_EO_Continue);
// Here we're at 3 "no" votes, 3 "yes" votes out of 7 total;
// disconnect "6" and "9" for "yes" to win
SetVoters(model, "2", "4", "5", "8", "10");
Issue("Unexpected result after voting users disconnected.");
TEST_ExpectTrue(model.GetStatus() == VPM_Success);
}
protected static function SubTest_CanChangeReconnectVoting() {
local VotingModel model;
Context("Testing \"reconnect\" voting where users are allowed to change their vote.");
model = MakeVotingModel(VP_CanChangeVote);
SetVoters(model, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
VoteYes(model, "1", TEST_EO_Continue);
VoteNo(model, "2", TEST_EO_Continue);
VoteYes(model, "3", TEST_EO_Continue);
VoteNo(model, "4", TEST_EO_Continue);
VoteYes(model, "5", TEST_EO_Continue);
VoteNo(model, "6", TEST_EO_Continue);
// Disconnect 1 3 "yes" voters
SetVoters(model, "2", "4", "5", "6", "7", "8", "9", "10");
VoteYes(model, "7", TEST_EO_Continue);
VoteNo(model, "8", TEST_EO_Continue);
VoteYes(model, "9", TEST_EO_Continue);
MakeFaultyNoVote(model, "1", VFR_NotAllowed);
MakeFaultyNoVote(model, "3", VFR_NotAllowed);
// Restore 3 "yes" voter
SetVoters(model, "2", "3", "4", "5", "6", "7", "8", "9", "10");
VoteNo(model, "3", TEST_EO_End);
}
protected static function Test_All() {
local VotingModel model;
Context("Testing permissive voting options.");
model = MakeVotingModel(VP_CanLeaveAndChangeVote);
SetVoters(model, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
VoteYes(model, "1", TEST_EO_Continue);
VoteYes(model, "2", TEST_EO_Continue);
VoteNo(model, "3", TEST_EO_Continue);
VoteYes(model, "4", TEST_EO_Continue);
VoteNo(model, "5", TEST_EO_Continue);
VoteNo(model, "6", TEST_EO_Continue);
// Disconnect 1 and 5 voters
SetVoters(model, "2", "3", "4", "6", "7", "8", "9", "10");
MakeFaultyNoVote(model, "1", VFR_NotAllowed);
MakeFaultyNoVote(model, "5", VFR_NotAllowed);
VoteYes(model, "3", TEST_EO_Continue);
VoteNo(model, "7", TEST_EO_Continue);
VoteNo(model, "8", TEST_EO_Continue);
VoteNo(model, "9", TEST_EO_Continue);
// Bring back 1, disconnect 3 and 6
SetVoters(model, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
VoteYes(model, "8", TEST_EO_Continue);
VoteNo(model, "4", TEST_EO_Continue);
// Disconnect 10, finishing voting (since now only 9 voters are available)
SetVoters(model, "1", "2", "3", "4", "5", "6", "7", "8", "9");
Issue("Unexpected result after voting users disconnected.");
TEST_ExpectTrue(model.GetStatus() == VPM_Failure);
}
defaultproperties {
caseGroup = "Commands"
caseName = "Voting model"
}

35
sources/InfoQueryHandler/InfoQueryHandler.uc

@ -34,7 +34,7 @@ var private const int TACEDIA_HELP_COMMANDS_CHAT, TACEDIA_HELP_COMMANDS_CONSOLE;
var private const int TACEDIA_HELP_COMMANDS_CHAT_AND_CONSOLE;
var private const int TACEDIA_HELP_COMMANDS_NO, TACEDIA_HELP_COMMANDS_USELESS;
var private const int TACEDIA_RUNNING, TACEDIA_VERSION, TACEDIA_CREDITS;
var private const int TACEDIA_ACKNOWLEDGMENT, TPREFIX, THELP, TSEPARATOR;
var private const int TACEDIA_ACKNOWLEDGMENT, TPREFIX, TSEPARATOR;
public static function StaticConstructor()
{
@ -228,7 +228,7 @@ private final static function StopOutput()
private final static function OutAcediaHelp()
{
local MutableText prefix, helpName, builder;
local MutableText prefix, builder;
default.currentOutput
.Flush()
@ -242,17 +242,12 @@ private final static function OutAcediaHelp()
.GetChatPrefix()
.IntoMutableText()
.ChangeDefaultColor(__().color.TextEmphasis);
helpName = class'Commands_Feature'.static
.GetHelpCommandName()
.IntoMutableText()
.ChangeDefaultColor(__().color.TextEmphasis);
if ( class'Commands_Feature'.static.IsUsingChatInput()
&& class'Commands_Feature'.static.IsUsingMutateInput())
{
builder =
T(default.TACEDIA_HELP_COMMANDS_CHAT_AND_CONSOLE).MutableCopy();
builder.Replace(T(default.TPREFIX), prefix);
builder.Replace(T(default.THELP), helpName);
default.currentOutput.WriteLine(builder);
__().memory.Free(builder);
}
@ -261,24 +256,20 @@ private final static function OutAcediaHelp()
builder =
T(default.TACEDIA_HELP_COMMANDS_CHAT).MutableCopy();
builder.Replace(T(default.TPREFIX), prefix);
builder.Replace(T(default.THELP), helpName);
default.currentOutput.WriteLine(builder);
default.currentOutput.WriteLine(builder);
__().memory.Free(builder);
}
else if (class'Commands_Feature'.static.IsUsingMutateInput())
{
builder =
T(default.TACEDIA_HELP_COMMANDS_CONSOLE).MutableCopy();
builder.Replace(T(default.THELP), helpName);
default.currentOutput.WriteLine(builder);
__().memory.Free(builder);
default.currentOutput
.WriteLine(T(default.TACEDIA_HELP_COMMANDS_CONSOLE));
}
else
{
default.currentOutput
.WriteLine(T(default.TACEDIA_HELP_COMMANDS_USELESS));
}
__().memory.Free2(prefix, helpName);
__().memory.Free(prefix);
}
private final static function OutAcediaStatus()
@ -306,11 +297,11 @@ defaultproperties
TACEDIA_HELP = 2
stringConstants(2) = "Acedia always supports four commands: {$TextEmphasis help}, {$TextEmphasis status}, {$TextEmphasis version} and {$TextEmphasis credits}"
TACEDIA_HELP_COMMANDS_CHAT = 3
stringConstants(3) = "To get detailed information about available to you commands, please type {$TextEmphasis %PREFIX%%HELP%} in chat"
stringConstants(3) = "To get detailed information about available to you commands, please type {$TextEmphasis %PREFIX%help} in chat"
TACEDIA_HELP_COMMANDS_CONSOLE = 4
stringConstants(4) = "To get detailed information about available to you commands, please type {$TextEmphasis mutate %HELP% -l} in console"
stringConstants(4) = "To get detailed information about available to you commands, please type {$TextEmphasis mutate help -l} in console"
TACEDIA_HELP_COMMANDS_CHAT_AND_CONSOLE = 5
stringConstants(5) = "To get detailed information about available to you commands, please type {$TextEmphasis %PREFIX%%HELP%} in chat or {$TextEmphasis mutate %HELP% -l} in console"
stringConstants(5) = "To get detailed information about available to you commands, please type {$TextEmphasis %PREFIX%help} in chat or {$TextEmphasis mutate help -l} in console"
TACEDIA_HELP_COMMANDS_NO = 6
stringConstants(6) = "Unfortunately other commands aren't available right now. To enable them please type {$TextEmphasis mutate acediacommands} in console if you have enough rights to reenable them."
TACEDIA_HELP_COMMANDS_USELESS = 7
@ -320,13 +311,11 @@ defaultproperties
TACEDIA_VERSION = 9
stringConstants(9) = "AcediaCore version 0.1.dev8 - this is a development version, bugs and issues are expected"
TACEDIA_CREDITS = 10
stringConstants(10) = "AcediaCore was developed by dkanus, 2019 - 2023"
stringConstants(10) = "AcediaCore was developed by dkanus, 2019 - 2022"
TACEDIA_ACKNOWLEDGMENT = 11
stringConstants(11) = "Special thanks for NikC- and Chaos for suggestions, testing and discussion"
TPREFIX = 12
stringConstants(12) = "%PREFIX%"
THELP = 13
stringConstants(13) = "%HELP%"
TSEPARATOR = 14
stringConstants(14) = "============================="
TSEPARATOR = 13
stringConstants(13) = "============================="
}

7
sources/Users/ACommandUserGroups.uc

@ -22,6 +22,7 @@ class ACommandUserGroups extends Command
protected function BuildData(CommandDataBuilder builder)
{
builder.Name(P("usergroups"));
builder.Group(P("admin"));
builder.Summary(P("User groups management."));
builder.Describe(P("Allows to add/remove user groups and users to these: groups. Changes made"
@ -76,7 +77,7 @@ protected function BuildData(CommandDataBuilder builder)
builder.Describe(P("Allows to force usage of invalid user IDs."));
}
protected function Executed(CallData arguments, EPlayer instigator, CommandPermissions permissions)
protected function Executed(CallData arguments, EPlayer instigator)
{
local bool forceOption;
local Text groupName, userID, userName, annotation;
@ -652,6 +653,6 @@ private function DisplayUsersFor(Text groupName)
}
}
defaultproperties {
preferredName = "usergroups"
defaultproperties
{
}

55
sources/Users/Users_Feature.uc

@ -70,7 +70,16 @@ var private LoggerAPI.Definition errDBContainsNonLowerRegister;
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();
}
if (_server.IsAvailable()) {
LoadUserData();
SetupPersistentData(usePersistentData);
@ -87,7 +96,9 @@ protected function OnDisabled()
_.users._reloadFeature();
feature =
Commands_Feature(class'Commands_Feature'.static.GetEnabledInstance());
if (feature != none) {
if (feature != none)
{
feature.RemoveCommand(class'ACommandUserGroups');
feature.FreeSelf();
}
ResetUploadedUserGroups();
@ -579,9 +590,9 @@ public final function bool AddGroup(BaseText groupName)
local Text lowerCaseGroupName;
local HashTable emptyHashTable;
if (groupName == none) return false;
if (groupName.Compare(P("all"), SCASE_INSENSITIVE)) return false;
if (groupName == none) {
return false;
}
lowerCaseGroupName = groupName.LowerCopy();
if (loadedGroupToUsersMap.HasKey(lowerCaseGroupName))
{
@ -665,9 +676,9 @@ public final function bool RemoveGroup(BaseText groupName)
local bool groupExists;
local Text lowerCaseGroupName;
if (groupName == none) return false;
if (groupName.Compare(P("all"), SCASE_INSENSITIVE)) return false;
if (groupName == none) {
return false;
}
lowerCaseGroupName = groupName.LowerCopy();
groupExists = loadedGroupToUsersMap.HasKey(lowerCaseGroupName);
if (!groupExists)
@ -743,9 +754,9 @@ public final function bool IsGroupExisting(BaseText groupName)
local bool result;
local Text lowerCaseGroupName;
if (groupName == none) return false;
if (groupName.Compare(P("all"), SCASE_INSENSITIVE)) return true;
if (groupName == none) {
return false;
}
lowerCaseGroupName = groupName.LowerCopy();
result = loadedGroupToUsersMap.HasKey(lowerCaseGroupName);
lowerCaseGroupName.FreeSelf();
@ -797,10 +808,9 @@ public final function bool AddSteamIDToGroup(
local Text lowercaseGroupName;
local HashTable groupUsers;
if (steamID == none) return false;
if (loadedGroupToUsersMap == none) return false;
if (groupName == none) return false;
if (groupName.Compare(P("all"), SCASE_INSENSITIVE)) return true;
if (steamID == none) return false;
if (loadedGroupToUsersMap == none) return false;
if (groupName == none) return false;
lowercaseGroupName = groupName.LowerCopy();
groupUsers = loadedGroupToUsersMap.GetHashTable(lowercaseGroupName);
@ -988,10 +998,9 @@ public final function bool RemoveSteamIDFromGroup(
local Text lowercaseGroupName;
local HashTable groupUsers;
if (steamID == none) return false;
if (groupName == none) return false;
if (groupName.Compare(P("all"), SCASE_INSENSITIVE)) return false;
if (loadedGroupToUsersMap == none) return false;
if (steamID == none) return false;
if (groupName == none) return false;
if (loadedGroupToUsersMap == none) return false;
lowercaseGroupName = groupName.LowerCopy();
groupUsers = loadedGroupToUsersMap.GetHashTable(lowercaseGroupName);
@ -1186,7 +1195,6 @@ public final function array<Text> GetGroupsForSteamID(BaseText steamID)
if (loadedGroupToUsersMap == none) return result;
if (steamID == none) return result;
result[0] = P("all").Copy();
immutableSteamID = steamID.LowerCopy();
iter = HashTableIterator(loadedGroupToUsersMap.Iterate());
while (!iter.HasFinished())
@ -1952,10 +1960,9 @@ public final function bool IsSteamIDInGroup(
local Text lowerGroupName;
local HashTable nextGroupUsers;
if (loadedGroupToUsersMap == none) return false;
if (groupName == none) return false;
if (groupName.Compare(P("all"), SCASE_INSENSITIVE)) return true;
if (steamID == none) return false;
if (loadedGroupToUsersMap == none) return false;
if (groupName == none) return false;
if (steamID == none) return false;
lowerGroupName = groupName.LowerCopy();
nextGroupUsers = loadedGroupToUsersMap.GetHashTable(lowerGroupName);
@ -2121,7 +2128,7 @@ public final /*unreal*/ function bool IsUserInGroup_S(
*
* Data loaded once is cached and this method returning `true` does not
* guarantee that is isn't outdated. Additional, asynchronous queries must be
* made to check for that.
* made to check for that.
*
* @return `true` if user groups' data was loaded and `false` otherwise.
*/

Loading…
Cancel
Save