diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs
index d39b84e0c..ee7b7623d 100644
--- a/TShockAPI/Commands.cs
+++ b/TShockAPI/Commands.cs
@@ -1176,7 +1176,7 @@ private static void ManageUsers(CommandArgs args)
try
{
- TShock.UserAccounts.SetUserGroup(account, args.Parameters[2]);
+ TShock.UserAccounts.SetUserGroup(args.Player, account, args.Parameters[2]);
TShock.Log.ConsoleInfo(GetString("{0} changed account {1} to group {2}.", args.Player.Name, account.Name, args.Parameters[2]));
args.Player.SendSuccessMessage(GetString("Account {0} has been changed to group {1}.", account.Name, args.Parameters[2]));
@@ -1193,6 +1193,10 @@ private static void ManageUsers(CommandArgs args)
{
args.Player.SendErrorMessage(GetString($"User {account.Name} does not exist."));
}
+ catch (UserGroupUpdateLockedException)
+ {
+ args.Player.SendErrorMessage(GetString("Hook blocked the attempt to change the user group."));
+ }
catch (UserAccountManagerException e)
{
args.Player.SendErrorMessage(GetString($"User {account.Name} could not be added. Check console for details."));
diff --git a/TShockAPI/DB/UserManager.cs b/TShockAPI/DB/UserManager.cs
index 0354989e7..6fc50d98d 100644
--- a/TShockAPI/DB/UserManager.cs
+++ b/TShockAPI/DB/UserManager.cs
@@ -25,6 +25,7 @@ You should have received a copy of the GNU General Public License
using System.Text.RegularExpressions;
using BCrypt.Net;
using System.Security.Cryptography;
+using TShockAPI.Hooks;
namespace TShockAPI.DB
{
@@ -166,7 +167,41 @@ public void SetUserGroup(UserAccount account, string group)
if (null == grp)
throw new GroupNotExistsException(group);
- if (_database.Query("UPDATE Users SET UserGroup = @0 WHERE Username = @1;", group, account.Name) == 0)
+ if (AccountHooks.OnAccountGroupUpdate(account, ref grp))
+ throw new UserGroupUpdateLockedException(account.Name);
+
+ if (_database.Query("UPDATE Users SET UserGroup = @0 WHERE Username = @1;", grp.Name, account.Name) == 0)
+ throw new UserAccountNotExistException(account.Name);
+
+ try
+ {
+ // Update player group reference for any logged in player
+ foreach (var player in TShock.Players.Where(p => p != null && p.Account != null && p.Account.Name == account.Name))
+ {
+ player.Group = grp;
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new UserAccountManagerException(GetString("SetUserGroup SQL returned an error"), ex);
+ }
+ }
+ ///
+ /// Sets the group for a given username
+ ///
+ /// Who changes the group
+ /// The user account
+ /// The user account group to be set
+ public void SetUserGroup(TSPlayer author, UserAccount account, string group)
+ {
+ Group grp = TShock.Groups.GetGroupByName(group);
+ if (null == grp)
+ throw new GroupNotExistsException(group);
+
+ if (AccountHooks.OnAccountGroupUpdate(account, author, ref grp))
+ throw new UserGroupUpdateLockedException(account.Name);
+
+ if (_database.Query("UPDATE Users SET UserGroup = @0 WHERE Username = @1;", grp.Name, account.Name) == 0)
throw new UserAccountNotExistException(account.Name);
try
@@ -619,7 +654,7 @@ public UserAccountExistsException(string name)
public class UserAccountNotExistException : UserAccountManagerException
{
/// Creates a new UserAccountNotExistException object, with the user account name in the message.
- /// The user account name to be pasesd in the message.
+ /// The user account name to be passed in the message.
/// A new UserAccountNotExistException object with a message containing the user account name that does not exist.
public UserAccountNotExistException(string name)
: base(GetString($"User account {name} does not exist"))
@@ -627,6 +662,20 @@ public UserAccountNotExistException(string name)
}
}
+ /// The UserGroupUpdateLockedException used when the user group update failed and the request failed as a result..
+ [Serializable]
+ public class UserGroupUpdateLockedException : UserAccountManagerException
+ {
+ /// Creates a new UserGroupUpdateLockedException object.
+ /// The name of the user who failed to change the group.
+ /// New UserGroupUpdateLockedException object with a message containing the name of the user account that failed to change the group.
+ public UserGroupUpdateLockedException(string name) :
+ base(GetString($"Unable to update group of user {name}."))
+ {
+ }
+ }
+
+
/// A GroupNotExistsException, used when a group does not exist.
[Serializable]
public class GroupNotExistsException : UserAccountManagerException
diff --git a/TShockAPI/Hooks/AccountHooks.cs b/TShockAPI/Hooks/AccountHooks.cs
index 9c08b26dc..ae9fff244 100644
--- a/TShockAPI/Hooks/AccountHooks.cs
+++ b/TShockAPI/Hooks/AccountHooks.cs
@@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
+using System.ComponentModel;
using TShockAPI.DB;
namespace TShockAPI.Hooks
{
@@ -39,6 +40,31 @@ public AccountCreateEventArgs(UserAccount account)
}
}
+ public class AccountGroupUpdateEventArgs : HandledEventArgs
+ {
+ public string AccountName { get; private set; }
+ public Group Group { get; set; }
+
+ public AccountGroupUpdateEventArgs(string accountName, Group group)
+ {
+ this.AccountName = accountName;
+ this.Group = group;
+ }
+ }
+
+ public class AccountGroupUpdateByPlayerEventArgs : AccountGroupUpdateEventArgs
+ {
+ ///
+ /// The player who updated the user's group
+ ///
+ public TSPlayer Player { get; private set; }
+
+ public AccountGroupUpdateByPlayerEventArgs(TSPlayer player, string accountName, Group group) : base(accountName, group)
+ {
+ this.Player = player;
+ }
+ }
+
public class AccountHooks
{
public delegate void AccountCreateD(AccountCreateEventArgs e);
@@ -62,5 +88,25 @@ public static void OnAccountDelete(UserAccount u)
AccountDelete(new AccountDeleteEventArgs(u));
}
+
+ public delegate void AccountGroupUpdateD(AccountGroupUpdateEventArgs e);
+ public static event AccountGroupUpdateD AccountGroupUpdate;
+
+ public static bool OnAccountGroupUpdate(UserAccount account, TSPlayer author, ref Group group)
+ {
+ AccountGroupUpdateEventArgs args = new AccountGroupUpdateByPlayerEventArgs(author, account.Name, group);
+ AccountGroupUpdate?.Invoke(args);
+ group = args.Group;
+
+ return args.Handled;
+ }
+ public static bool OnAccountGroupUpdate(UserAccount account, ref Group group)
+ {
+ AccountGroupUpdateEventArgs args = new AccountGroupUpdateEventArgs(account.Name, group);
+ AccountGroupUpdate?.Invoke(args);
+ group = args.Group;
+
+ return args.Handled;
+ }
}
}
diff --git a/TShockAPI/Rest/RestManager.cs b/TShockAPI/Rest/RestManager.cs
index c41e7767b..0a96b004b 100644
--- a/TShockAPI/Rest/RestManager.cs
+++ b/TShockAPI/Rest/RestManager.cs
@@ -555,7 +555,8 @@ private object UserUpdateV2(RestRequestArgs args)
{
try
{
- TShock.UserAccounts.SetUserGroup(account, group);
+ TShock.UserAccounts.SetUserGroup(new TSRestPlayer(args.TokenData.Username, TShock.Groups.GetGroupByName(args.TokenData.UserGroupName)),
+ account, group);
response.Add("group-response", "Group updated successfully");
}
catch (Exception e)
diff --git a/docs/changelog.md b/docs/changelog.md
index d4ab3cfeb..f0a5436c8 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -89,6 +89,7 @@ Use past tense when adding new entries; sign your name off when you add or chang
* Added a method `TSPlayer.GiveItem`, which has `TShockAPI.NetItem` structure in its arguments. (@AgaSpace)
* Added a property `TSPlayer.Hostile`, which gets pvp player mode. (@AgaSpace)
* Fixed typo in `/gbuff`. (@sgkoishi, #2955)
+* Added a hook `AccountHooks.AccountGroupUpdate`, which is called when you change the user group. (@AgaSpace)
## TShock 5.2
* An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK)