예제 #1
0
        private void OnCommandSourcesChanged()
        {
            AsyncHelper.RunSync(async() =>
            {
                await m_CommandSources.DisposeAllAsync();

                try
                {
                    m_CommandSources = m_Options.Value.CreateCommandSources(m_ServiceProvider);
                }
                catch (ObjectDisposedException)
                {
                    // https://github.com/openmod/OpenMod/issues/61
                    m_CommandSources = new List <ICommandSource>();
                }

                var commands = new List <ICommandRegistration>();
                foreach (var sources in m_CommandSources)
                {
                    commands.AddRange(await sources.GetCommandsAsync());
                }

                foreach (var registration in commands)
                {
                    var permission = m_CommandPermissionBuilder.GetPermission(registration, commands);

                    m_PermissionRegistry.RegisterPermission(registration.Component,
                                                            permission,
                                                            description: $"Grants access to the {registration.Id} command.",
                                                            defaultGrant: PermissionGrantResult.Default);

                    if (registration.PermissionRegistrations == null)
                    {
                        continue;
                    }

                    foreach (var permissionRegistration in registration.PermissionRegistrations)
                    {
                        m_PermissionRegistry.RegisterPermission(permissionRegistration.Owner,
                                                                $"{permission}.{permissionRegistration.Permission}",
                                                                permissionRegistration.Description,
                                                                permissionRegistration.DefaultGrant);
                    }
                }

                var commandsData = await m_CommandDataStore.GetRegisteredCommandsAsync() ?? new RegisteredCommandsData();
                commandsData.Commands ??= new List <RegisteredCommandData>();

                foreach (var command in commands
                         .Where(d => !commandsData.Commands.Any(c => c.Id.Equals(d.Id, StringComparison.OrdinalIgnoreCase))))
                {
                    commandsData.Commands.Add(CreateDefaultCommandData(command));
                }

                await m_CommandDataStore.SetRegisteredCommandsAsync(commandsData);
            });
        }
예제 #2
0
        protected override async Task OnExecuteAsync()
        {
            var commands   = m_CommandStore.Commands;
            var totalCount = commands.Count;

            const int itemsPerPage = 10;

            int currentPage = 1;

            if (Context.Parameters.Length == 0 || Context.Parameters.TryGet(0, out currentPage))
            {
                if (currentPage < 1)
                {
                    throw new CommandWrongUsageException(Context);
                }

                var pageCommands = commands
                                   .Where(d => d.ParentId == null)
                                   .Skip(itemsPerPage * (currentPage - 1))
                                   .Take(itemsPerPage)
                                   .ToList();

                await PrintPageAsync(currentPage, (int)Math.Ceiling((double)totalCount / itemsPerPage), pageCommands);
            }
            else if (Context.Parameters.Length > 0)
            {
                var context    = m_CommandContextBuilder.CreateContext(Context.Actor, Context.Parameters.ToArray(), Context.CommandPrefix, commands);
                var permission = context.CommandRegistration == null ? null : m_CommandPermissionBuilder.GetPermission(context.CommandRegistration, commands);

                if (context.CommandRegistration == null)
                {
                    await Context.Actor.PrintMessageAsync(m_StringLocalizer["commands:errors:not_found", new { CommandName = context.GetCommandLine(false) }], Color.Red);

                    return;
                }

                if (!string.IsNullOrEmpty(permission) && await m_PermissionChecker.CheckPermissionAsync(Context.Actor, permission) != PermissionGrantResult.Grant)
                {
                    throw new NotEnoughPermissionException(permission, m_StringLocalizer);
                }

                await PrintCommandHelpAsync(context, permission, commands);

                await context.DisposeAsync();
            }
        }
예제 #3
0
        protected virtual string GetPermission(ICommandRegistration commandRegistration, IReadOnlyCollection <ICommandRegistration> commands)
        {
            var permission = commandRegistration == null ? null : m_CommandPermissionBuilder.GetPermission(commandRegistration, commands);

            if (permission == null)
            {
                return(null);
            }

            var registeredPermission = m_PermissionRegistry.FindPermission(commandRegistration.Component, permission);

            if (registeredPermission == null)
            {
                throw new Exception($"Unregistered permission \"{permission}\" in component: {commandRegistration.Component.OpenModComponentId}");
            }

            return($"{registeredPermission.Owner.OpenModComponentId}:{registeredPermission.Permission}");
        }
예제 #4
0
        private void OnCommandSourcesChanged()
        {
            AsyncHelper.RunSync(m_CommandSources.DisposeAllAsync);

            try
            {
                m_CommandSources = m_Options.Value.CreateCommandSources(m_ServiceProvider);
            }
            catch (ObjectDisposedException)
            {
                // https://github.com/openmod/OpenMod/issues/61
                m_CommandSources = new List <ICommandSource>();
            }

            var commands = m_CommandSources.SelectMany(d => d.Commands).ToList();

            foreach (var registration in commands)
            {
                var permission = m_CommandPermissionBuilder.GetPermission(registration, commands);

                m_PermissionRegistry.RegisterPermission(registration.Component,
                                                        permission,
                                                        description: $"Grants access to the {registration.Id} command.",
                                                        defaultGrant: PermissionGrantResult.Default);

                if (registration.PermissionRegistrations == null)
                {
                    continue;
                }

                foreach (var permissionRegistration in registration.PermissionRegistrations)
                {
                    m_PermissionRegistry.RegisterPermission(permissionRegistration.Owner,
                                                            $"{permission}.{permissionRegistration.Permission}",
                                                            permissionRegistration.Description,
                                                            permissionRegistration.DefaultGrant);
                }
            }
        }
예제 #5
0
        public async Task <ICommandContext> ExecuteAsync(ICommandActor actor, string[] args, string prefix)
        {
            if (args == null || args.Length == 0)
            {
                throw new Exception("Can not execute command with null or empty args");
            }

            var currentCommandAccessor = m_LifetimeScope.Resolve <ICurrentCommandContextAccessor>();
            var commandsRegistrations  = m_CommandStore.Commands;
            var logger = m_LifetimeScope.Resolve <ILogger <CommandExecutor> >();
            var commandContextBuilder = m_LifetimeScope.Resolve <ICommandContextBuilder>();
            var permissionChecker     = m_LifetimeScope.Resolve <IPermissionChecker>();
            var stringLocalizer       = m_LifetimeScope.Resolve <IOpenModStringLocalizer>();
            var commandContext        = commandContextBuilder.CreateContext(actor, args, prefix, commandsRegistrations);

            var commandExecutingEvent = new CommandExecutingEvent(actor, commandContext);
            await m_EventBus.EmitAsync(m_Runtime, this, commandExecutingEvent);

            if (commandExecutingEvent.IsCancelled)
            {
                return(commandExecutingEvent.CommandContext);
            }

            logger.LogInformation($"Actor {actor.Type}/{actor.DisplayName} ({actor.Id}) has executed command \"{string.Join(" ", args)}\".");

            try
            {
                if (commandContext.Exception != null)
                {
                    throw commandContext.Exception;
                }

                currentCommandAccessor.Context = commandContext;

                var permission = m_CommandPermissionBuilder.GetPermission(commandContext.CommandRegistration);
                if (!string.IsNullOrWhiteSpace(permission) && await permissionChecker.CheckPermissionAsync(actor, permission) != PermissionGrantResult.Grant)
                {
                    throw new NotEnoughPermissionException(permission, stringLocalizer);
                }

                var command = commandContext.CommandRegistration.Instantiate(commandContext.ServiceProvider);
                await command.ExecuteAsync();

                currentCommandAccessor.Context = null;
            }
            catch (UserFriendlyException ex)
            {
                await actor.PrintMessageAsync(ex.Message, Color.DarkRed);

                commandContext.Exception = ex;
            }
            catch (Exception ex)
            {
                await actor.PrintMessageAsync("An internal error occured during the command execution.", Color.DarkRed);

                logger.LogError(ex, $"Exception occured on command \"{string.Join(" ", args)}\" by actor {actor.Type}/{actor.DisplayName} ({actor.Id})");
                commandContext.Exception = ex;

#if DEBUG
                throw; // in debug mode we want to debug such exceptions instead of catching them
#endif
            }
            finally
            {
                var commandExecutedEvent = new CommandExecutedEvent(actor, commandContext);
                await m_EventBus.EmitAsync(m_Runtime, this, commandExecutedEvent);

                await commandContext.DisposeAsync();
            }

            return(commandContext);
        }
예제 #6
0
        public async Task InvalidateAsync()
        {
            await m_CommandSources.DisposeAllAsync();

            if (m_Runtime.IsDisposing)
            {
                return;
            }

            m_CommandSources = m_Options.Value.CreateCommandSources(m_ServiceProvider);

            if (m_CommandSources.Count == 0)
            {
                m_Logger.LogDebug("InvalidateAsync: failed because no command sources were found; this is normal on booting.");
                return;
            }

            var commands = new List <ICommandRegistration>();

            foreach (var sources in m_CommandSources)
            {
                commands.AddRange(await sources.GetCommandsAsync());
            }

            foreach (var registration in commands)
            {
                var permission = m_CommandPermissionBuilder.GetPermission(registration, commands).Split(':')[1];

                m_PermissionRegistry.RegisterPermission(registration.Component,
                                                        permission,
                                                        description: $"Grants access to the {registration.Id} command.",
                                                        defaultGrant: PermissionGrantResult.Default);

                if (registration.PermissionRegistrations == null)
                {
                    continue;
                }

                foreach (var permissionRegistration in registration.PermissionRegistrations)
                {
                    m_PermissionRegistry.RegisterPermission(permissionRegistration.Owner,
                                                            $"{permission}.{permissionRegistration.Permission}",
                                                            permissionRegistration.Description,
                                                            permissionRegistration.DefaultGrant);
                }
            }

            var commandsData = await m_CommandDataStore.GetRegisteredCommandsAsync();

            if (commandsData?.Commands == null)
            {
                throw new Exception("Failed to register commands: command data was null");
            }

            foreach (var command in commands
                     .Where(d => !commandsData.Commands.Any(c =>
                                                            c.Id?.Equals(d.Id, StringComparison.OrdinalIgnoreCase) ?? false)))
            {
                commandsData.Commands.Add(CreateDefaultCommandData(command));
            }

            if (commandsData.Commands.Count == 0)
            {
                throw new Exception("Failed to register commands: command data was empty.");
            }

            await m_CommandDataStore.SetRegisteredCommandsAsync(commandsData);

            m_Logger.LogDebug($"Reloaded {commands.Count} commands.");
        }
예제 #7
0
        private void AppendCommand(
            StringBuilder markdownBuilder,
            ICommandRegistration command,
            List <ICommandRegistration> commands,
            ICommandContext commandContext)
        {
            markdownBuilder.Append("- ").Append(commandContext.CommandPrefix).Append(commandContext.CommandAlias);
            if (!string.IsNullOrEmpty(command.Syntax))
            {
                markdownBuilder.Append(' ').Append(command.Syntax);
            }

            if (!string.IsNullOrEmpty(command.Description))
            {
                markdownBuilder.Append(": ").Append(command.Description);
            }

            markdownBuilder.AppendLine();
            markdownBuilder.Append("  id: ").AppendLine(command.Id);
            var permissionRegistrations = new List <IPermissionRegistration>();

            var commandPermission = m_PermissionBuilder.GetPermission(command, commands);

            var commandPermissionRegistration = m_PermissionRegistry.FindPermission(m_Plugin, commandPermission);

            if (commandPermissionRegistration != null)
            {
                permissionRegistrations.Add(commandPermissionRegistration);
            }

            if (command.PermissionRegistrations != null)
            {
                var prefixedPermissions = new List <IPermissionRegistration>();

                foreach (var permission in command.PermissionRegistrations)
                {
                    prefixedPermissions.Add(new PermissionRegistration
                    {
                        Permission   = commandPermission + "." + permission.Permission,
                        Description  = permission.Description,
                        DefaultGrant = permission.DefaultGrant,
                        Owner        = permission.Owner
                    });
                }

                permissionRegistrations.AddRange(prefixedPermissions);
            }

            if (permissionRegistrations.Count > 0)
            {
                markdownBuilder.AppendLine("  permissions:");

                foreach (var permissionRegistration in permissionRegistrations)
                {
                    markdownBuilder.Append("  - ").Append(permissionRegistration.Permission);
                    if (!string.IsNullOrEmpty(permissionRegistration.Description))
                    {
                        markdownBuilder.Append(": ").Append(permissionRegistration.Description);
                    }

                    markdownBuilder.AppendLine();
                }

                m_PrintedCommandPermissions.AddRange(permissionRegistrations);
            }

            var childCommands = commands
                                .Where(d => string.Equals(d.ParentId, command.Id, StringComparison.OrdinalIgnoreCase));

            foreach (var child in childCommands)
            {
                var ctx2 = m_CommandContextBuilder.CreateContext(null, new string[] { command.Name, child.Name }, string.Empty, commands);
                AppendCommand(markdownBuilder, child, commands, ctx2);
            }
            commandContext.DisposeAsync().GetAwaiter().GetResult();
        }
예제 #8
0
        private void AppendCommand(
            StringBuilder markdownBuilder,
            ICommandRegistration command,
            List <ICommandRegistration> commands,
            List <string> args)
        {
            var ctx = m_CommandContextBuilder.CreateContext(null, args.ToArray(), string.Empty, commands);

            try
            {
                markdownBuilder.Append($"- {ctx.CommandPrefix}{ctx.CommandAlias}");
                if (!string.IsNullOrEmpty(command.Syntax))
                {
                    markdownBuilder.Append($" {command.Syntax}");
                }

                if (!string.IsNullOrEmpty(command.Description))
                {
                    markdownBuilder.Append($": {command.Description}");
                }

                markdownBuilder.AppendLine("  ");
                markdownBuilder.AppendLine($"  id: {command.Id}  ");
                var permissionRegistrations = new List <IPermissionRegistration>();

                var commandPermission = m_PermissionBuilder.GetPermission(command, commands);

                var commandPermissionRegistration = m_PermissionRegistry.FindPermission(m_Plugin, commandPermission);
                if (commandPermissionRegistration != null)
                {
                    permissionRegistrations.Add(commandPermissionRegistration);
                }

                if (command.PermissionRegistrations != null)
                {
                    var prefixedPermissions = new List <IPermissionRegistration>();

                    foreach (var permission in command.PermissionRegistrations)
                    {
                        prefixedPermissions.Add(new PermissionRegistration
                        {
                            Permission   = commandPermission + "." + permission.Permission,
                            Description  = permission.Description,
                            DefaultGrant = permission.DefaultGrant,
                            Owner        = permission.Owner
                        });
                    }

                    permissionRegistrations.AddRange(prefixedPermissions);
                }

                if (permissionRegistrations.Count > 0)
                {
                    markdownBuilder.AppendLine($"  permissions: ");

                    foreach (var permissionRegistration in permissionRegistrations)
                    {
                        markdownBuilder.Append($"  - {permissionRegistration.Owner.OpenModComponentId}:{permissionRegistration.Permission}");
                        if (!string.IsNullOrEmpty(permissionRegistration.Description))
                        {
                            markdownBuilder.Append($": {permissionRegistration.Description}");
                        }

                        markdownBuilder.AppendLine("  ");
                    }

                    m_PrintedCommandPermissions.AddRange(permissionRegistrations);
                }

                var childCommands = commands
                                    .Where(d => string.Equals(d.ParentId, command.Id, StringComparison.OrdinalIgnoreCase));
                foreach (var child in childCommands)
                {
                    args.Add(child.Name);
                    AppendCommand(markdownBuilder, child, commands, args);
                }
            }
            finally
            {
                ctx.DisposeAsync().GetAwaiter().GetResult();
            }
        }
예제 #9
0
        public async Task <ICommandContext> ExecuteAsync(ICommandActor actor, string[] args, string prefix)
        {
            if (args == null || args.Length == 0)
            {
                throw new Exception("Can not execute command with null or empty args");
            }

            m_Logger.LogInformation($"Actor {actor.Type}/{actor.DisplayName} ({actor.Id}) has executed command \"{string.Join(" ", args)}\".");

            var currentCommandAccessor = m_LifetimeScope.Resolve <ICurrentCommandContextAccessor>();
            var commandContextBuilder  = m_LifetimeScope.Resolve <ICommandContextBuilder>();
            var stringLocalizer        = m_LifetimeScope.Resolve <IOpenModStringLocalizer>();

            var commandsRegistrations = await m_CommandStore.GetCommandsAsync();

            var commandContext        = commandContextBuilder.CreateContext(actor, args, prefix, commandsRegistrations);
            var commandExecutingEvent = new CommandExecutingEvent(actor, commandContext);
            await m_EventBus.EmitAsync(m_Runtime, this, commandExecutingEvent);

            if (commandExecutingEvent.IsCancelled)
            {
                return(commandExecutingEvent.CommandContext);
            }

            try
            {
                if (commandContext.Exception != null)
                {
                    throw commandContext.Exception;
                }

                currentCommandAccessor.Context = commandContext;

                var permission        = m_CommandPermissionBuilder.GetPermission(commandContext.CommandRegistration);
                var permissionChecker = m_Runtime.Host.Services.GetRequiredService <IPermissionChecker>();

                if (!string.IsNullOrWhiteSpace(permission) && await permissionChecker.CheckPermissionAsync(actor, permission) != PermissionGrantResult.Grant)
                {
                    throw new NotEnoughPermissionException(stringLocalizer, permission);
                }

                Stopwatch sw = new Stopwatch();
                sw.Start();
                var command = commandContext.CommandRegistration.Instantiate(commandContext.ServiceProvider);
                await command.ExecuteAsync();

                m_Logger.LogDebug($"Command \"{string.Join(" ", args)}\" executed in {sw.ElapsedMilliseconds}ms");
                sw.Reset();

                currentCommandAccessor.Context = null;
            }
            catch (UserFriendlyException ex)
            {
                commandContext.Exception = ex;
            }
            catch (Exception ex)
            {
                commandContext.Exception = ex;
            }
            finally
            {
                var commandExecutedEvent = new CommandExecutedEvent(actor, commandContext);
                await m_EventBus.EmitAsync(m_Runtime, this, commandExecutedEvent);

                if (commandContext.Exception != null && !commandExecutedEvent.ExceptionHandled)
                {
                    if (commandContext.Exception is UserFriendlyException)
                    {
                        await actor.PrintMessageAsync(commandContext.Exception.Message, Color.DarkRed);
                    }
                    else
                    {
                        await actor.PrintMessageAsync("An internal error occured during the command execution.", Color.DarkRed);

                        m_Logger.LogError(commandContext.Exception, $"Exception occured on command \"{string.Join(" ", args)}\" by actor {actor.Type}/{actor.DisplayName} ({actor.Id})");
                    }
                }

                await commandContext.DisposeAsync();
            }

            return(commandContext);
        }