From 8ddba36094ce2a91e5303aa29dddfd5a5dc8c193 Mon Sep 17 00:00:00 2001 From: Xander Stoffels Date: Thu, 15 Sep 2022 11:09:29 +0900 Subject: [PATCH] Switching game modes now requires OP. Provide user feedback on command failure. --- .../_Attributes/RequirePermissionAttribute.cs | 21 +++++++++------- Obsidian/Commands/Framework/CommandHandler.cs | 25 ++++++++++++++++--- .../Commands/Framework/Entities/Command.cs | 7 +++++- .../Exceptions/NoPermissionException.cs | 19 ++++++++++++++ Obsidian/Commands/MainCommandModule.cs | 1 + 5 files changed, 60 insertions(+), 13 deletions(-) create mode 100644 Obsidian/Commands/Framework/Exceptions/NoPermissionException.cs diff --git a/Obsidian.API/_Attributes/RequirePermissionAttribute.cs b/Obsidian.API/_Attributes/RequirePermissionAttribute.cs index 23d28afe2..6a834fd24 100644 --- a/Obsidian.API/_Attributes/RequirePermissionAttribute.cs +++ b/Obsidian.API/_Attributes/RequirePermissionAttribute.cs @@ -3,15 +3,18 @@ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public sealed class RequirePermissionAttribute : BaseExecutionCheckAttribute { - private string[] permissions; - private PermissionCheckType checkType; - private bool op; + private readonly string[] _permissions; + private readonly PermissionCheckType _checkType; + private readonly bool _op; + + public PermissionCheckType CheckType => _checkType; + public string[] RequiredPermissions => _permissions; public RequirePermissionAttribute(PermissionCheckType checkType = PermissionCheckType.All, bool op = true, params string[] permissions) { - this.permissions = permissions; - this.checkType = checkType; - this.op = op; + _permissions = permissions; + _checkType = checkType; + _op = op; } public override Task RunChecksAsync(CommandContext context) @@ -20,11 +23,11 @@ public override Task RunChecksAsync(CommandContext context) return Task.FromResult(true); if (context.Player == null) return Task.FromResult(false); - if (this.op && context.Player.IsOperator) + if (_op && context.Player.IsOperator) return Task.FromResult(true); - if (this.permissions.Length > 0) - return Task.FromResult(checkType == PermissionCheckType.All ? context.Player.HasAllPermissions(permissions) : context.Player.HasAnyPermission(permissions)); + if (_permissions.Length > 0) + return Task.FromResult(_checkType == PermissionCheckType.All ? context.Player.HasAllPermissions(_permissions) : context.Player.HasAnyPermission(_permissions)); return Task.FromResult(false); } diff --git a/Obsidian/Commands/Framework/CommandHandler.cs b/Obsidian/Commands/Framework/CommandHandler.cs index 1a4346733..e72ca5fcf 100644 --- a/Obsidian/Commands/Framework/CommandHandler.cs +++ b/Obsidian/Commands/Framework/CommandHandler.cs @@ -182,13 +182,33 @@ public async Task ProcessCommand(CommandContext ctx) // if string is "command-qualified" we'll try to execute it. string[] command = CommandParser.SplitQualifiedString(qualified); // first, parse the command - await ExecuteCommand(command, ctx); + try + { + await ExecuteCommand(command, ctx); + } + catch (CommandExecutionCheckException ex) + { + await ProvideFeedbackToSender(ctx, ex); + } } } + private static async Task ProvideFeedbackToSender(CommandContext ctx, CommandExecutionCheckException ex) + { + switch (ex) + { + case NoPermissionException: + await ctx.Sender.SendMessageAsync(ChatMessage.Simple("You are not allowed to execute this command", ChatColor.Red)); + break; + default: + await ctx.Sender.SendMessageAsync(ChatMessage.Simple(ex.Message, ChatColor.Red)); + break; + } + } + private async Task ExecuteCommand(string[] command, CommandContext ctx) { - Command cmd = null; + Command? cmd = default; var args = command; // Search for correct Command class in this._commands. @@ -201,7 +221,6 @@ private async Task ExecuteCommand(string[] command, CommandContext ctx) if (cmd is not null) { ctx.Plugin = cmd.Plugin?.Plugin; - await cmd.ExecuteAsync(ctx, args); } else diff --git a/Obsidian/Commands/Framework/Entities/Command.cs b/Obsidian/Commands/Framework/Entities/Command.cs index 41efebe1e..2ba8bc146 100644 --- a/Obsidian/Commands/Framework/Entities/Command.cs +++ b/Obsidian/Commands/Framework/Entities/Command.cs @@ -1,6 +1,7 @@ using Obsidian.Commands.Framework.Exceptions; using Obsidian.Plugins; using System.Reflection; +using System.Runtime.CompilerServices; namespace Obsidian.Commands.Framework.Entities; @@ -157,7 +158,11 @@ public async Task ExecuteAsync(CommandContext context, string[] args) { // A check failed. // TODO: Tell user what arg failed? - throw new CommandExecutionCheckException($"One or more execution checks failed."); + throw c switch + { + RequirePermissionAttribute r => new NoPermissionException(r.RequiredPermissions, r.CheckType), + _ => new CommandExecutionCheckException($"One or more execution checks failed."), + }; } } diff --git a/Obsidian/Commands/Framework/Exceptions/NoPermissionException.cs b/Obsidian/Commands/Framework/Exceptions/NoPermissionException.cs new file mode 100644 index 000000000..e8e4564a5 --- /dev/null +++ b/Obsidian/Commands/Framework/Exceptions/NoPermissionException.cs @@ -0,0 +1,19 @@ +namespace Obsidian.Commands.Framework.Exceptions; + +/// +/// Exception indicating that a command can not be executed by a certain CommandExecutor due to missing required permissions. +/// +internal class NoPermissionException : CommandExecutionCheckException +{ + public PermissionCheckType CheckType { get; } + public string[] RequiredPermissions { get; } + + + public NoPermissionException(string[] requiredPermsissions, PermissionCheckType checkType) : base("CommandSender does not have the required permissions.") + { + RequiredPermissions = requiredPermsissions; + CheckType = checkType; + } + +} + diff --git a/Obsidian/Commands/MainCommandModule.cs b/Obsidian/Commands/MainCommandModule.cs index b33d7f1cb..03cd164f4 100644 --- a/Obsidian/Commands/MainCommandModule.cs +++ b/Obsidian/Commands/MainCommandModule.cs @@ -197,6 +197,7 @@ public Task UptimeAsync(CommandContext ctx) [Command("gamemode")] [CommandInfo("Change your gamemode.", "/gamemode ")] [IssuerScope(CommandIssuers.Client)] + [RequirePermission(op: true, permissions: "obsidian.gamemode")] public async Task GamemodeAsync(CommandContext ctx, string gamemode) { var player = ctx.Player;