
沉云工坊API
CloudWorks API 是一个开源的私有API,包含一些实用的工具。
CloudWorks API
统一模组开发接口 — 为 NeoForge 1.21.1 模组提供标准化能力,包括耐久方块系统、配方解析和控制台日志捕获。
版本:1.1.0-1.21.1 | 平台:NeoForge 1.21.1 | Java 21
目录
快速开始
添加依赖
将以下内容添加到你的 build.gradle:
repositories {
maven { url = 'https://your-maven-repo' }
}
dependencies {
implementation 'com.cloudworks:CloudWorksAPI:1.1.0-1.21.1'
}
可选依赖:Jade 模组集成
如果你需要为耐久方块显示 Jade 耐久度,请添加 Jade API 依赖:
repositories {
maven { url = "https://api.modrinth.com/maven" }
}
dependencies {
compileOnly "maven.modrinth:jade:15.10.5+neoforge"
}
启用模块
CloudWorks API 使用注解按需激活模块。将相应注解添加到你的主模组类:
@Mod("your_mod_id")
@CloudworksRecipeParser // 启用 RecipeParser 模块
@CloudWorksConsoleSeeker // 启用 ConsoleSeeker + API
public class YourMod {
public YourMod(IEventBus modEventBus) {
// ...
}
}
- 带有注解:强制启用该模块,忽略配置文件开关
- 无注解:由配置文件控制
- 每个模块可独立启用/禁用,彼此之间无依赖
DurableBlock — 耐久方块系统
核心概念
DurableBlock 提供了一套完整的耐久方块系统,允许方块拥有耐久值、抗性和自动恢复能力。
与传统方案不同,该系统不依赖 BlockEntity。相反,它使用一个 LivingEntity 子类(DurableBlockEntity)来存储耐久数据并接收伤害。这种设计结合了 BlockEntity 的数据持久化能力和 LivingEntity 的可攻击性。
架构
DurableBlock (方块)
│ 放置时生成实体
│ 属性:maxDurability, baseResistance, 类型特定抗性
▼
DurableBlockEntity (LivingEntity)
│ 仅服务端实体(clientTrackingRange=0)
│ 绑定方块坐标(boundPos),每 tick 验证方块存在性
│ 耐久计算、伤害处理、NBT 持久化
│
├── 正常流程:耐久归零 → 破坏方块 → 自毁
└── PersistentDurableBlockEntity(子类)
└── 防秒杀保护:拦截秒杀攻击 → 扣除 20% 耐久
核心类
| 类 | 职责 |
|---|---|
DurableBlock |
耐久方块基类,定义耐久属性和抗性参数,管理实体生成 |
DurableBlockEntity |
方块实体替代(LivingEntity),处理伤害、耐久计算、数据持久化 |
PersistentDurableBlock |
持久化耐久方块,继承 DurableBlock,使用 PersistentDurableBlockEntity |
PersistentDurableBlockEntity |
持久化实体,拦截秒杀攻击,转化为耐久损失 |
DurableBlockDamageType |
伤害类型枚举(爆炸 / 物理 / 魔法) |
使用方法
基础耐久方块
public class MyConcreteBlock extends DurableBlock {
public MyConcreteBlock() {
super(Properties.of()
.strength(3.0f)
.requiresCorrectToolForDrops(),
200, // maxBlockDurability:最大耐久
2, // baseResistance:固定伤害减免
0.3f, // explosionResistance:爆炸伤害减免(0.0 ~ 1.0)
0.5f, // physicalResistance:物理伤害减免(0.0 ~ 1.0)
0.1f, // magicResistance:魔法伤害减免(0.0 ~ 1.0)
0 // recoveryRate:每秒自动恢复量(0 = 无)
);
}
}
持久化耐久方块(带防秒杀保护)
public class MyPersistentBlock extends PersistentDurableBlock {
public MyPersistentBlock() {
super(Properties.of().strength(3.0f),
200, 2, 0.3f, 0.5f, 0.1f, 0);
}
}
伤害计算
实际伤害 = max(0, 原始伤害 - 基础抗性) × (1 - 类型抗性)
- 基础抗性:从原始伤害中扣除固定值(最小为 0)
- 类型抗性:根据伤害类型(爆炸/物理/魔法)应用相应的减免比例
- 伤害类型分类:
- 爆炸:TNT、苦力怕、末地水晶、床/重生锚爆炸
- 物理:近战攻击、箭、三叉戟、弹射物
- 魔法:药水、龙息、凋灵效果、虚空伤害、火/闪电
仅服务端实体
耐久方块实体注册时使用 clientTrackingRange(0),意味着该实体仅存在于服务端。客户端完全不知道这个实体的存在。这带来了以下好处:
- 玩家无法近战攻击该实体:客户端没有目标;方块只能被挖掘
- 不阻碍方块交互:不会阻止在方块上方放置方块或左键挖掘
- 生物和远程攻击正常工作:凋灵、僵尸和远程攻击仍可在服务端对实体造成伤害
- 无需同步:不存在客户端/服务端实体数据同步问题
防秒杀保护(持久化)
PersistentDurableBlockEntity 使用多层拦截机制来抵御秒杀攻击:
remove()拦截:检查internalRemoval标志;外部调用改为扣除 20% 耐久setHealth()拦截:防止外部将生命值清零setPose()拦截:防止进入Pose.DYING状态hurt()检测:检查instant_kill_weapon标签,取消秒杀武器伤害事件dead状态修正:每 tick 检查并修正dead字段- 无敌帧:每次受击后 3 tick 无敌,限制每秒最多受到 5 次攻击
秒杀武器标签
cloudworks_api:instant_kill_weapon 物品标签用于标记秒杀武器。当持有带标签物品的实体发起攻击时,PersistentDurableBlockEntity 将直接取消伤害事件。
默认内容:
{
"values": [
"avaritia:infinity_sword"
]
}
整合包作者可以通过数据包向此标签添加更多武器。
生命周期管理
- 方块放置:
DurableBlock.setPlacedBy()在方块位置生成实体 - 方块破坏:实体不再因方块破坏事件而移除,由其自身管理生命周期
- Tick 验证:实体每 tick 检查绑定的方块是否仍然存在;如果不存在,则自毁
- 耐久耗尽:
onDurabilityZero()破坏方块并移除实体 - NBT 持久化:耐久数据通过
addAdditionalSaveData/readAdditionalSaveData保存到区块中
绑定位置机制
每个实体在 boundPos 中存储其绑定的方块位置。当实体因任何原因即将消失时,它会破坏该位置的方块。同时,每 tick 检查该位置的方块是否仍存在且符合预期类型;如果不是,则自行移除。这种设计防止了实体的碰撞箱延伸到相邻方块区域时错误地删除其他实体的 Bug。
Jade 模组集成
DurableBlock 内置了 Jade 集成(DurableBlockJadePlugin),可在 Jade 提示框中显示方块耐久信息:
- 在方块名称下方显示
耐久度:当前 / 最大 - 当前耐久显示 2 位小数
- 由于实体采用仅服务端方案,耐久数据通过
IServerDataProvider在服务端收集,并通过 Jade 的数据包同步到客户端
碰撞箱设计
实体碰撞箱为 1.02×1.02(略大于 1×1 方块),以方块中心(y+0.5)居中,六个面各延伸 0.01 格。这允许生物从任何侧面攻击实体,同时实体不会阻碍方块放置或移动。
RecipeParser — 配方解析
核心概念
配方解析流程:
配方 JSON --> serializeRecipe() --> JsonElement
|
v
DSL 模板 (.rpml) --> TemplateParser --> TemplateNode (AST)
|
v
RecipeExtractor.extract()
|
v
RecipeData
(Ingredient[], Product[])
- 序列化:通过 Minecraft Codec 将配方序列化为 JSON
- 模板解析:将
.rpmlDSL 模板解析为 AST - 数据提取:根据 AST 从 JSON 中提取结构化数据
核心类
| 类 | 职责 |
|---|---|
RecipeParser |
单例核心,管理模板加载、配方解析和流体转换 |
RecipeParserAPI |
静态门面类,对外暴露所有 API |
RecipeData |
解析结果,包含 inputs 和 outputs |
Ingredient |
输入材料:id、count、unit、type |
Product |
输出产物:id、count、unit、type、rate(概率) |
API 参考
所有 API 均通过 RecipeParserAPI 静态方法调用。
基础查询
| 方法 | 描述 |
|---|---|
getRecipeData(ResourceLocation, RecipeManager) |
解析单个配方 |
getRecipeDataBatch(Collection, RecipeManager) |
批量解析配方 |
isRecipeParsable(ResourceLocation, RecipeManager) |
检查配方是否可解析 |
getParsableRecipes(String modId, String recipeType, RecipeManager) |
获取指定类型的所有可解析配方 |
高级查询
// 查找产出目标物品的配方
List<RecipeParseResult> parseProduceRecipe(
ResourceLocation targetId, // 目标物品/流体 ID
QueryMode mode, // ITEM 或 FLUID
RecipeManager recipeManager
)
// 查找使用目标作为输入的配方
List<RecipeParseResult> parseUsageRecipe(
ResourceLocation targetId,
QueryMode mode,
RecipeManager recipeManager
)
| 模式 | 匹配逻辑(产出) | 匹配逻辑(使用) |
|---|---|---|
ITEM |
匹配直接输出 + 流体到物品转换 | 匹配 unit=item 的原料 |
FLUID |
匹配流体输出 + 反向转换匹配 | 匹配 unit=fluid 的原料 |
异步 API 方法
所有异步 API 均通过 RecipeParserAPI 静态方法调用。繁重的配方扫描和解析工作运行在专用工作线程(AsyncRecipeParser 线程池,2 个守护线程)上,然后通过 MinecraftServer 回调将结果送回服务端线程。
RecipeParserAPI.parseProduceRecipeAsync(
ResourceLocation.parse("minecraft:oak_planks"),
QueryMode.ITEM,
recipeManager,
results -> {
// 此回调运行在服务端线程 —— 可以安全地操作 Minecraft 对象
for (RecipeParseResult r : results) {
sendSuccess("找到配方:" + r.getRecipeId());
}
},
errorMsg -> sendFailure("查询失败:" + errorMsg),
server
);
异步方法签名:
| 方法 | 描述 |
|---|---|
getRecipeDataAsync(id, mgr, cb, err, server) |
异步解析单个配方 |
getRecipeDataBatchAsync(ids, mgr, cb, err, server) |
异步批量解析配方 |
parseProduceRecipeAsync(id, mode, mgr, cb, err, server) |
异步查找产出配方 |
parseUsageRecipeAsync(id, mode, mgr, cb, err, server) |
异步查找使用配方 |
数据模型
Ingredient(原料)
| 字段 | 类型 | 描述 |
|---|---|---|
id |
String |
物品/流体/标签 ID |
count |
double |
数量 |
unit |
String |
"item" / "fluid" |
type |
String |
"solid" / "tag" / "fluid" |
Product(产物)
| 字段 | 类型 | 描述 |
|---|---|---|
id |
String |
产物 ID |
count |
double |
数量 |
unit |
String |
"item" / "fluid" |
type |
String |
"solid" / "tag" / "fluid" |
rate |
double |
产出概率(0.0 ~ 1.0,默认 1.0) |
DSL 配方模板
配方模板(.rpml 文件)位于 cloudworks/recipe_parser/templates/,命名格式为 {modid}_{recipetype}.rpml。
标记类型
| 标记 | 语法 | 描述 |
|---|---|---|
input |
<input,id=foo,count=1,unit=item,type=solid> |
声明输入原料 |
output |
<output,id=bar,count=1,unit=item,type=solid> |
声明输出产物 |
object |
<object,id=myObj> |
声明 JSON 对象节点 |
key |
<key,id=myKey> |
动态 JSON 键遍历 |
io_attribute |
<count,output_id=bar> |
向标记注入属性 |
symbol |
<symbol,id=sym,input_id=keyIng> |
声明有序合成符号 |
patternline |
<patternline,id=line> |
声明有序合成模式行 |
duplicate |
<duplicate,id=dupX,structure=X> |
声明可重复的 JSON 对象结构 |
script |
<script,set_global_fluid_transfer=true> |
全局设置脚本 |
optional |
<optional,id=opt> |
可选字段 |
variable |
<variable,id=var> |
变量引用 |
模板示例
原版工作台(有序合成)
{
"type": "minecraft:crafting_shaped",
<script,set_global_fluid_transfer=true,set_global_default_transfer_rate=250>
"pattern": [
<patternline,id=line>
],
"key": <object,id=keyObj>
<key,id=key>
<object,id=keyIng>
"item": <input,id=keyIng,count=1,unit=item,type=solid>
"tag": <input,id=keyIng,count=1,unit=item,type=tag>
<symbol,id=sym,input_id=keyIng>
<count,id=keyIng_count,input_id=keyIng>
<patternline,id=line,input_id=sym>
"result": <object,id=res>
"count": <count,output_id=res>
"id": <type,output_id=res>
"item": <output,id=res,count=1,unit=item,type=solid>
原版工作台(无序合成)
{
"type": "minecraft:crafting_shapeless",
"ingredients": [
<object,id=ingStruct>
"item": <input,id=ingItem,count=1,unit=item,type=solid>
"tag": <input,id=ingItem,count=1,unit=item,type=tag>
<count,id=ingCount,input_id=ingItem>
<duplicate,id=ingStruct>
],
"result": <object,id=res>
"count": <count,output_id=res>
"id": <type,output_id=res>
"item": <output,id=res,count=1,unit=item,type=solid>
流体到物品转换
当配方输出流体时,可以将其转换为等价的物品,从而也可以通过物品 ID 找到该配方。
转换逻辑:总流体量(mB)÷ 比率 → 取整(按四舍五入策略)× 每单位产物数量
全局设置(通过模板中的 <script> 配置):
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
global_fluid_transfer |
boolean |
false |
是否启用流体到物品转换 |
global_default_transfer_rate |
double |
100 |
多少 mB 转换为 1 个物品 |
global_default_transfer_result |
string |
null |
默认转换结果物品 ID |
global_default_transfer_extra_input |
map |
{} |
额外原料 |
global_default_transfer_float_round |
enum |
default |
round_up / round_down / default |
global_enable_template_config |
boolean |
true |
是否启用外部模板配置文件 |
配方级配置(cloudworks/recipe_parser/templates_config/{modid}_{recipetype}.json):
{
"recipe:id": {
"enable_transfer": true,
"transfer_blacklist": ["fluid:to_skip"],
"methods": [{
"rate": 100,
"round": "default",
"extra_input": {"minecraft:item": 1},
"result": {"minecraft:output_item": 2}
}]
}
}
模板更新机制
cloudworks/recipe_parser/config.json:
{
"version": "1.1.0-1.21.1",
"enable_update": true,
"force_update": false,
"update_ignore": ["minecraft_crafting.rpml"]
}
force_update=true→ 跳过所有检查,释放所有文件enable_update=false→ 跳过更新version匹配 → 跳过更新- 否则 → 更新版本,释放
update_ignore之外的文件
ConsoleSeeker — 控制台日志捕获
ConsoleSeeker 将控制台日志实时输出到游戏内聊天,并为下游模组提供完整的事件管线,用于过滤和处理日志。
启用方式
@CloudWorksConsoleSeeker // 强制启用 ConsoleSeeker + API
或在 cloudworks/console_seeker/config.json 中配置:
{
"enable_module": true,
"enable_api": false,
"enable_command_for_any_operator": true,
"list_type": "whitelist",
"player_list": [],
"max_log_length": 150,
"enable_timestamp": false
}
| 字段 | 类型 | 默认值 | 描述 |
|---|---|---|---|
enable_module |
boolean |
true |
是否启用 ConsoleSeeker 模块 |
enable_api |
boolean |
false |
是否启用事件 API(注解可覆盖此项) |
enable_command_for_any_operator |
boolean |
true |
是否允许所有 OP 使用命令 |
list_type |
string |
"whitelist" |
"whitelist" / "blacklist" |
player_list |
string[] |
[] |
玩家名称列表 |
max_log_length |
int |
150 |
日志最大长度(0 = 不截断) |
enable_timestamp |
boolean |
false |
是否显示时间戳 |
架构
Log4j2 Logger
│
▼
ChatAppender (Log4j2 Appender)
│ 过滤 [CHAT] 子字符串
▼
LogToChatManager
│ 按日志级别和玩家订阅过滤
▼
ConsoleSeekerEventManager
│ 内部过滤单元 → 收集 tagSet
▼
NeoForge EVENT_BUS
│ 分发 ConsoleSeekerInfoEvent / WarnEvent / ErrorEvent
▼
ExternalLogFilter(由下游模组订阅)
│ 匹配 tagSet → parse() → onReceive()
▼
下游模组自定义逻辑
事件管线
三个独立的事件管线,对应不同的日志级别:
| 事件类 | 日志级别 |
|---|---|
ConsoleSeekerInfoEvent |
INFO |
ConsoleSeekerWarnEvent |
WARN |
ConsoleSeekerErrorEvent |
ERROR |
所有事件均继承自 ConsoleSeekerLogEvent,包含以下字段:
| 字段 | 类型 | 描述 |
|---|---|---|
loggerName |
String |
日志记录器名称 |
level |
Level |
日志级别 |
message |
String |
格式化消息(已移除 ANSI 颜色代码) |
timestamp |
long |
Unix 时间戳(毫秒) |
threadName |
String |
线程名称 |
thrownString |
String |
异常堆栈跟踪(无异常时为 null) |
tagSet |
Set<DirectedDeliveryTag> |
定向投递标签集合 |
内部过滤器(主动过滤)
通过 ConsoleSeekerEventManager 注册内部过滤单元,让 ConsoleSeeker 主动过滤日志:
// 实现 InternalFilterUnit 接口
InternalFilterUnit myUnit = new InternalFilterUnit() {
public DirectedDeliveryTag getTag() {
return new DirectedDeliveryTag("mymod", "cpu_alert");
}
public boolean test(LogEvent event) {
return event.getLoggerName().startsWith("com.example");
}
};
// 注册到内部过滤单元列表
ConsoleSeekerEventManager.addInternalFilterUnit(myUnit);
过滤规则:
- 空列表 → 所有日志事件直接发布,tagSet 为空
- 非空列表 → 至少一个单元必须通过才能发布;收集通过单元的标签到 tagSet
- 所有单元均失败 → 事件被丢弃
定向投递标签
使用 "modid:tagName" 格式,防止跨模组冲突:
// 创建标签
DirectedDeliveryTag tag = new DirectedDeliveryTag("mymod", "cpu_alert");
// 匹配规则:空标签匹配一切,非空标签需要精确匹配
tag.matches(otherTag);
外部过滤器(被动过滤)
下游模组继承 ExternalLogFilter<T> 进行自定义日志解析:
public class MyCpuFilter extends ExternalLogFilter<Integer> {
public MyCpuFilter() {
super(new DirectedDeliveryTag("mymod", "cpu_alert"));
}
@Override
protected Integer parse(ConsoleSeekerLogEvent event) {
// 自定义解析逻辑
return Integer.parseInt(event.getMessage());
}
@Override
protected void onReceive(Integer value) {
// 处理解析结果
System.out.println("CPU 使用率:" + value);
}
}
// 注册以开始接收事件
MyCpuFilter filter = new MyCpuFilter();
filter.register();
工作流程:
- 将事件的
tagSet与过滤器的标签进行匹配(交集匹配,空标签集匹配一切) parse()将原始事件转换为自定义类型(默认返回原始日志字符串)onReceive()处理解析结果
调试命令
命令前缀:/cloudworks。需要 OP 等级 4(部分命令)。
配方模块
/cloudworks recipe parse produce item [id] — 查询产出指定物品的配方
/cloudworks recipe parse produce liquid [id] — 查询产出指定流体的配方
/cloudworks recipe parse usage item [id] — 查询使用指定物品的配方
/cloudworks recipe parse usage liquid [id] — 查询使用指定流体的配方
/cloudworks recipe parsebatch <modid> <type> — 批量解析
/cloudworks recipe listtemplates — 列出已加载的模板
控制台模块
/cloudworks console <info|warn|error> <on|off> — 切换日志输出到聊天
/cloudworks config console player_list add <player_name> — 添加玩家到列表
/cloudworks config console player_list remove <player_name> — 从列表移除玩家
/cloudworks config console player_list query — 查看玩家列表
状态查询
/cloudworks status — 查看所有模块状态
输出内容包括:RecipeParser 模板数量、ConsoleSeeker 启用的级别/API 状态/过滤单元数量。
模块架构
src/main/java/com/cloudworks/api/
├── CloudWorksAPI.java # NeoForge 模组入口点
├── annotation/
│ ├── CloudworksRecipeParser.java # 启用 RecipeParser
│ └── CloudWorksConsoleSeeker.java # 启用 ConsoleSeeker
├── command/
│ └── DebugCommand.java # 统一调试命令入口
│
├── durableblock/ # DurableBlock 模块
│ ├── DurableBlock.java # 耐久方块基类
│ ├── DurableBlockEntity.java # 方块实体替代(LivingEntity)
│ ├── PersistentDurableBlock.java # 持久化耐久方块
│ ├── PersistentDurableBlockEntity.java # 防秒杀保护实体
│ ├── DurableBlockDamageType.java # 伤害类型枚举
│ └── jade/
│ └── DurableBlockJadePlugin.java # Jade 模组集成插件
│
├── recipeparser/ # RecipeParser 模块
│ ├── RecipeParser.java # 核心单例
│ ├── RecipeParserAPI.java # 静态 API 门面
│ ├── AsyncRecipeParser.java # 异步线程池
│ ├── ApiSelfTest.java # 自动测试
│ ├── DebugOutputWriter.java # 调试 JSON 导出
│ ├── dsl/ # DSL 模板引擎
│ │ ├── Template.java, TemplateNode.java
│ │ ├── TemplateParser.java, TemplateTokenizer.java
│ │ ├── TemplateValidator.java, RecipeExtractor.java
│ │ ├── GlobalSettings.java, TemplateConfig.java
│ │ └── ...
│ └── model/ # 数据模型
│ ├── RecipeData.java, Ingredient.java, Product.java
│ └── QueryMode.java, RecipeParseResult.java
│
└── consoleseeker/ # ConsoleSeeker 模块
├── ChatAppender.java # Log4j2 Appender
├── LogToChatManager.java # 日志转聊天管理器
├── ConsoleSeekerConfig.java # 配置文件管理
├── ConsoleSeekerEventManager.java # 事件管线核心
├── ConsoleSeekerCommand.java # 命令处理器
├── InternalFilterUnit.java # 内部过滤单元接口
├── ExternalLogFilter.java # 外部过滤器抽象类
├── DirectedDeliveryTag.java # 定向投递标签
├── LogFilter.java # 日志工具(截断/去色)
└── event/ # 事件类
├── ConsoleSeekerLogEvent.java # 基础事件类
├── ConsoleSeekerInfoEvent.java
├── ConsoleSeekerWarnEvent.java
└── ConsoleSeekerErrorEvent.java
许可证
本项目以 GNU 通用公共许可证 v3.0 开源。参见 LICENSE 获取完整文本。
CloudWorks API — 统一模组开发接口 版权所有 (C) 2026 CloudWorks Team
本程序是自由软件:你可以基于自由软件基金会发布的 GNU 通用公共许可证的条款(许可证第 3 版或(由你选择的)任何后续版本)重新分发和/或修改它。
本程序的发布是希望它能有用,但不提供任何保证;甚至没有适销性或特定用途适用性的隐含保证。详情请参阅 GNU 通用公共许可证。
你应该已经随本程序收到了一份 GNU 通用公共许可证的副本。如果没有,请参见 https://www.gnu.org/licenses/。
正在加载版本记录…
正在加载评论…
评论在新手盒子客户端中发表,这里同步展示。