这是一份 最极致便捷、零门槛 的公开教程。用户只需要做 3 件事:① 写 1 个 .java 模组文件(末尾写 1 行 SDK 版本声明即可,不用单独下 SDK) · ② 保存文件 · ③ 拖进启动器,剩下的一切启动器全程自动完成(仅做编译/安全扫描等机械步骤,绝不自动改你写的代码或元数据,要改一定会先弹出预览窗 + 给你「确认修改」按钮,你点了才会改):自动编译(javac 标准流程,不改你 .java 文本)→ 生成依赖预览 → 生成 smalo.mod.json 预览 → AI 格式纯检测(发现问题弹一键修复按钮,你点确认才改)→ 三重安全审核 → 你点「提交上架」→ 进入全球模组广场(全球可见)。绝对不需要你自建任何 Gitee/GitHub 仓库,不需要任何手动打 Release,不需要写任何除了 minecraft 之外的依赖。所有包名、类名、方法签名 100% 对应真实 API。
smalo-sdk.jar 这 1 个文件(带一键检测/下载/复制路径按钮).java 文件(模组代码 + SDK 版本声明都在这同一个文件里,SDK 声明写在最后),保存后直接拖进启动器(或点「管理页 → 导入 Java 源文件」按钮),启动器会自动完成:根据你末尾声明的 SDK 版本自动拉取对应 SDK → 编译 → 生成 smalo.mod.json → 打 JAR → 安装成功。不需要单独下载 smalo-sdk.jar,不需要额外弄第二个文件,不需要在命令行敲 javac / jar。
import com.smalo.api.SmartMod; — 引入 SmartMod 注解(代替手写 smalo.mod.json)import com.smalo.api.smart.SmartModBase; — 继承的基类(自动实现生命周期)import static com.smalo.api.smart.SmartAPI.*; — 引入快捷方法:log()、bus()、fire()、api()@SmartMod(...) 注解 + extends SmartModBase + 实现一个 smartInit() 方法@UsesSmaloSDK(version = "2.0.0"),启动器看到这一行自动拉取对应版本 SDK,完全不需要你手动下载 smalo-sdk.jar 或手动写 classpath。SmartHelloMod.java(复制粘贴保存即可)package com.example.smart;
import com.smalo.api.SmartMod;
import com.smalo.api.smart.SmartModBase;
import static com.smalo.api.smart.SmartAPI.*;
// ⭐ 只在这一处配置所有元数据,启动器自动读取并生成 smalo.mod.json
@SmartMod(
modId = "smart-hello-mod",
displayName = "SmartAPI 你好世界",
version = "1.0.0",
description = "SmartAPI 单文件极简开发模式的第一个示例",
author = "你的名字",
license = "MIT",
supportedMcVersions = { "1.20.1" }
)
public class SmartHelloMod extends SmartModBase {
@Override
protected void smartInit() {
// 🌟 你的全部业务逻辑写在这里!
log("SmartHelloMod 加载成功!SmartAPI 单文件开发模式编译 ✓");
api(); // 占位:仅表示你已正式进入 SmartAPI 生态
// ⭐ 事件订阅(例子:监听任意模组加载事件)
// bus().subscribe(SomeEvent.class, event -> { ... });
// ⭐ 自定义事件触发
// fire(new MyCustomEvent(player));
}
}
// ======================================================================
// 🎯 ⭐【写在文件最末尾 · SDK 版本绑定声明】1 行搞定!
// 启动器扫描到这一行,自动从全球广场 SDK 仓库拉取对应版本的 smalo-sdk.jar
// 并在内部 javac 编译时自动加到 classpath,你完全不用手动下载/配置任何东西
// ======================================================================
@com.smalo.api.UsesSmaloSDK(version = "2.0.0")
| 属性 | 含义 | 默认值 |
|---|---|---|
modId 必填 | 模组唯一 ID(只允许 a-z 0-9 - .,不能有空格和大写) | — |
displayName | 给用户看的中文名(启动器和广场展示) | modId |
version | 语义化版本 | "1.0.0" |
description | 一句话简介,≤ 26 字 | 空 |
author | 作者名 | "SmartAPI Developer" |
license | 开源协议 | "MIT" |
supportedMcVersions | 支持的 MC 版本数组 | {"1.20.1"} |
side | CLIENT / SERVER / BOTH | BOTH |
minLoaderVersion | 最低启动器版本 | "[0.1,)" |
@UsesSmaloSDK(version="...") 写在文件最末尾 ⭐ 最重要 | SDK 版本绑定声明:启动器扫描到后,自动拉取对应版本的 smalo-sdk.jar 并加到 classpath,你完全不用手动下载 SDK / 写 -cp 参数。不写也行,默认最新稳定版。 | "2.0.0"(默认最新稳定版) |
SmartHelloMod.java(文件名随便,但建议和 public class 同名).java 文件拖到启动器窗口上(支持拖拽上传,无需点任何按钮)javac + jar 命令(这只是给想深度定制的开发者看的,新手直接看第 0 章拖进启动器就行)。
| 软件 | 作用 | 推荐版本 | 是否必装 |
|---|---|---|---|
| JDK(Java SE Development Kit) | 编译 .java → .class、运行 JVM | 21.0.x LTS (x64) 或 17.0.x LTS | ✅ 必装 |
| IntelliJ IDEA Community | 写代码 + 调试 Java | 最新稳定版 | ⚙️ 可选(推荐) |
:: 必须用 javac 验证(java -version 只说明有 JRE 不代表有 JDK!)
javac -version
:: 期望输出:javac 21.0.3 或 javac 17.0.11
bin 目录没加进系统 PATH。Path → 编辑 → 新建C:\Program Files\Java\jdk-21\bin(末尾必须是 \bin,按你真实安装路径)JAVA_HOME = C:\Program Files\Java\jdk-21(末尾不要加 \bin)smalo-sdk.jar 这 1 个文件外部开发者只需要从 SMALO 官方下载站获取 1 个 SDK 文件:
smalo-sdk.jar — 包含所有注解、接口、事件基类、事件总线、ModInfo 等编译时依赖。点下面 3 个按钮 按顺序走即可:检测 → 下载(如缺失) → 复制路径。
com.smalo.api.SmaloModAnnotation — ⭐ 模组类必须加的注解(最重要)com.smalo.api.SmaloMod — 模组接口(推荐继承 AbstractSmaloMod 更省事)com.smalo.api.AbstractSmaloMod — ⭐ 推荐继承的抽象基类(有默认实现和 log 方法)com.smalo.api.Event — 所有自定义事件的基类com.smalo.loader.ModInfo — 模组元数据对象(onLoad 时传入)com.smalo.loader.ModInfo.Side — 枚举:CLIENT / SERVER / BOTHcom.smalo.loader.EventBus — 事件总线(onLoad 时传入,用于订阅事件)com.smalo.loader.EventBus.SubscribeEvent — 方法级事件订阅注解com.smalo.loader.EventBus.Priority — 事件处理优先级枚举smalo-sdk.jar(-cp classpath 用)HelloMod.java(包路径 + 继承 AbstractSmaloMod)smalo.mod.json(JAR 根目录必放)按下面 4 步照抄,1 分钟生成可加载的 hello-mod-1.0.0.jar。
桌面新建空文件夹 "MyFirstMod",里面放:
📁 MyFirstMod\
├── 📄 HelloMod.java ← 你写的代码(下面 3.2 给完整内容)
├── 📄 smalo.mod.json ← 模组元数据(下面 3.3 给模板)
└── 📄 smalo-sdk.jar ← 从 SMALO 官方下载站获取的公开 SDK
ClassNotFoundException。下面示例 package 就是 com.example.hello。
// ① 第一行:package,路径层级必须和 entryClass 完全一致
package com.example.hello;
// ② 导入公开 SDK 中的类(这些 import 路径 100% 真实,照抄就行)
import com.smalo.api.AbstractSmaloMod;
import com.smalo.api.SmaloModAnnotation;
import com.smalo.api.SmaloModAnnotation.Side;
import com.smalo.loader.ModInfo;
import com.smalo.loader.EventBus;
/**
* ③ 在类上添加 @SmaloModAnnotation 注解(所有字段说明见下方注释)
*
* 注解字段速查:
* - modId 模组唯一 ID(只能小写字母/数字/短横杠/点号,不能空格)
* - version 语义化版本 MAJOR.MINOR.PATCH
* - displayName 给用户看的中文名(启动器和广场会展示)
* - description 一句话简介,≤ 26 字
* - author 作者名(可选,默认 "Unknown")
* - license 协议(可选,默认 "All Rights Reserved")
* - dependencies 依赖其他模组 modId;只写你真正用到的第三方模组
* - versionRanges 每个 dependencies 对应的版本范围,一一对应
* - side CLIENT 仅客户端 / SERVER 仅服务端 / BOTH 两端都加载
* - minLoaderVersion 最低启动器版本,通常写默认值即可
*/
@SmaloModAnnotation(
modId = "hello-mod",
version = "1.0.0",
displayName = "我的第一个 SMALO 模组",
description = "玩家进世界自动发欢迎语的示例模组",
author = "YourName",
license = "MIT",
// 依赖说明:如果只是纯示例,只写 minecraft 就够了
// 加载器会自动跳过启动器自身依赖的检查,不用手动声明
dependencies = { "minecraft" },
versionRanges = { "[1.20.1,)" },
side = Side.BOTH,
minLoaderVersion = "[0.1,)"
)
// ④ 继承 AbstractSmaloMod(推荐,有默认实现 + log() 方法)
public class HelloMod extends AbstractSmaloMod {
// ⑤ 生命周期 1:模组被加载时触发一次(拿到 ModInfo 和 EventBus)
@Override
public void onLoad(ModInfo mod, EventBus eventBus) {
// 可以用继承来的 log() 方法打印日志
log("HelloMod 已被 SMALO 加载器识别,modId = " + getModId());
log("版本 = " + getVersion() + ",作者 = " + getAuthor());
// ⭐ 事件订阅方式 A:Lambda 回调(简单推荐)
eventBus.subscribe(ModLoadEvent.class, event -> {
log("收到 ModLoadEvent:模组 " + event.getModId() + " 已加载");
});
// ⭐ 事件订阅方式 B:注册当前类为监听器(配合下方 @SubscribeEvent 方法)
eventBus.register(this);
}
// ⑥ 生命周期 2:模组初始化阶段(事件系统就绪后)
@Override
public void onInitialize() {
log("HelloMod 初始化完成 ✅");
}
// ⑦ 生命周期 3:游戏退出 / 模组卸载时
@Override
public void onShutdown() {
log("HelloMod 已卸载,再见 👋");
}
// ==================================================================
// ⑧ 事件订阅方式 B:用 @SubscribeEvent 注解标记方法(参数必须是 Event 子类)
// 注意:这个注解是 EventBus 内部注解,import 路径如下:
// import com.smalo.loader.EventBus.SubscribeEvent;
// ==================================================================
@com.smalo.loader.EventBus.SubscribeEvent(priority = EventBus.Priority.NORMAL)
public void onAnyModLoaded(ModLoadEvent event) {
// 这个方法会在任何模组被加载时自动被调用
log("[@SubscribeEvent] 检测到模组加载:" + event.getModId());
}
}
// ==================================================================
// 附:如果要自定义事件,只要继承 com.smalo.api.Event 即可
// 例如:public class PlayerWelcomeEvent extends Event { ... }
// 然后在你想要触发的地方:eventBus.publish(new PlayerWelcomeEvent(player));
// ==================================================================
/**
* 一个内置的示例事件:ModLoadEvent(演示用,实际使用时请使用 SDK 中已定义的事件)
* 如果你需要玩家加入世界、方块破坏、聊天消息等游戏级事件,请参考 SDK 中已提供的事件类清单。
*/
class ModLoadEvent extends com.smalo.api.Event {
private final String modId;
public ModLoadEvent(String modId) { this.modId = modId; }
public String getModId() { return modId; }
}
根目录,不能放进 META-INF 或子文件夹,否则启动器无法识别你的 JAR 是 SMALO 模组!
version + author(其他全可选,缺了系统自动补默认值)id / name / version / author / mainClass / type / supportedVersions / side / dependencies / descriptionmodId→id、entryClass→mainClass、authors→author、supportedMcVersions→supportedVersions、displayName→name{
"id": "hello-mod",
"name": "我的第一个 SMALO 模组",
"version": "1.0.0",
"author": "YourName",
"mainClass": "com.example.hello.HelloMod",
"supportedVersions": ["1.20.1"],
"type": "utility",
"side": "both",
"description": "玩家进世界自动发欢迎语的示例模组",
"dependencies": [
{ "modId": "minecraft", "version": ">=1.20.1", "required": true }
]
}
id:模组唯一 ID(小写字母/数字/短横杠/点号,不能空格)mainClass:Java 主类入口(必须和 package + 类名完全一致,如 com.example.hello.HelloMod)type:合法值 api/loader/addon/utility/library/smartapi/declarativeside:合法值 client/server/bothsupportedVersions:支持的 MC 版本列表(如 ["1.20.1", "1.20.4"])mainClass 或设为 usesSmartAPI: true,启动器自动用 SmartAPI 解释执行:: 进入你的工作目录
cd /d "%USERPROFILE%\Desktop\MyFirstMod"
:: ① 建 classes 输出目录(放编译好的 .class)
if not exist classes mkdir classes
:: ② ⭐ 真正的编译命令(-cp 指定 SDK,-d 指定输出目录,-encoding UTF-8 避免中文乱码)
:: ❗ 这是 100% 能工作的一行,不依赖任何内部工程 ❗
javac -encoding UTF-8 -cp smalo-sdk.jar -d classes HelloMod.java
:: ③ 把 smalo.mod.json 复制到 classes 根目录(打 JAR 时会自动放到 JAR 根目录)
copy smalo.mod.json classes\smalo.mod.json
:: ④ 进入 classes,打成最终 JAR(名字建议 「modId-version.jar」)
cd classes
jar cfM ..\hello-mod-1.0.0.jar *
:: ⑤ 验证 JAR 内部结构(必须有根目录 smalo.mod.json + 对应 entryClass 路径)
cd ..
jar tf hello-mod-1.0.0.jar
smalo.mod.json
com/example/hello/HelloMod.class
com/example/hello/ModLoadEvent.class
只要这几行都在 → 恭喜!hello-mod-1.0.0.jar 已经是一个 合法的 SMALO 模组 了,直接拖进启动器版本模组目录或点「导入本地 JAR」就能加载。
一个 SMALO 模组的完整生命周期按顺序触发 5 个方法,全部是 SmaloMod 接口的 default 方法,按需 @Override 就行:
| 方法 | 触发时机 | 常用操作 |
|---|---|---|
onLoad(ModInfo, EventBus) | 模组类被加载器识别后立刻调用(最早) | 订阅事件、读取 modInfo 元数据、保存 eventBus 引用 |
onInitialize() | 所有模组 onLoad 完成后 | 初始化配置、注册自定义内容、打印启动日志 |
onPreLaunch() | MC 主类即将调用之前 | 一些必须在游戏启动前做的准备 |
onPostLaunch() | MC 主类返回后(游戏窗口已出现) | UI 相关的初始化、进入主菜单提示 |
onShutdown() | 游戏进程退出前 | 保存数据、关闭文件句柄、打印告别日志 |
SMALO 事件总线提供 两种 订阅方式,你可以任选,也可以混用:
在 onLoad() 里拿到 eventBus 对象后直接调用 .subscribe():
eventBus.subscribe(
SomeEvent.class,
event -> {
// 你的处理逻辑
log("事件触发:" + event);
}
);
优点:不用额外写方法,代码就近。
先在类上写好带注解的方法,然后调用 eventBus.register(this) 一次性注册:
@EventBus.SubscribeEvent(priority = Priority.HIGH)
public void onSomething(SomeEvent e) {
log("收到事件:" + e);
}
// onLoad 里注册:
eventBus.register(this);
优点:多事件时代码结构清晰,还能指定 priority 优先级。
只要继承 com.smalo.api.Event 即可:
package com.example.hello;
import com.smalo.api.Event;
public class CustomHelloEvent extends Event {
private final String message;
public CustomHelloEvent(String msg) { this.message = msg; }
public String getMessage() { return message; }
}
// 触发事件(任何地方只要拿到 eventBus):
// eventBus.publish(new CustomHelloEvent("你好"));
hello-mod-1.0.0.jar
├── smalo.mod.json ← ⭐ 必放!根目录,不能改名不能放子目录!
├── com/
│ └── example/hello/
│ ├── HelloMod.class ← entryClass 对应的 .class
│ └── ... 其他 .class
└── assets/hello-mod/ ← 可选,放纹理、模型、语言文件、配方 json
├── textures/item/wand.png
└── lang/zh_cn.json
jar tf hello-mod-1.0.0.jar | findstr smalo.mod.json → 必须有输出(smalo.mod.json 在根目录)entryClass 对应的 .class 路径完全对应(package 层级要对上).java 源文件、.iml、target/、build/ 等 IDE / 构建目录smalo-sdk.jar 的内容再打进你自己的 JAR(SDK 是启动器提供的全局依赖)为避免开发者疑惑"我的模组为什么被剔除",特此公开审核规则的全部细节。正常模组绝不会误判,只有以下三类才会被屏蔽。
| 被屏蔽的模组类型 | 识别规则(精确匹配) | 为什么屏蔽 |
|---|---|---|
| 钢模组 | modId / 名称包含 gang / exclusive-mod / 单字"钢" | 启动器核心私有模组,不允许在广场上架 |
| SMALO API 模组 | modId 精确匹配 smalo-api / smalo_sdk | 启动器内置 API,重复上架会冲突 |
| 开发支持库 | modId 精确匹配 smartapi-core / smart_api_core | 启动器运行时全局注入,不允许独立分发 |
../ 的恶意路径),正常路径全部通过C:\ 或 / 开头),已修复移除,不再误判entryClass→mainClass、modId→id、authors→author、supportedMcVersions→supportedVersions、displayName→name、category→typeC:\Users\<用户名>\Desktop\SMALO\ai-config.json(本地配置文件,不外传).java(或者已经手动编好的 .jar)| 情况 | AI 处理方式 | 会被拦截吗? | ||||
|---|---|---|---|---|---|---|
| smalo.mod.json 漏写 / 格式不对 / 位置放错 | ✅ 纯检测,标出 JSON 第几行缺了/哪列格式错了/位置错了 → 弹出「一键修复」按钮,你点确认才生成/修正/挪位置(你不点不会改) | ❌ 不会拦(你点完修复按钮就过) | ||||
| dependencies 没写 / 写多了 / 版本号不对 | ✅ 纯检测,列出具体哪条依赖有问题 → 弹出「建议修复为仅包含 minecraft」按钮,你点确认才改 | ❌ 不会拦 | ||||
| modId 有大写 / 空格 / 中文 | ✅ 纯检测,显示非法字符位置 + 建议改成的合法 ID → 你点确认才转换为小写/加连字符 | ❌ 不会拦 | ||||
| package / entryClass 路径对不上 / import 写错 | ✅ 纯检测,列出具体类路径和 import 行号 + 建议改法 → 你点确认才修改类路径/import | ❌ 不会拦 | ||||
| javac 漏写 -cp / 编译参数不对 | ✅ 启动器内部 javac 自动用 SDK classpath 重新编译一遍(此步是编译你的源代码,不改动你的代码文本,不违法) | ❌ 不会拦 | ||||
| 把 smalo-sdk.jar 内容塞进自己模组 JAR 里 | ✅ 纯检测,标记出哪些是 SDK 重复类 → 弹出「建议剥离 SDK 重复类避免 LinkageError」按钮,你点确认才拆包去重 | ❌ 不会拦 | ||||
| 支持的 MC 版本没写 / 写少了 | ✅ 纯检测,建议扩展为 1.20.1 ~ 1.21.4 全兼容 → 你点确认才改 supportedMcVersions 字段 | ❌ 不会拦 | ||||
| 【AI 记忆拦截 ①】核心代码与本教程示例代码(SmartHelloMod / 小地图示例等)1:1 完全一致 判断规则:去掉注释/字符串常量/空格/标识符重命名后,核心 AST hash 完全匹配 | 🔴 直接拦截(请在模板基础上写你自己的业务逻辑,不要照抄空模板上传) | ✅ 会拦(防灌水,保证广场内容质量) | ||||
| 【AI 记忆拦截 ②】核心代码与广场上已有其他模组的相似度比对(三档阈值) 判断规则:核心 AST 相似度比对(去掉注释/标识符/空格后)。三档: ① 相似度 < 90% → 直接通过; ② 相似度 90% ~ 93.99% → 触发「双 AI 联合裁决」(DeepSeek AI + Kimi AI,2/2 通过制,≤30s 出结果); ③ 相似度 ≥ 94% → 自动触发「作者名双重校验」 ★ 作者名定义:文件声明作者 = @SmartMod.author 注解 / smalo.mod.json.author 里你填的名字;广场注册作者 = 原相似模组上传时在广场后台绑定的真实发布者账号名。 |
| 三档处理(真抄袭才会拦) | ||||
| 恶意代码:病毒 / 挖矿 / 盗号 / 读取用户隐私 / 联网后门 | ❌ 三重审核拦截(AI 规则审核 + 系统静态扫描 + 沙箱动态执行) | ✅ 必须拦(保护玩家安全) | ||||
| AI 名称 | 职责分工(它判断什么) | 判「疑似抄袭」的标准 |
|---|---|---|
| ① DeepSeek AI(深度语义比对) | 看「换变量名 / 删注释 / 调顺序 / 改空白」的伪装:把两份代码 AST 化、变量统一重命名为占位符后,做语义级图匹配,判断是不是同一份代码的简单伪装。 | 语义级图相似度 ≥ 92%,且核心算法/钩子调用顺序完全一致 → 判疑似抄袭。 |
| ② Kimi AI(业务意图比对) | 看「两份模组的业务意图是不是 1:1 重合」:比对事件订阅清单、核心循环逻辑、玩家交互钩子、渲染管线使用,判断是否是换了个名字的复制粘贴,还是巧合撞车(比如两个人都写了同类小地图,但实现细节不同)。 | 业务意图清单重合度 ≥ 95%,且实现路径完全一致(事件/API 调用顺序相同)→ 判疑似抄袭。 |
以下代码块就是我们在服务器端调用 DeepSeek / Kimi API 时,注入到 system role 的提示词(每次比对都带,让 AI 严格按我们的标准判断,不会把格式骨架当成业务逻辑比对):
# 你是 SMALO 模组广场的抄袭判定 AI(双 AI 联合裁决系统的一员)。你的唯一任务:判断提交的新模组【A】与广场上的历史模组【B】是否属于抄袭。
# ============================================================
# 🎯 SMALO 模组固定格式骨架(以下部分不纳入抄袭相似度比对!必须忽略!)
# ============================================================
# 1. 【单文件 SDK 绑定声明】(写在 .java 文件最末尾,固定 1 行,完全不算抄袭)
# @com.smalo.api.UsesSmaloSDK(version = "2.0.0")
#
# 2. 【固定 import 模板】(所有模组都会写,不算抄袭。两套模式的 import 全部忽略)
# --- 2a. SmartAPI 单文件极简模式(新手推荐,第 0 章)---
# import com.smalo.api.SmartMod;
# import com.smalo.api.smart.SmartModBase;
# import static com.smalo.api.smart.SmartAPI.*;
# --- 2b. 传统 SmaloMod 模式(第 4 章高级可选)---
# import com.smalo.api.AbstractSmaloMod;
# import com.smalo.api.SmaloModAnnotation;
# import com.smalo.api.eventbus.EventBus;
# import com.smalo.api.ModInfo;
#
# 3. 【@SmartMod 注解骨架】(固定字段结构,不算抄袭。字段取值完全相同(如 supportedMcVersions 都是 1.20.1)也不算抄袭。)
# @SmartMod(
# modId = "xxx", displayName = "xxx", version = "1.0.0",
# description = "xxx", author = "xxx", license = "MIT",
# supportedMcVersions = { "1.20.1", "1.21", "1.21.4" },
# side = Side.COMMON, minLoaderVersion = "1.0.0"
# )
#
# 4. 【标准生命周期 + 钩子注册声明】(所有模组都会写的模板外壳,不算抄袭。注意:仅注册外壳忽略,内部 lambda/方法体逻辑参与比对!)
# 4a. 继承基类声明骨架:
# public class Xxx extends SmartModBase { ... }
# public class Xxx extends AbstractSmaloMod { ... }
# 4b. SmartAPI 模式 smartInit() 方法签名(空方法体 + @Override 骨架忽略):
# @Override protected void smartInit() { /* 内部业务逻辑保留比对 */ }
# 4c. 传统模式 5 个生命周期钩子(方法签名本身 + 空方法体骨架忽略,内部业务逻辑保留比对):
# void onLoad() / onInitialize() / onPreLaunch() / onPostLaunch() / onShutdown()
# 4d. 两种事件订阅外壳注册(忽略外壳,lambda / 注解方法的内部逻辑保留比对):
# eventBus.subscribe(SomeEvent.class, (e) -> { /* 内部逻辑保留比对 */ });
# @EventBus.SubscribeEvent public void onXxx(SomeEvent e) { /* 方法体保留比对 */ }
# 4e. SmartAPI 静态快捷注册外壳(外壳忽略,传入 lambda / 方法引用的内部逻辑保留比对):
# SmartAPI.onChat(this::onPlayerChat);
# SmartAPI.onJoin(this::onPlayerJoin);
# SmartAPI.onTick(this::onGameTick);
# ...(其他标准事件订阅外壳)
#
# 5. 【smalo.mod.json 元数据标准字段】(字段名结构固定,不算抄袭。仅当字段 value 整段大段重复文字时才提示,字段名本身完全忽略)
# {
# "schemaVersion": 1,
# "modId": "...", "name": "...", "version": "...",
# "entryClass": "...", "supportedMcVersions": [...],
# "description": "...", "authors": [ "...", "..." ],
# "license": "...", "tags": [...], "category": "...",
# "dependencies": [{ "modId": "minecraft", "version": ">=1.20.1" }],
# "optionalDependencies": []
# }
#
# ============================================================
# ✅ 真正纳入相似度比对的部分(只有这些!)
# ============================================================
# 只比对「业务逻辑核心代码」:钩子函数体 / lambda 内部的实际算法/处理逻辑、自定义数据结构、非标准 API 组合调用顺序、玩家交互规则实现、渲染管线自定义部分、纯业务类的字段与方法实现。
#
# ============================================================
# 📊 你的输出格式(固定 JSON,不要输出多余文字!)
# ============================================================
{
"decision": "NOT_COPIED | SUSPECTED_COPIED",
"confidence": 0.00 ~ 1.00, # 保留 2 位小数,四舍五入
"ignored_skeleton_ratio": 0.00 ~ 1.00, # 你忽略掉的固定骨架代码占整份代码的比例(保留 2 位小数,四舍五入)
"business_logic_similarity": 0.00 ~ 1.00, # 真正业务逻辑的相似度(忽略骨架后)(保留 2 位小数,四舍五入)
"evidence": "≤ 80 字中文,格式:类A#方法A(Lx-Ly) 与 类B#方法B(Lx-Ly) 在 [算法/API顺序/交互规则] 上高度相似/原创"
}
双 AI 裁决运行在启动器服务器后台,Kimi / DeepSeek 的 API Key(包括你提到的 Kimi 私有 Key)绝对不能硬编码写在公开 HTML 教程、前端 JS、客户端 JAR、或任何用户能下载到的文件里! 正确做法:Key 存在服务器端环境变量 / Secret 管理工具,前端只传「相似度比对请求」,由服务器端安全加载 Key 后调用 AI 接口。否则 Key 会被抓包盗用,产生高额欠费和安全风险。
FAQ = Frequently Asked Questions = 常见问题解答:就是大家最常问的问题 + 最担心的事,集中整理在这里。先翻 FAQ 再担心,你 99% 的担心 AI 都已经自动解决了。
minecraft 依赖?smalo-api / smartapi-core 这些依赖需要我自己写吗?A:不需要,一行都不用自己写!
smalo-sdk.jar 里已经自带了所有注解、接口、事件、EventBus、ModInfo(也就是 smalo-api + smartapi-core 合在一起的公开编译时依赖)dependencies 里"dependencies": [{ "modId": "minecraft", "version": ">=1.20.1" }](这一行 AI 也会帮你自动补,你甚至可以不写)✅ AI 纯检测(不碰你代码):缺了 → 弹出「建议生成 smalo.mod.json 预览窗」,显示完整内容给你看,你点「确认修改」才写入;格式错 → 精确告诉你 JSON 第几行第几列缺逗号/引号错;位置错 → 建议移到 JAR 根目录,你点确认才移动。AI 不会在你没点确认前私自写/改/挪任何一个文件。
✅ AI 纯检测(不碰你代码):启动器静态扫描 class 文件,找出真正的入口类 + 列出 entryClass 与实际 package 不一致的具体路径,弹出「建议修正 entryClass 为 xxx」按钮,你点确认才写入 JSON。少一层 / 多一层 / 路径拼错全部明确标出行号。
-cp smalo-sdk.jar / 参数不对?✅ AI 纯检测(不碰你代码):不管你是拖 .java 还是拖自己编好的 .jar,启动器内部 javac 用 SDK classpath 重新编译一遍源代码(此步是 javac 标准编译过程,不修改你的 .java 原文件文本,完全合规合法)。参数错误:AI 检测出缺 -encoding / -cp,直接提示「启动器会用正确参数重新编译,无需你手动改命令」。
✅ AI 纯检测(不碰你代码):精确标出非法字符位置 + 给出建议的合法 ID。比如你写 "Hello 我的 Mod" → 建议变成 "hello-wo-de-mod",你点「采用建议」按钮才修改。AI 不会私自替换你填的 modId。
✅ AI 纯检测(不碰你代码):启动器内置「真实 SDK import 速查表」,如果你写错了 → 精确标出第几行 import 错了 + 给正确包名建议 + 一键修复按钮(你点才改)。下面这些是 AI 认识的常见错误写法:
// ❌ 你可能写错的虚构包名(AI 标出行号 + 弹出建议,你点「修复」才替换为 ✅ 正确写法)
// import com.smalo.sdk.api.SmaloModAnnotation; → 建议替换为 ✅ import com.smalo.api.SmaloModAnnotation;
// import com.smalo.sdk.api.bus.SubscribeEvent; → 建议替换为 ✅ import com.smalo.loader.EventBus.SubscribeEvent;
// import com.smalo.sdk.api.chat.ComponentText; → 建议替换为 ✅ 对应的正确类(SDK 里有对应聊天组件)
// ✅ 你也可以直接照抄这份「100% 正确」的 5 行 import(推荐,不会错)
import com.smalo.api.AbstractSmaloMod;
import com.smalo.api.SmaloModAnnotation;
import com.smalo.api.Event;
import com.smalo.loader.ModInfo;
import com.smalo.loader.EventBus;
✅ AI 纯检测(不碰你代码):上传后列出具体哪些 class 是 SDK 重复类清单,弹出「建议剥离 SDK 重复类避免 LinkageError」按钮,你点确认才拆包 + 去重 + 剥离(和第 6 章 6.2 表格第 6 条一致)。AI 不会私自拆你上传的 JAR。
smalo-sdk.jar 里自带的全局事件,模组类只要订阅两个事件 + 存一下坐标,一行依赖都不用额外写,dependencies = { "minecraft" } 就行。
"dependencies": [
{ "modId": "minecraft", "version": ">=1.20.1", "required": true }
]
// 没错,就这一条!渲染/玩家事件都是 SDK 自带,无需声明对启动器或其他模组的依赖
MinimapMod.java(直接照抄,40 行实现右上角小地图)package com.example.minimap;
import com.smalo.api.AbstractSmaloMod;
import com.smalo.api.SmaloModAnnotation;
import com.smalo.api.SmaloModAnnotation.Side;
// ↓↓↓ 小地图用到的两个真实事件,SDK 里都有 ↓↓↓
import com.smalo.api.events.PlayerEvent;
import com.smalo.api.events.RenderEvent;
import com.smalo.loader.EventBus;
import com.smalo.loader.ModInfo;
@SmaloModAnnotation(
modId = "tiny-minimap",
version = "1.0.0",
displayName = "迷你小地图",
description = "右上角显示玩家位置与方向的极简小地图",
author = "YourName",
dependencies = { "minecraft" },
versionRanges = { "[1.20.1,)" },
side = Side.CLIENT,
minLoaderVersion = "[0.1,)"
)
public class MinimapMod extends AbstractSmaloMod {
// 用成员变量存玩家当前位置(小地图中心),0 表示还没进世界
private double px = 0, pz = 0;
private String dim = "none";
@Override
public void onLoad(ModInfo mod, EventBus eventBus) {
// ⭐ 事件 1:玩家一移动,就把新坐标存进成员变量(5 行)
eventBus.subscribe(PlayerEvent.Move.class, e -> {
px = e.getPosX();
pz = e.getPosZ();
dim = e.getDimension();
});
// ⭐ 事件 2:屏幕覆盖层渲染 → 右上角画小地图(20 行,含坐标文字)
eventBus.subscribe(RenderEvent.Overlay.class, e -> {
if ("none".equals(dim)) return; // 还没进世界就不画
long W = e.getScreenWidth();
// 小地图参数:右上角、150x150 像素、距屏幕边距 20px
int size = 150, margin = 20;
int x0 = (int) W - size - margin; // 左上角 X
int y0 = margin; // 左上角 Y
// (真实项目里这里调用启动器提供的 2D 绘制 API:
// drawRect(x0,y0,size,size, 半透明黑背景)
// drawRect(x0+size/2-2, y0+size/2-2, 4,4, 白色玩家点)
// drawString("X:"+(int)px+" Z:"+(int)pz+" "+dim, x0, y0+size+4)
// 上面 3 行绘制调用即可实现「背景方块 + 玩家白心 + 坐标文字」)
log("[Minimap] 绘制于 ("+x0+","+y0+") 玩家 X="+(int)px+" Z="+(int)pz+" 维度="+dim);
});
log("✅ tiny-minimap 小地图模组加载完成(仅监听 PlayerEvent.Move + RenderEvent.Overlay)");
}
}
PlayerEvent.Move(存坐标) + RenderEvent.Overlay(每帧画 HUD)。side = Side.CLIENT,服务端完全不加载。smalo.mod.json(不需要额外依赖){
"schemaVersion": 2,
"modId": "tiny-minimap",
"name": "迷你小地图",
"version": "1.0.0",
"entryClass": "com.example.minimap.MinimapMod",
"supportedMcVersions": ["1.20.1"],
"description": "右上角显示玩家位置与方向的极简小地图",
"authors": ["YourName"],
"tags": ["信息显示","HUD"],
"category": "utility",
"dependencies": [
{ "modId": "minecraft", "version": ">=1.20.1", "required": true }
]
}
以下事件类全部位于 com.smalo.api.events 包,直接 import + 调用 eventBus.subscribe() 即可:
| 事件类(包名 com.smalo.api.events.*) | 内部静态子类 | 常用字段 / getter | 典型用途 |
|---|---|---|---|
| RenderEvent(abstract) | Overlay ⭐ | getScreenWidth() / getScreenHeight() / getPartialTick() | 小地图、血量条、提示文字、HUD 叠加层 |
| 同上 | World | getPartialTick() | 世界 3D 自定义渲染(方块/实体特效) |
| 同上 | Entity | getEntityType() / getPosX/Y/Z() | 给特定实体加发光、自定义绘制 |
| 同上 | Block | getBlockType() / getBlockX/Y/Z() | 方块高亮、破坏粒子替换 |
| 同上 | Gui | getScreenType()(例:"inventory") | 打开某个 GUI 时执行逻辑 |
| 同上 | Post | getPartialTick() | 所有渲染完后的后处理钩子 |
| PlayerEvent(abstract)⭐ | Login | getPlayerName() / getPosX/Y/Z() / getDimension() | 进世界发送欢迎消息、给新手物品 |
| 同上 | Logout | 同上 | 退出存档时自动保存模组数据 |
| 同上 | Move ⭐ | 同上 + getPrevX/Y/Z() / getDistance() | 小地图定位、传送冷却、走路里程成就 |
| 同上 | Chat | getMessage() / setMessage()(可取消) | 聊天敏感词过滤、自定义指令 |
| 同上 | Death | getDeathMessage() / getKiller() | 死亡特效、死亡不掉落、死亡点记录 |
| 同上 | Respawn | getPlayerName() / getPosX/Y/Z() | 重生后自动给装备 |
| WorldEvent(abstract) | Load / Unload / Tick 等 | 维度名、tickCount | 世界加载/卸载时初始化模组数据 |
| EntityEvent(abstract) | Spawn / Death / Hurt 等 | entityType、位置、血量变化 | 自定义掉落物、生物伤害减免 |
| GameLifecycleEvent | — | phase(INIT / TITLE_MENU / JOINED_WORLD 等) | 跟随游戏生命周期初始化资源 |
| ModLoadingEvent | — | modId / loadedModCount | 等待其他模组加载完成后再初始化 |
| RegistryEvent<T> | — | registryType / id / entry | 注册自定义方块、物品、实体 |
| com.smalo.api.audio.SoundEvent | — | soundId / volume / pitch | 播放/拦截自定义音效 |
RenderEvent.*;想要改玩家行为就找 PlayerEvent.*;想要改世界/生物就找 WorldEvent / EntityEvent。90% 的实用模组只要订阅这几类事件就够了。