跳转至

注册自定义命令

SerenityJS提供了强大的自定义命令注册工具,可以在服务器实例中使用。本指南将指导你完成创建自定义命令的全过程,包括定义其属性和重载。

命令注册基础

在SerenityJS中,命令与世界关联。这意味着不同的世界可以拥有各自独立的命令集。要注册命令,首先需要确定要将其注册到哪个世界,这通过访问世界实例的commandPalette属性来实现。下面是一个简单自定义命令的示例。

在这些示例中,我们需要一个World实例来注册命令。

在第一个例子中,我们将创建一个名为ping的简单命令,执行时会返回"Pong!"。这个命令不需要任何参数,任何命令发送者都可以执行。

import { World } from "@serenityjs/core";

// 为给定的世界注册一个命令的函数
function register(world: World): void {
  // 在这个示例中,我们将注册一个简单的命令"ping"
  // 执行时会返回"Pong!"

  // 命令通过世界实例的"commandPalette"属性来注册
  world.commandPalette.register(
    "ping", // 命令的名称
    "Ping the server to check if it's responsive.", // 命令的简短描述
    // 当没有重载匹配时的默认执行器函数
    () => {
      return {
        message: "Pong!"
      };
    }
  );
}

在下一个示例中,我们将为命令分配额外的权限。SerenityJS提供了一个权限系统,允许你根据命令发送者的权限来限制命令的访问。在本例中,我们将ping命令限制为只能由具有example.ping权限的玩家执行。

import { World } from "@serenityjs/core";

// 为给定的世界注册一个命令的函数
function register(world: World): void {
  // 在这个示例中,我们将注册一个简单的命令"ping"
  // 执行时会返回"Pong!"

  // 命令通过世界实例的"commandPalette"属性来注册
  world.commandPalette.register(
    "ping", // 命令的名称
    "Ping the server to check if it's responsive.", // 命令的简短描述
    // 这个函数被称为注册器函数
    // 它允许你定义命令的额外属性
    // 以及用于不同参数结构的重载
    (registry) => {
      // 为该命令分配权限
      registry.permissions = ["example.ping"];

      // 注册器对象还允许你将命令标记为调试命令
      // 这意味着命令在游戏内命令列表中会显示为蓝色
      // 功能上,这不会改变任何东西
      registry.debug = true;
    },
    // 当没有重载匹配时的默认执行器函数
    () => {
      return {
        message: "Pong!"
      };
    }
  );
}

在下一个示例中,我们将创建一个具有重载的命令。重载允许你定义命令的不同参数结构。这意味着你可以有多种方式执行同一命令,每种方式都有自己的参数集。在本例中,我们为ping命令创建一个重载,该重载接受一个名为target的单个字符串参数。执行时,命令会返回"Pong, {target}!"。

import { TargetEnum, World } from "@serenityjs/core";

// 为给定的世界注册一个命令的函数
function register(world: World): void {
  // 在这个示例中,我们将注册一个简单的命令"ping"
  // 执行时会返回"Pong!"

  // 命令通过世界实例的"commandPalette"属性来注册
  world.commandPalette.register(
    "ping", // 命令的名称
    "Ping the server to check if it's responsive.", // 命令的简短描述
    // 这个函数被称为注册器函数
    // 它允许你定义命令的额外属性
    // 以及用于不同参数结构的重载
    (registry) => {
      // 为该命令分配权限
      registry.permissions = ["example.ping"];

      // 注册器对象还允许你将命令标记为调试命令
      // 这意味着命令在游戏内命令列表中会显示为蓝色
      // 功能上,这不会改变任何东西
      registry.debug = true;

      // 为命令注册一个重载
      registry.overload(
        {
          // 我们定义了一个名为"target"的参数
          // 你可以在此处找到所有可用参数类型的列表:https://www.serenityjs.net/classes/_serenityjs_core.Enum.html
          target: [TargetEnum, false] // false表示该参数不是可选的
        },
        // 该重载的执行器函数
        ({ target }) => {
          // 验证target参数
          if (!target.result || target.result?.length > 1) {
            // 如果参数无效则抛出错误
            throw new Error(
              "Invalid target argument, expected a single value."
            );
          }

          // 获取target参数的值
          const value = target.result[0].isPlayer()
            ? target.result[0].username
            : target.result[0].type.identifier;

          // 返回响应消息
          return {
            message: `Pong, ${value}!`
          };
        }
      );
    },
    // 当没有重载匹配时的默认执行器函数
    () => {
      return {
        message: "Pong!"
      };
    }
  );
}

在下一个示例中,我们将创建一个使用自定义枚举的命令,以及一个目标参数。该命令还将演示如何使用命令上下文的origin属性来获取有关命令发送者的信息。在本例中,我们将创建一个名为kit的命令,该命令将beginner | intermediate | professional项目套件分配给target参数。

import { CustomEnum, Player, TargetEnum, World } from "@serenityjs/core";

// 为kit参数创建一个自定义枚举
class KitEnum extends CustomEnum {
  public static readonly identifier = "kit_enum";
  public static readonly options = ["beginner", "intermediate", "professional"];
}

// 为给定的世界注册一个命令的函数
function register(world: World): void {
  // 在这个示例中,我们将注册一个命令"kit"
  // 该命令会给玩家分配一个项目套件

  // 命令通过世界实例的"commandPalette"属性来注册
  world.commandPalette.register(
    "kit", // 命令的名称
    "Give a kit to a player.", // 命令的简短描述
    // 这个函数被称为注册器函数
    // 它允许你定义命令的额外属性
    // 以及用于不同参数结构的重载
    (registry) => {
      // 为该命令分配权限
      registry.permissions = ["serenity.operator"];

      // 为命令注册一个重载
      registry.overload(
        {
          target: TargetEnum, // 接收套件的玩家
          kit: KitEnum // 要分配的套件
        },
        // 该重载的执行器函数
        ({ origin, target, kit }) => {
          // 验证命令发送者是否为玩家
          if (!(origin instanceof Player)) {
            // 如果命令发送者不是玩家则抛出错误
            throw new Error("Only players can use this command.");
          }

          // 准备一个计数变量用于计算分配的套件数量
          let giveCount = 0;

          // 遍历target参数
          for (const entity of target.result ?? []) {
            // 验证实体是否为玩家
            if (!entity.isPlayer()) continue;

            // 根据kit参数进行切换
            switch (kit.result) {
              default:
                break;

              case "beginner": {
                // 给予初学者套件
                entity.executeCommand("give @s minecraft:wooden_sword 1");
                entity.executeCommand("give @s minecraft:apple 5");
                entity.executeCommand("give @s minecraft:leather_chestplate 1");
                break;
              }

              case "intermediate": {
                // 给予中级套件
                entity.executeCommand("give @s minecraft:stone_sword 1");
                entity.executeCommand("give @s minecraft:apple 25");
                entity.executeCommand("give @s minecraft:iron_chestplate 1");
                break;
              }

              case "professional": {
                // 给予专业套件
                entity.executeCommand("give @s minecraft:diamond_sword 1");
                entity.executeCommand("give @s minecraft:apple 64");
                entity.executeCommand("give @s minecraft:diamond_chestplate 1");
                break;
              }
            }

            // 增加计数器
            giveCount++;
          }

          // 返回响应消息
          return {
            message: `Gave the ${kit.result} kit to ${giveCount} player(s).`
          };
        }
      );
    },
    // 当没有重载匹配时的默认执行器函数
    () => {
      throw new Error("Invalid arguments, please specify a target and kit.");
    }
  );
}

总结

在本指南中,我们介绍了在Serenity中注册命令的基础知识。我们注册了一个简单的命令、添加了权限、创建了重载,并为参数使用了自定义枚举。你可以使用这些技术来为你的服务器创建强大而灵活的命令。

如果你对完整的代码片段感兴趣,可以在这里找到!