feat(game): slash commands accept permission grants

A command may be used when the character's GM level allows it or a grant does: a grant of the command, of every
command up to a GM level, or of the dashboard permission the command follows (so accounts_kick covers /kick). A deny
of any of those takes it away even when the level allows it, except from GM 9 accounts (also while they play at a
lower level). Grants never take anyone below a command's floor above GM 1 (/execute), and commands the client handles
keep their fixed level. Expired grants count for nothing. The grants are the account's and the logged-in
character's, read from the database the first time a command is used and kept on the User. /help lists the commands
a player may use this way. The self and rank rules for commands that act on another player count grants of self_* and
manage_equal_rank too. A denied command says it was taken away.

Check: grant a GM 0 account /spawn (or "every command up to GM 8") on the dashboard, relog, and /spawn works and
shows in /help; deny /spawn from a GM 8 character: "it was taken away"; grant accounts_kick and /kick works (the rank
rules still apply to whom). dGameTests SlashCommandGrantsTest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-29 01:11:17 -05:00
parent 868f34123f
commit e00d22152e
4 changed files with 186 additions and 10 deletions

View File

@@ -11,6 +11,7 @@
#include "dCommonVars.h"
#include "AccountRules.h"
#include "SlashCommandLevels.h"
#include "PermissionGrants.h"
#include <functional>
#include <optional>
#include <string>
@@ -80,7 +81,8 @@ namespace SlashCommandHandler {
* their own level only with manage_equal_rank, and on themselves only with the self_* permission for the rule
* (OTHERS: on themselves always, as before).
*/
AccountRules::eManageDenial TargetDenial(uint8_t actorLevel, uint32_t actorAccountId, uint8_t targetLevel, uint32_t targetAccountId, SlashCommandLevels::eTargetRule rule);
AccountRules::eManageDenial TargetDenial(uint8_t actorLevel, uint32_t actorAccountId, uint8_t targetLevel, uint32_t targetAccountId, SlashCommandLevels::eTargetRule rule,
const PermissionGrants::Held* actorGrants = nullptr);
// Why a command may not be used on someone, for the chat; empty when it may
std::string TargetRefusal(AccountRules::eManageDenial denial, SlashCommandLevels::eTargetRule rule, std::string_view command);
@@ -93,6 +95,20 @@ namespace SlashCommandHandler {
// The account of the player using a command (0 if unknown)
uint32_t AccountOf(Entity* player);
/**
* The permission grants of a player's account and logged-in character (PermissionGrants.h), loaded from the database
* the first time they are needed and kept on the User until the dashboard changes them (DashboardActions). nullptr:
* not a player.
*/
const PermissionGrants::Held* GrantsOf(Entity* player);
// Whether a player may use a command now: its level, or a grant for it (the command, a command group, or the
// dashboard permission it follows), unless a deny takes it away (PermissionGrants::MayUseCommand)
bool MayUse(Entity* player, const Command& command);
// The command as the grant rules see it: its name, level now, floor, and the dashboard permission it follows
PermissionGrants::Command GrantRules(const Command& command);
};
namespace GMZeroCommands {