Yuzhe's Blog

yuzhes

用 RedScript 开发 Minecraft Datapack

如果你写过稍微复杂一点的 Minecraft Datapack,就会很快碰到原生 mcfunction 的天花板。它当然强大,但那种强大更像“你可以用积木拼出 CPU”,不是“它本身适合做软件工程”。

原生 mcfunction 最难写的点,不是命令多,而是抽象层级太低。变量要自己映射成 scoreboard,状态要自己选命名约定,条件分支靠 execute if/unless 链,循环通常得手搓计数器,跨文件复用时还要同时记住 namespace、调用路径和上下文实体。代码一旦过百行,维护成本会急剧上升。


原生 Datapack 到底难在哪里

举个很常见的需求:玩家进入副本后,显示标题、初始化状态、每秒检查一次附近敌人数量,数量清零后结算奖励。用原生命令写,这件事会拆成好几组 scoreboard objective、若干 tick 分发函数、一些 execute as @a[tag=...] at @s if entity ... 判断,还有手工维护的状态常量。

问题不在于“做不到”,而在于每次都得重新发明脚手架:

这些都属于语言或运行时本该帮你兜住的复杂度,但在原生 Datapack 里,它们全部直接暴露给作者。


RedScript 解决了什么

RedScript 的价值,不是把命令“翻译成另一种语法糖”,而是把一部分底层机制提升成语言特性。

第一,变量、函数和作用域变成显式概念。你可以写 let count = 0fn reward(p: Player) { ... },而不是手工决定这个值应该放在 $tmp3 还是 player_count 里。

第二,控制流恢复成普通程序的样子。ifmatchfor-each 都先以高级结构存在,再由编译器变成 scoreboard 判断和 execute 链。你写的是“根据状态分派逻辑”,而不是“拼一串条件命令”。

第三,Minecraft 特有的上下文被类型化了。玩家、实体选择器、触发器、@tick 这种概念在语言里是一等公民,编译器知道它们是什么,因此能帮你生成那些最枯燥、也最容易出错的样板代码。


一个快速上手示例

下面是一个很典型的 Datapack 逻辑:玩家加入时进入等待状态,倒计时结束后开战,每 tick 检查周围僵尸数量,为零时发奖励。

import "stdlib/state"
import "stdlib/dialog"
import "stdlib/scheduler"

const WAITING: int = 0
const COMBAT:  int = 1

@on(PlayerJoin)
fn on_join(p: Player) {
    set_state(p, WAITING)
    dialog_title(p, "§6副本开始", "§7准备中")
    task_schedule(p, 0, 100)
}

@tick
fn game_tick() {
    scheduler_tick()

    for player in @a[tag=instance_player] {
        match get_state(player) {
            Some(WAITING) => {
                if (task_ready(player, 0) == 1) {
                    set_state(player, COMBAT)
                    dialog_actionbar(player, "§c战斗开始")
                }
            }
            Some(COMBAT) => {
                if enemy_count(player) == 0 {
                    give(player, "minecraft:diamond", 3)
                }
            }
            _ => {}
        }
    }
}

这里真正重要的是可读性差异。你读到的是“加入游戏时设状态并启动计时器”“每 tick 遍历副本玩家”“按状态分派逻辑”,而不是几十行底层命令细节。编译器会负责把它们展开成 objective、计数器、函数分发和具体 .mcfunction 文件。


为什么这种抽象对 Datapack 特别重要

很多平台上的脚本语言只是“写起来舒服一点”;但在 Datapack 场景里,语言抽象直接决定项目是否能继续长大。因为 Minecraft 命令系统没有模块系统、没有真实局部变量、没有自然的数据结构,你只要开始做副本、技能、任务链、状态机,很快就会进入“功能能跑,但没人想再改”的阶段。

RedScript 做的事情,是把这些重复性结构收编进编译器:事件注册、触发器分发、状态存储、延迟任务、枚举匹配、实体迭代。这样你花精力的地方终于回到玩法逻辑,而不是命名和样板管理。


结语

如果你只想写几条简单命令,原生 mcfunction 完全够用;但只要项目开始出现状态、流程和复用,直接写命令文件的成本会迅速超过“引入一门小语言”的成本。RedScript 适合的正是这个区间:它不替代 Minecraft 的执行模型,而是把那套模型包装成更接近正常编程的开发体验。

对 Datapack 开发来说,这种提升不是锦上添花,而是从“能写”走向“能维护”的分水岭。