Minecraft · Fabric Mod

在游戏里写 Java,让红石跑起来。

代码方块是一个可编程的红石方块。每个方块绑定一段程序,每游戏刻执行一次, 从 6 个面读取红石信号,再把结果写回去。可以直接写 Java,也可以用轻量的 DSL。

26.2 当前版本 20 次/秒 每游戏刻执行 6 面 红石输入输出 Java / DSL 两种写法
codeblock / passthrough.dsl
# 信号透传:北面(通道2)输入 → 顶面(通道1)输出
cout(1, cin(2));

# 通道 0=下 1=上 2=北 3=南 4=西 5=东
# 强度自动限制在 0–15(十六进制 0–F)

方块怎么工作

不离开游戏,改完就能看到结果。

每游戏刻执行

程序每个 tick 跑一次,20 次/秒。读取相邻方块朝它发出的信号,把结果从任意面输出。

在游戏里写 Java

直接写完整的 Java 类,游戏内编译后运行;需要更多编译能力时可以配合 ECJ 模组。

定时热重载

改完代码不用退出世界,方块会按设定的间隔重新加载程序,调试红石时序很方便。

命令与记分板

可以执行命令、读写记分板、发送可点击消息,把红石逻辑接到服务器玩法里。

多窗口

主画面之外可以再开一个窗口,投放副画面或实时内容,不抢主画面焦点。

下载

按游戏版本选择。下载包为官网直链,点开即可下载。

CodeBlock 26.2

当前维护版本,包含最新的多窗口与编辑器功能。

推荐
下载 26.2

CodeBlock 1.21.11 已停止维护

旧版本,仍可下载回退。后续新功能只做 26.2。

下载 1.21.11

ECJ 编译模组 已停止维护

给用 JRE 启动游戏的玩家补充 Java 编译能力,可装可不装。

下载 ECJ

Gitee 镜像

国内网络更稳,历史版本也保留在这里。

镜像
打开 Gitee

源码

源码与发布记录都在 Gitee。

打开 Gitee 仓库

接口文档

红石 I/O、状态与等待、消息音效、文件命名、多窗口与 execute 用法。

多窗口

把主画面 / HUD / F3 复刻到独立窗口

除了游戏主窗口,还能再开若干独立窗口,把主画面、HUD 特写或 F3 调试文字复刻进去,用来做分屏监视。窗口 API 挂在 CodeContext 上,只能写在 .java 程序里(.codescript 没有对应语法)。仅 26.2 线(v0.1.1 起)提供;1.21.11 线已停更,没有这些方法。

两套窗口

类型说明
内置单窗口固定 400×300 的副窗口,按预设或贴图集显示内容,用 renderSecondWindow() / setSecondWindowSource(String) 控制。
可配置多窗口任意多个独立窗口,可设尺寸、坐标、背景色、标题与文字,支持拖动缩放、Alt+Enter 全屏、实时镜像 / HUD 特写 / 快照。所有方法第一个参数都是窗口 id。

内置单窗口 API

方法说明
void renderSecondWindow()请求重画一次内置窗口内容(改了显示源或贴图后调用)。
void setSecondWindowSource(String source)设置内置窗口显示源,取值见下表。传 null 回到默认图集。
void setSecondWindowPresetEnabled(boolean on)按当前界面自动切换预设画面;开启时会清掉手动设置的内置窗口显示源。
void setLipColor(int r, int g, int b, int a)给内置窗口画面叠一层颜色(0–255)。
void clearLipColor()取消颜色叠加。
source说明
"blocks"(默认)石头 / 泥土 / 橡木板 / 铁块 / 玻璃 / 苔石 6 种方块贴图组成的图集。
"ui"原版 GUI 贴图图集(物品栏、熔炉、成就背景等)。
"screenshot"实时截取主画面(按固定间隔抓帧)。

预设模式按当前界面自动切换:物品栏(inv)、暂停菜单(pause)、死亡界面(death)、标题界面(title)、游戏内无界面(world)。

可配置多窗口 API

方法说明
String createSecondWindow(String id, int w, int h, int x, int y, boolean closable, boolean fullscreenCap, boolean resizable, boolean focusable)创建窗口,返回 id;id 已存在返回原 id,参数非法返回空字符串。宽高最小 32px,x/y 是屏幕左上角坐标。closable=允许点 × 关,fullscreenCap=允许 Alt+Enter 全屏,resizable=允许拖拽缩放,focusable=创建后抢焦点(false 则后台创建,MC 不掉焦点)。
boolean hasSecondWindow(String id)窗口是否已经创建完成。创建是异步的,刚调 createSecondWindow 的同一刻可能还是 false。
void resizeSecondWindow(String id, int w, int h)改尺寸(最小 32×32)。
void moveSecondWindow(String id, int x, int y)移到屏幕坐标,比关掉重建轻得多。
void toggleSecondWindowFullscreen(String id)全屏 / 窗口化切换,仅 fullscreenCap=true 时生效。
void setSecondWindowColor(String id, float r, float g, float b, float a)清屏背景色,每个分量 0.0–1.0。
void setSecondWindowTitle(String id, String title)标题栏文字,自动去掉 § 颜色码;内容相同不会重复调用。
void setSecondWindowText(String id, String text)窗口内显示文字,支持 § 颜色码,超出宽度自动换行。
void setSecondWindowTextEx(String id, String text, int fontSize, int anchorX, int anchorY, int baseColor, int posX, int posY)完整版:文字 + 字号 + 九宫格位置 + 基础色 + 像素坐标,参数含义见下表。
void setSecondWindowTextPos(String id, int posX, int posY)只改文字像素坐标,-1 表示回到九宫格定位。
void setSecondWindowSource(String id, String source)设置显示源,默认每 10 帧抓一次。
void setSecondWindowSource(String id, String source, int interval)同上,interval 为每多少帧抓一次(1=每帧最流畅最吃资源,越大越省)。
void clearSecondWindow(String id)清掉文字与画面,恢复纯背景色;窗口本身不关闭。
void closeSecondWindow(String id)关闭窗口。脚本主动关闭总是允许,不受 closable 限制。

显示源(可配置窗口)

source说明
""(默认)纯背景色 + 文字。
"screen"实时镜像主窗口画面,3D 世界与任意界面(原版 / 模组 GUI)都能复刻;按 interval 节流抓帧。
"snapshot"抓一帧后定格(内部自动切回 "",画面保留);要停止显示用 clearSecondWindow。
"hud"HUD 特写:只保留快捷栏、血量、饥饿、护甲、经验条(元素级白名单),黑底居中按比例显示。

文字参数(setSecondWindowTextEx)

参数说明
fontSize像素字高,最小 8;传 -2 为自动字号(窗口高 / 12,窗口越大字越大)。
anchorX0=左 1=中 2=右。
anchorY0=上 1=中 2=下。
baseColor基础色,格式 0xAARRGGBB;文字里写 § 颜色码会覆盖它。
posX / posY像素坐标定位;-1 表示跟随九宫格锚点。

渲染抑制与 F3

方法说明
void setSuppressMainRender(boolean on) / boolean isSuppressMainRender()把主窗口画面替换成纯黑(游戏照常运行),用于只想看小窗的场合;开启期间指向主画面的镜像窗也会一起变黑。
void setSuppressWindow(String id, boolean on) / boolean isWindowSuppressed(String id)让指定窗口只画背景色、跳过内容渲染(变黑),窗口不关闭。
String getF3Text()取当前 F3 调试文本:每行用 \n 分隔,左右两栏用 | 连接。没有 F3 内容时是空串。
boolean hasF3Content()当前有没有可用的 F3 文本(F3 未打开时为 false)。
void setF3MainSuppressed(boolean on)主窗口不显示 F3 覆盖层,只在小窗里看。

例:实时镜像小窗 + HUD 特写小窗 + F3 文字小窗

public void onTick(CodeContext ctx, long tick) {
    // 主画面镜像:640×480,可关 / 可全屏 / 可缩放,后台创建不抢焦点
    if (!ctx.hasSecondWindow("mirror")) {
        ctx.createSecondWindow("mirror", 640, 480, 40, 40,
                true, true, true, false);
        ctx.setSecondWindowTitle("mirror", "主画面镜像");
        ctx.setSecondWindowColor("mirror", 0.1f, 0.12f, 0.16f, 1f);
        ctx.setSecondWindowSource("mirror", "screen", 10);   // 每 10 帧抓一帧
    }

    // HUD 特写:只显示快捷栏 / 血量 / 饥饿 / 护甲 / 经验条
    if (!ctx.hasSecondWindow("hud")) {
        ctx.createSecondWindow("hud", 320, 180, 700, 40,
                false, true, false, false);
        ctx.setSecondWindowSource("hud", "hud", 4);
        ctx.setSecondWindowTextEx("hud", "§bCodeBlock", -2, 1, 2, 0xFFFFFFFF, -1, -1);
    }

    // 每 5 秒把 F3 文本刷进第三个窗口
    if (tick % 100 == 0) {
        if (!ctx.hasSecondWindow("f3")) {
            ctx.createSecondWindow("f3", 420, 300, 40, 540,
                    true, false, true, false);
        }
        if (ctx.hasF3Content()) {
            ctx.setSecondWindowText("f3", ctx.getF3Text());
        }
    }
    ctx.sleepTicks(20);   // 别空转占核
}
跨线程安全:这些窗口 API 都是登记式调用——脚本在后台线程只写参数和标志,真正的窗口创建、移动、抓帧、关闭由主渲染线程执行,所以直接在 onTick 里调即可。不要自己开线程或用 LWJGL 直接碰 GL 上下文。
窗口不抢键鼠:focusable=false 创建的窗口只做监视,不影响操作。窗口尺寸、位置改了之后镜像纹理会按新尺寸自动重建;拖动窗口期间抓屏会自动暂停。

概述

引入头文件(必须放在代码最开头)

写任何代码方块程序,第一行必须先引入下面两个库,否则编译器找不到接口、直接报错。

import codeblockmod.api.CodeBlockProgram;
import codeblockmod.api.CodeContext;
库作用
codeblockmod.api.CodeBlockProgram程序入口接口。你写的类必须 implements CodeBlockProgram,并实现它的 onTick(CodeContext ctx, long tick) 方法——这是代码方块运行你程序的唯一入口。
codeblockmod.api.CodeContext上下文接口。onTick 拿到的 ctx 就是它的实例,所有 API(红石读写、状态存储、消息、音效、命令、数组等)都挂在 ctx 上调用。
注意:两个 import 缺一不可,且必须写在类定义之前。其余 Minecraft 原生类(如 BlockPos、Level 等)只有在你手动用到时才需要额外 import,积木生成的代码不会用到。

运行模型与线程安全

只有两种时机会跑程序

  • ① 手动运行:首次载入方块、准星对着方块按 O、右键方块重载。
  • ② 检测到更改运行:程序文件 mtime 变化(每 20 tick 扫一次),或邻居红石变化(去抖 4 tick 后跑一次)。

线程模型

你的 onTick 跑在后台守护线程里,不阻塞服务端主线程。 ctx 里凡是触碰世界的操作(读红石、写输出、发消息、放音)都会自动经 runOnMain() 调度回服务端主线程执行,所以你不需要自己加锁或调度, 直接在 onTick 里调用即可。

红石 I/O(cin / cout)

通道编号(= 方块六个面)

通道方向说明
0下 DOWN方块底面
1上 UP方块顶面
2北 NORTH—Z 方向
3南 SOUTH+Z 方向
4西 WEST—X 方向
5东 EAST+X 方向

接口一览

方法说明
int readInput(int channel)读某面外部输入的红石强度(0–15)。已排除本方块自身在该面发出的信号,避免自激。
void writeOutput(int channel, int value)向某面输出强度,自动 clamp 到 0–15。
int readInputMax()返回 6 个面中最大的输入强度。
void writeOutputAll(int value)6 个面同时输出相同强度(自动 clamp)。
String debugInputs()返回 6 个方向当前输入的可读字符串。接线不确定时用它确认拉杆/压力板到底接在哪一面。已实现
接线铁律:输入端与输出端物理分离。 例如「读西面、写东面」。若把输出和读取接到同一段红石粉,自己的信号会反馈回来被自己读到(物理自激), 表现就是 0/1 反复跳。readInput 只排除本方块自身发射量,排除不了同一段红石线的反射。

例:阈值比较器

public void onTick(CodeContext ctx, long tick) {
    int v = ctx.readInput(4);              // 读西面
    ctx.writeOutput(5, v >= 8 ? 15 : 0);   // 东面:够强就满输出
}

跨 tick 状态与等待

计数器、计时器、延时

方法说明
int getStateInt(String key)读取跨 tick 持久化的整数状态(计数器/计时器/标志位)。key 不存在返回 0。
void setState(String key, int value)写入整数状态。每个代码方块有自己独立的状态表。
void sleepTicks(long n)真阻塞 n 个游戏 tick(1 tick ≈ 50ms),期间让出 CPU。用于延时/降频。
void sleep(long millis)真阻塞指定毫秒。在 while(true) 里用它让出 CPU 降频。

例:每 20 tick 计一次数

public void onTick(CodeContext ctx, long tick) {
    int n = ctx.getStateInt("count");
    ctx.setState("count", n + 1);
    ctx.writeOutput(5, n % 16);   // 0–F 循环输出
    ctx.sleepTicks(20);              // 约 1 秒一次,同时让出 CPU
}
死循环务必带等待。while(true) 里如果没有 sleepTicks() 或 sleep(),会纯空转占满一个 CPU 核心(看门狗只会打日志,不会帮你停)。

消息、音效与屏幕提示

跟玩家沟通

方法说明
void sendMessage(String text)聊天框发一条消息,方圆 64 格内玩家可见。支持 § 颜色码。同文本 1 秒内去重。
void sendClickable(String text, String url)可点击消息(悬停下划线)。以 http 开头=打开链接,否则当作 MC 命令执行。3 秒去重。
void playSound(String soundId)播放原版音效,方圆 16 格可听到。例:block.note_block.harp、entity.player.levelup。ID 无效时回退竖琴音。
void sendActionBar(String text)物品栏上方短暂文字(Action Bar),约 3 秒消失。
void sendTitle(String text)屏幕中央大标题。

例:可点击链接

ctx.sendClickable("§b点我打开文档", "https://jky.ccwu.cc/codeblock.html#docs");
ctx.sendClickable("§a点我回到出生点", "/spawn");

自定义音频

播放你自己的 mp3 / wav / ogg

音频文件放在存档目录的 res/media/ 下,用文件名直接播放(流式 SourceDataLine,mp3 已内置解码)。

方法说明
void playAudio(String filename, float volume)播放 res/media/ 下的音频。调一次播一次,次数完全由你的代码决定。音量 >1 视为百分比(100=满音量),否则按 0.0–1.0。
void stopAudio()停止该方块正在播放的所有自定义音频。
void stopAudio(String filename)停止指定文件名的音频。
ctx.playAudio("ding.mp3", 80);   // 80% 音量
// ...
ctx.stopAudio("ding.mp3");        // 只停这一首
并发上限为每方块 8 条音频线路(防止资源耗尽),超出的播放请求会被丢弃。文件不存在时会回退一声竖琴提示音。

文件、目录与命名

路径 / 文件用途
<存档>/res/program/程序目录(随存档走)
CB_<名字>.javaJava 源码(游戏内 javac 实时编译;JRE 用户需装 ECJ 附加模组)
CB_<名字>.class编译产物。存在且不旧于源码时直接加载、跳过编译(编译缓存)
CB_<名字>.codescript零依赖 DSL 脚本(记事本即可写,无需编译器)
CB_<名字>.blocksBlockly 图形积木 XML,供图形编辑器还原
<存档>/res/media/音频文件(playAudio 从这里取)

命名规则已实现

  • 方块名 block1 → 代码文件固定为 CB_block1.java,名字与代码绑定。
  • 名字只允许字母 / 数字 / 下划线 / 中文;禁止为空、禁止与别的方块重名。
  • 改名会同步重命名整套 CB_<旧名>.* 文件,并自动改写 .java 里的类名、删除陈旧 .class(下次编译重建)。

编辑器里的快捷操作已实现

  • 📄 用默认应用打开 Java 代码:用系统默认程序打开当前方块的 CB_<名字>.java。
  • 📂 打开代码文件夹:在文件管理器打开 res/program/。

进阶 API

代码 API 与编辑器积木完整参考

本页分两部分:① 代码 API(直接写 Java 用的方法清单)、② 编辑器积木(图形化拖拽对应的积木清单)。两者一一对应。

一、代码 API(Java 方法)

字符串数组(一维)已实现

名单不用显式创建:arrayAdd("名单名", "值") 发现 key 不存在就自动建空数组,跨 tick 持久化,key 支持中文。

方法用途
void arrayAdd(key, v)往名单末尾加一个值,允许重复。
void arrayAddOnce(key, v)只在名单里没有这个值时才加(去重),最常用于"白名单/已领奖名单"。
void arrayRemove(key, v)从名单里删掉第一个匹配的值。
void arrayClear(key)清空整个名单。
int arraySize(key)名单里有几个元素。
String arrayGet(key, i)取第 i 个元素(从 0 开始数),越界返回空字符串。
int arrayFind(key, v)找这个值在第几位(0 起),找不到返回 -1。判断"在不在名单里"就用 arrayFind(...) >= 0。
String arrayJoin(key, sep)把名单所有元素用分隔符拼成一个字符串,方便 sendMessage 打印。
void setString(key, v) / String getString(key)存/取单个字符串状态(不是数组,就是一个 key→一个字符串)。
替代记分板:判断"某玩家是否属于某组"(白名单/黑名单/已领奖),不要用记分板,直接 arrayFind("vip", playerName) >= 0 一行搞定,无需建 objective。

二维字符串数组(表格)已实现

行 × 列的表格,存成绩表 / 配置矩阵。同样用 key 自动创建。编辑器里「创建二维数组」积木直接填行数和列数,下面自动长方格。

方法用途
void array2DSet(key, r, c, v)写第 r 行第 c 列(都从 0 起),行/列不够自动补空行。
String array2DGet(key, r, c)读第 r 行第 c 列,越界返回空字符串。
int array2DRows(key) / int array2DCols(key)表格有几行 / 几列。
int array2DFind(key, v)全表找这个值在第几行,找不到 -1。
String array2DRowJoin(key, r, sep)把某一行用分隔符拼成字符串。
void array2DClear(key)清空整张表。

边沿检测已实现

方法用途
boolean inputChanged(channel)某一面的红石值跟上一次比有没有变。按钮去抖用这个——按下瞬间变一次返回 true,按住不放持续返回 false。
boolean valueChanged(key, value)通用边沿检测:跟上次同 key 记录的值比,变了返回 true。不限红石,任意整数都行(比如变量、计算结果)。

精确识别"是谁按的按钮"已实现

监听玩家右键交互,不靠"谁离得近"猜——多人围在一起时,只有真正按下去的人会被记到。

方法用途
String whoUsed(dx, dy, dz)谁最后按过相对本方块 (dx,dy,dz) 处的方块。按钮在西面一格 → whoUsed(-1,0,0)。默认只认 1 秒内的操作。
String whoUsed(dx, dy, dz, withinTicks)同上,自定义时间窗(tick 数);0 或负数=不限时间。
String whoUsedNearby(radius)不想算坐标时用:半径内最近交互过的人。
String nearestPlayer() / nearestPlayer(r)兜底:最近的玩家名。
int playerCount()当前在线玩家数。

命令执行(最高权限)已实现

所有命令都在最高权限下跑——服务器关了命令、玩家不是 OP 也能执行。位置默认在方块中心。

方法用途
void executeCommand(cmd)执行一条命令。/execute 的各种子命令(if/as/at/store 等)直接写在 cmd 字符串里即可,无需专门 API。
void executeCommandAs(player, cmd)以指定玩家身份执行,命令里的 @s 指向他,仍最高权限。
int executeCommandResult(cmd)执行并拿到返回值:0=失败/条件不成立,1=成功,其他=命令本身的返回值(如 /list 人数、data get 取到的数)。
boolean executeCommandTest(cmd)等价于 executeCommandResult(cmd) > 0,专门给 /execute if 条件判断用,放进 if 更顺手。
int executeCommandResultAs(player, cmd)以玩家身份执行并取返回值。
/execute if 不成立 ≠ 返回 0。MC 底层条件不成立会抛异常,模组已统一捕获并归一为 0,你直接判断返回值就行,不用 try-catch。

记分板读写已实现

需要存数值(如金币数、击杀数)时用。目标不存在时自动创建 dummy 目标,开箱即用。纯判断"在不在组里"用字符串数组更简单,不必用记分板。

方法用途
int getScore(objective, player)读某玩家在某记分板目标上的分数,查不到返回 0。
void setScore(objective, player, v)直接设分数。
void addScore(objective, player, delta)加/减分数(负数即减)。

二、编辑器积木(图形化)

积木按工具箱分类排列,每个积木的 Java 生成代码在括号里标注。

[I/O] 输入/输出

积木用途
读取某面强度ctx.readInput(面),0-15。
读取所有面最大值ctx.readInputMax()。
输入变化?ctx.inputChanged(面),边沿检测,按钮去抖。
值变化? 键 □ 当前值 □ctx.valueChanged(key, value),通用边沿检测(任意整数)。
输出到 □ 强度 □ctx.writeOutput(面, 值)。
向所有面输出 □ctx.writeOutputAll(值)。
调试: 打印所有输入聊天框打印 6 个面的红石强度,排查接线用。

[Exec] 执行/判断命令

积木用途
执行命令 □ctx.executeCommand(cmd),原始执行任意命令。
以玩家 □ 身份执行命令 □ctx.executeCommandAs(玩家, cmd),@s 指向该玩家。
命令成立? □ctx.executeCommandTest(cmd),execute 条件判断,返回真/假。
执行命令 □ 取结果ctx.executeCommandResult(cmd),拿返回值(数字)。
以玩家 □ 执行命令 □ 取结果ctx.executeCommandResultAs(玩家, cmd)。
execute 若方块 相对 x y z 是 □ 则运行 □execute if block ~x ~y ~z 方块 run cmd。
execute 若存在实体 □ 则运行 □execute if entity 选择器 run cmd。
execute 若不存在实体 □ 则运行 □execute unless entity 选择器 run cmd。
execute 若实体 □ 槽位 □ 含物品 □ 则运行 □execute if items entity 选择器 槽位 物品 run cmd。
execute 若谓词 □ 成立 则运行 □execute if predicate 谓词 run cmd。
execute 以 □ 身份 在其位置运行 □execute as 选择器 at @s run cmd,as+at 合体。
execute 以 □ 身份运行 □execute as 选择器 run cmd,只改执行者。
execute 在 □ 位置运行 □execute at 选择器 run cmd,只改位置。
execute 在坐标 x y z 运行 □execute positioned x y z run cmd。
execute 存结果到 玩家 □ 记分板 □ 运行 □execute store result score 玩家 目标 run cmd。
execute 若 玩家 □ 记分板 □ 比较符 □ 值 □ 则运行 □execute if score 玩家 目标 比较符 值 run cmd,记分板条件。
execute 若玩家 □ 在名单 □ 里 则运行 □字符串数组做白名单判断(替代记分板)。
execute 若名单 □ 包含 □ 则运行 □通用版,值不限玩家名。
execute 若名单 □ 不包含 □ 则运行 □反向判断(黑名单)。
对名单 □ 中每个元素存入变量 □ 执行 □遍历名单逐个处理,替代 @a[scores=...] 批量执行。
名单 □ 包含 □ ?ctx.arrayFind(key,值) >= 0,返回真/假,直接接 if。

[Array1] 字符串数组

积木用途
往名单 □ 追加 □ctx.arrayAdd(key, 值),允许重复。
往名单 □ 追加(去重)□ctx.arrayAddOnce(key, 值),已存在就不加。
从名单 □ 删除 □ctx.arrayRemove(key, 值)。
清空名单 □ctx.arrayClear(key)。
名单 □ 长度ctx.arraySize(key),元素个数。
名单 □ 取第 □ 个ctx.arrayGet(key, 下标),0 起。
名单 □ 查找 □ctx.arrayFind(key, 值),返回位置,找不到 -1。
名单 □ 用 □ 拼接ctx.arrayJoin(key, 分隔符)。
设置字符串 □ = □ctx.setString(key, 值),存单个字符串。
读取字符串 □ctx.getString(key)。

[State] 状态/计时 · [Player] 玩家 · [CMD] 命令 · [Chat] 聊天 · [Audio] 音频

积木用途
[State] 状态/计时
读状态 □ctx.getStateInt(key)。
写状态 □ = □ctx.setState(key, 值)。
等待 □ 刻 / 秒 / 毫秒ctx.sleepTicks(n) / sleep(ms)。while 循环必须加。
当前 tick 数程序启动后经过的游戏刻数。
[Player] 玩家
谁按过 相对 x y z 的方块ctx.whoUsed(dx,dy,dz),精确识别。
谁按过 半径 □ 内的方块ctx.whoUsedNearby(r)。
最近玩家 / 最近玩家(半径)ctx.nearestPlayer()。
在线玩家数ctx.playerCount()。
[CMD] 普通命令积木(直接生成命令字符串)
给予 / 传送 / 说 / 召唤 / 效果 / 放方块 / 填充 / 击杀 / 游戏模式 / 时间 / 天气 / 标题图形化填参数 → 自动拼命令 → ctx.executeCommand(...)。
原始命令 □直接写完整命令文本。
读分数 / 设分数 / 改分数ctx.getScore/setScore/addScore。
[Chat] 聊天
发消息 / 可点击消息 / 音效 / ActionBar / Title对应 sendMessage / sendClickable / playSound / sendActionBar / sendTitle。
[Audio] 自定义音频
播放音频 □ 音量 □ctx.playAudio(文件名, 音量),文件放存档 res/media/。
停止所有音频 / 停止 □ctx.stopAudio() / stopAudio(文件名)。

例:按钮按了 → 记人名进名单 → 判断 → 执行命令

// 西面按钮按下时
if (ctx.inputChanged(4) && ctx.readInput(4) > 0) {
    String p = ctx.whoUsed(-1, 0, 0);          // 是谁按的
    if (!p.isEmpty()) {
        if (ctx.arrayFind("vip", p) >= 0) {              // 在 VIP 名单里?
            ctx.executeCommand("give " + p + " diamond 1");  // 发钻石
        } else {
            ctx.arrayAddOnce("vip", p);                   // 不在就加入名单
        }
    }
}
典型链路:inputChanged 边沿检测 → whoUsed 精确识别 → arrayFind 名单判断 → executeCommand 执行命令。全程无需记分板。

execute 命令详解

/execute 子命令语法、用途与示例

/execute 是 MC 最强大的命令——改执行者、改位置、加条件、存结果,一条命令全搞定。代码方块里所有 execute 积木最终都是拼出 execute ... run ... 字符串交给 ctx.executeCommand() 执行。

基本结构

execute [修饰子命令] [条件子命令] [store子命令] run <要执行的命令>

子命令从左到右依次生效,run 只能放最后。除 run 外,其他子命令可多个串联、可重复。

一、修饰子命令(改「在哪跑、以谁的身份跑」)

子命令用途 + 示例
as <选择器> 改执行者(命令里的 @s 指向它),不改位置。
例:execute as @a run say 你好 → 每个玩家都说"你好"。
at <选择器> 改执行位置/朝向/维度,不改执行者。
例:execute at @p run setblock ~ ~ ~ stone → 在最近玩家脚下放石头。
as <选择器> at @s 执行者+位置一起改(最常用)。
例:execute as @a at @s run particle flame ~ ~1 ~ → 每个玩家头上冒火焰粒子。
positioned <x> <y> <z> 指定绝对/相对坐标。~ = 相对当前位置,^ = 相对朝向。
例:execute positioned ~ ~1 ~ run setblock ~ ~ ~ diamond_block → 头顶放钻石块。
in <维度> 切换执行维度:minecraft:overworld / the_nether / the_end。

二、条件子命令(if 成立 / unless 不成立才继续)

子命令用途 + 示例
if/unless entity <选择器> 实体存在/不存在。
例:execute if entity @e[type=zombie,r=10] run say 附近有僵尸!
if/unless block <x y z> <方块> 某位置是不是指定方块。
例:execute if block ~ ~-1 ~ minecraft:gold_block run say 脚下是金块!
if/unless blocks <x1 y1 z1> <x2 y2 z2> <x y z> all|masked 两个区域方块是否一致。all=全比,masked=只比非空气。
if/unless items entity <选择器> <槽位> <物品> 实体某槽位是否有某物品。槽位如 weapon.mainhand、armor.head、hotbar.0。
例:execute if items entity @s weapon.mainhand minecraft:diamond run say 手持钻石!
if/unless predicate <谓词> 数据包谓词条件(高级,用于复杂判定)。
if/unless score <目标> <记分项> <操作符> <值> 记分板分数比较。操作符:= > >= < <=,或 matches 范围。
例:execute if score @s money matches 100.. run say 我有100块以上!

三、存储子命令(把命令结果存下来)

子命令用途 + 示例
store result score <玩家> <记分项> run <命令> 把 run 后命令的返回值存进记分板。
例:execute store result score @s online run list → 把在线人数存进 @s 的 online 分数。
store success score <玩家> <记分项> 存命令是否成功(0/1)。
返回值语义:0 = 失败/条件不成立,1 = 成功,其他 = 命令本身返回的数。 在代码方块里可以用 ctx.executeCommandResult("execute ... run ...") 直接拿到这个返回值,不用 store 到记分板。

四、常用组合示例

// 1. 让所有玩家传送到代码方块位置
ctx.executeCommand("execute as @a at @s run tp ~ ~ ~");

// 2. 头顶是钻石块就给最近玩家发钻石
if (ctx.executeCommandTest("execute if block ~ ~1 ~ minecraft:diamond_block")) {
    ctx.executeCommand("give @p diamond 1");
}

// 3. 统计半径 5 格内的僵尸数量,存进记分板
int zombieCount = ctx.executeCommandResult(
    "execute if entity @e[type=zombie,distance=..5]");
ctx.sendMessage("附近僵尸数: " + zombieCount);

// 4. 对 VIP 名单里的每个玩家发奖励(用字符串数组,不用记分板)
for (int i = 0; i < ctx.arraySize("vip"); i++) {
    String p = ctx.arrayGet("vip", i);
    ctx.executeCommand("give " + p + " diamond 1");
}

// 5. 手持钻石剑才执行后续命令
ctx.executeCommand("execute if items entity @s weapon.mainhand minecraft:diamond_sword run effect give @s strength 10 1");
在编辑器里:以上都有对应积木,直接在 [Exec] 执行/判断命令 分类里拖,不用手敲命令字符串。带返回值的用「命令成立?」「取结果」积木。