CodeBlock
  • 概述
  • 运行模型
  • 红石 I/O
  • 状态与等待
  • 消息与音效
  • 自定义音频
  • 文件与命名
  • 进阶 API
  • execute 命令
  • ← 回官网
接口文档(API)

代码方块把你写的 Java 程序当作一个「每 tick 跑一次的小芯片」。程序实现 CodeBlockProgram 接口的 onTick(CodeContext ctx, long tick), 模组在合适时机调用它,并传入 ctx——所有跟 MC 世界打交道的能力都挂在这个上下文对象上。

最小可运行程序

import codeblockmod.api.CodeBlockProgram;
import codeblockmod.api.CodeContext;

// 文件名必须与类名一致:CB_block1.java
public class CB_block1 implements CodeBlockProgram {
    public void onTick(CodeContext ctx, long tick) {
        int v = ctx.readInput(4);      // 读西面(4)的红石强度
        ctx.writeOutput(5, v);       // 原样写到东面(5)
    }
}
约束:文件名、类名、方块名字三者必须一致。方块名为 block1 时, Java 文件就是 CB_block1.java,类名必须是 CB_block1。 这是 Java「public 类名 = 文件名」的硬要求,不是模组的限制。
运行模型与线程安全

只有两种时机会跑程序

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

线程模型

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

看门狗

按设计,任何循环都永不自动终止。只有当程序连续 10 秒从未让出 CPU (循环体内缺少「等待」的纯空转)时,才会在日志里打一条 WARN 提示——不会中断线程、不会清输出。 自己用 sleepTicks() / sleep() 让出 CPU 即可规避占满单核。

红石 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
}
消息、音效与屏幕提示
跟玩家沟通
方法说明
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://codeblock.cc.cd/docs.html");
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(下次编译重建)。
改名界面已改为游戏内(右 Shift + 右键代码方块,弹出游戏内 Screen)。

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

  • 📄 用默认应用打开 Java 代码:用系统默认程序打开当前方块的 CB_<名字>.java。
  • 📂 打开代码文件夹:在文件管理器打开 res/program/。
进阶 API
代码 API 与编辑器积木完整参考

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

一、代码 API(Java 方法)

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

怎么创建名单?不用显式创建。直接用 arrayAdd("名单名", "值") 调用,模组发现这个"名单名"还不存在就自动建一个空数组。每个方块各自独立,跨 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)清空整张表。

红石输入/输出已实现

方法用途
int readInput(channel)读某一面接收到的红石强度(0-15)。channel:0=下 1=上 2=北 3=南 4=西 5=东。
int readInputMax()6 个面里最大的红石强度。
void writeOutput(channel, v)往某一面输出红石强度(自动 clamp 0-15)。
void writeOutputAll(v)6 个面同时输出相同强度。
boolean inputChanged(channel)某一面的红石值跟上一次比有没有变。按钮去抖用这个——按下瞬间变一次返回 true,按住不放持续返回 false。
boolean valueChanged(key, value)通用边沿检测:跟上次同 key 记录的值比,变了返回 true。不限红石,任意整数都行(比如变量、计算结果)。

状态 / 计时已实现

方法用途
int getStateInt(key)读一个跨 tick 持久化的整数(计数器/标志位),不存在返回 0。
void setState(key, v)存一个跨 tick 持久化的整数。
void sleepTicks(n)等 n 个游戏刻(1 刻=50ms)。while 循环里必须加这个,否则卡死游戏。
void sleep(ms)等指定毫秒数(真阻塞)。

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

方法用途
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)以玩家身份执行并取返回值。

记分板读写已实现

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

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

聊天 / 音效 / 音频已实现

方法用途
void sendMessage(text)聊天框发文字(方圆 64 格可见),支持 § 颜色代码。
void sendActionBar(text)物品栏上方显示短文字(约 3 秒消失)。
void sendTitle(text)屏幕中央大标题。
void sendClickable(text, url)可点击聊天消息(url 以 http 开头=开链接,否则=执行命令)。
void playSound(soundId)播放原版音效,如 block.note_block.harp、entity.player.levelup。
void playAudio(filename, volume)播放存档 res/media/ 目录下的自定义音频文件(mp3/wav/ogg)。
void stopAudio() / stopAudio(filename)停止所有/指定自定义音频。

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

积木按工具箱分类排列,每个积木的 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] 执行/判断命令 分类里拖,不用手敲命令字符串。带返回值的用「命令成立?」「取结果」积木。

← 回到 CodeBlock Mod 官网 · 由 zhangky 用 ❤ 与红石制作