mcpField
M
minecraft-server-scriptapi

minecraft-server-scriptapi

github.com/kaaariyaaa开发161 次安装39 周前更新
代码仓库

关于 minecraft-server-scriptapi

指导 AI Agent 使用 Minecraft Server ScriptAPI 的开发技能,面向编写事件、实体、组件和命令逻辑的开发者。它强调依据 Microsoft Learn 核实 API,并处理执行权限与时序限制。

主要功能

  1. 01按任务查阅官方 API 文档
  2. 02事件、维度与组件使用模式
  3. 03自定义命令和脚本事件指导
  4. 04执行权限与早期执行检查
  5. 05类型安全与性能注意事项

适用场景

  1. 01实现世界事件订阅
  2. 02编写自定义命令回调
  3. 03安排跨 tick 的耗时任务
  4. 04核对 API 的执行限制

MCP 相关

该技能指定 Microsoft Learn MCP 端点,并使用 microsoft_docs_search 搜索文档、使用 microsoft_docs_fetch 获取完整签名和详情。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/kaaariyaaa/scriptapiserverskill --skill minecraft-server-scriptapi

安装后在 Claude Code 中输入/minecraft-server-scriptapi确认已加载。

附带文件SKILL.ja.md

详细介绍

Minecraft Server ScriptAPI

工作流

  1. 范围:确定任务内容(事件、实体、组件、命令、时序)。除非请求使用 Beta/预览版,否则默认使用最新稳定版。
  2. 文档:从模块索引导航至具体的类/接口/事件。以 Microsoft Learn 为准。缺少详细信息时,通过 MCP 搜索。
  3. 输出:引用准确的 API 名称。提供包含所需导入项的最小可运行示例。创建自定义辅助函数前,先确认不存在官方等效方案。
    • 对于 CustomCommandParamType 之类的枚举,务必查阅 Microsoft Learn,确认其是否可用。
    • @minecraft/vanilla-data 不在 Microsoft Learn 上,因此跳过对此类枚举的 MCP 搜索。

常见模式

事件
  • 订阅/取消订阅 world/system 事件。添加防护逻辑以提升性能。
维度
  • 使用 world.getDimension(MinecraftDimensionTypes.<Dimension>),并根据任务选择合适的维度。
组件
  • 访问前先检查组件是否存在。
  • 使用类型化 ID:EntityComponentTypes、BlockComponentTypes、ItemComponentTypes。
调度
  • 使用 system.run、system.runTimeout、system.runInterval、system.runJob。
标识符
  • 必须使用 @minecraft/vanilla-data 枚举:MinecraftBlockTypes、MinecraftEntityTypes、MinecraftItemTypes、MinecraftDimensionTypes、MinecraftEffectTypes、potionEffect、potionDelivery、feature、enchantment、cooldownCategory、cameraPresets、biome。
  • 自定义 ID:必须包含命名空间前缀(例如,example:cmd)。每个附加包使用一个统一的前缀。

示例:

import { world, system } from "@minecraft/server";
import { MinecraftDimensionTypes, MinecraftBlockTypes } from "@minecraft/vanilla-data";

system.runInterval(() => {
  const dimensions = [MinecraftDimensionTypes.Overworld, MinecraftDimensionTypes.Nether];
  const blocks = [MinecraftBlockTypes.Stone, MinecraftBlockTypes.Sand, MinecraftBlockTypes.GrassBlock];
  for (const dimension of dimensions) {
    for (const block of blocks) {
      world.getDimension(dimension).setBlockType({ x: 0, y: 0, z: 0 }, block);
    }
  }
});

权限模式

只读
  • 在模拟/事件/刻开始之前。不得修改世界。
  • 解决方法:延后至 system.run/runTimeout。
文档验证
  • 使用任何方法/属性时,务必查阅 Microsoft Learn,确认其是否支持只读安全或早期执行。
  • 查找 API 参考中的明确说明(只读/早期执行),并严格遵守。
  • 仅对于箭头函数回调,还要检查是否有受限执行说明,例如:
    • “此闭包以受限执行权限调用。”
    • “此函数不能在受限执行模式下调用。”
  • 如果有适用的受限执行说明,请检查箭头函数主体,确保没有违反只读或早期执行要求;必要时使用 system.run 延后执行。

示例(延后只读操作):

world.beforeEvents.playerInteractWithBlock.subscribe((event) => {
  const player = event.player;
  system.run(() => {
    player.runCommand("say ok");
  });
});
早期执行
  • 在世界加载前。许多 API 不可用。
  • 解决方法:延后至 world.afterEvents.worldLoad 或 system.run。
  • 在根级别订阅,以免错过事件。

早期执行中安全的操作:

  • 事件订阅(world/system 的 beforeEvents/afterEvents)
  • system.clearJob、clearRun、run、runInterval、runJob、runTimeout、waitTicks
  • BlockComponentRegistry.registerCustomComponent、ItemComponentRegistry.registerCustomComponent

自定义命令

  • 接口:CustomCommand(name、description、permissionLevel、mandatoryParameters、optionalParameters)。
  • 参数:CustomCommandParameter(name、type、可选的 enumName)。
  • 参数类型和箭头函数参数类型:
    • String -> String
    • PlayerSelector -> Player
    • Location -> Vector3
    • ItemType -> ItemType
    • Integer -> Number
    • Float -> Number
    • Enum -> String
    • EntityType -> EntityType
    • EntitySelector -> Entity
    • Boolean -> Bool
    • BlockType -> BlockType
  • 注册枚举:CustomCommandRegistry.registerEnum(name, values)。
  • 注册命令:CustomCommandRegistry.registerCommand(customCommand, callback)。
  • 回调:(origin, ...args) => CustomCommandResult。
  • 自定义命令回调以受限执行权限运行,因此不要直接调用受只读限制的方法;如有需要,使用 system.run 延后执行。
  • 使用箭头函数时,参数名称和顺序应与 CustomCommandParameter.name 列表一致;定义了参数时,避免使用不匹配的名称或通用的 args。

示例:

import {
  system,
  StartupEvent,
  CommandPermissionLevel,
  CustomCommandParamType,
  CustomCommandStatus,
} from "@minecraft/server";

system.beforeEvents.startup.subscribe((init: StartupEvent) => {
  init.customCommandRegistry.registerEnum("example:mode", ["on", "off"]);

  init.customCommandRegistry.registerCommand(
    {
      name: "example:demo",
      description: "Command demo",
      permissionLevel: CommandPermissionLevel.GameDirectors,
      cheatsRequired: true,
      mandatoryParameters: [
        {  type: CustomCommandParamType.String, name: "msg", },
        {  type: CustomCommandParamType.Enum, name: "example:mode"},
      ],
      optionalParameters: [
        { type: CustomCommandParamType.Boolean, name: "silent" },
        { type: CustomCommandParamType.Integer, name: "count" },
      ],
    },
    (origin, msg, mode, silent, count) => {
      const msgValue = String(msg ?? "ok");
      const modeValue = String(mode ?? "off");
      const silentValue = Boolean(silent ?? false);
      const countValue = Number(count ?? 1);

      return {
        status: CustomCommandStatus.Success,
        message: silentValue
          ? undefined
          : `[${origin.sourceType}] ${msgValue} mode=${modeValue} count=${countValue}`,
      };
    }
  );
});

脚本事件

  • 发送:system.sendScriptEvent(id, message)(带命名空间的 ID,载荷字符串)。
  • 接收:system.afterEvents.scriptEventReceive.subscribe(callback, options?)。
  • 事件:ScriptEventCommandMessageAfterEvent(id、message、sourceType、可选的 sourceEntity/sourceBlock/initiator)。
  • 筛选器:ScriptEventMessageFilterOptions.namespaces。

示例:

import { system, world, ScriptEventSource } from "@minecraft/server";

system.afterEvents.scriptEventReceive.subscribe((event) => {
  const { id, message, sourceType, initiator, sourceEntity, sourceBlock } = event;
  if (id !== "example:say") return;

  switch (sourceType) {
    case ScriptEventSource.Block:
      world.sendMessage(`sendBy:${sourceBlock?.typeId ?? "unknown"} ${message}`);
      break;
    case ScriptEventSource.Entity:
      world.sendMessage(`sendBy:${sourceEntity?.typeId ?? "unknown"} ${message}`);
      break;
    case ScriptEventSource.NPCDialogue:
      world.sendMessage(`sendBy:${initiator?.typeId ?? "unknown"} ${message}`);
      break;
    case ScriptEventSource.Server:
      world.sendMessage(`sendBy:server ${message}`);
      break;
  }
});

性能

  • 事件中的高开销工作:使用 system.runJob(generator) 将任务分散到多个刻中执行。
  • 遍历大型集合前,先进行短路判断。

system.run 变体

  • run:下一刻执行。
  • runTimeout(cb, ticks):延迟 N 个刻。误用 0 可能导致紧密循环。
  • runInterval(cb, ticks):每隔 N 个刻重复执行,直到调用 clearRun。
  • runJob(generator):执行长时间运行的任务。每次迭代应保持精简。

类型安全

  • 使用 typeId 检查,并验证组件是否存在。
  • 仅对文档中列出的 @minecraft/server 类使用 instanceof。

最简模板

事件订阅:

world.afterEvents.playerJoin.subscribe((event) => {
  const player = event.player;
});

刻循环:

system.runInterval(() => {
  // tick logic
}, 1);

获取维度:

const overworld = world.getDimension(MinecraftDimensionTypes.Overworld);

生成实体:

const overworld = world.getDimension(MinecraftDimensionTypes.Overworld);
overworld.spawnEntity(MinecraftEntityTypes.Zombie, { x: 0, y: 80, z: 0 });

给予物品:

const item = new ItemStack(MinecraftItemTypes.Diamond, 1);
player.getComponent("inventory")?.container?.addItem(item);

应用效果:

player.addEffect(MinecraftEffectTypes.Speed, 200, { amplifier: 1 });

获取组件(类型化):

const health = entity.getComponent(EntityComponentTypes.Health);

易错点

  • 空组件:访问前先检查是否存在。
  • 高开销事件:使用 system.runJob。
  • 权限:避免违反只读限制或提前执行限制。
  • 原始 ID:使用类型化枚举。
  • 命名空间:每个附加包使用一个统一前缀。

MCP 工具

评论

支持 Markdown