Epsilon 是一个面向 Minecraft 的多加载器(Multi-Loader)客户端工具模组,同时支持 Fabric 和 NeoForge,采用三层架构。
Epsilon/
├── common/ ← 平台无关核心代码(模块、事件、图形、工具类)
├── fabric/ ← Fabric 加载器适配层
├── neoforge/ ← NeoForge 加载器适配层
├── buildSrc/ ← Gradle 自定义插件(multiloader-common、multiloader-loader)
└── docs/ ← 文档
common/中的代码不能调用任何 ModLoader 的 API- 需要加载器 API 时,在
fabric/或neoforge/子项目中实现,通过 compat 接口传入 common common/中的 Java 源码会被 Gradle 自动共享到 fabric/ 和 neoforge/ 的编译路径- 编写前必须查阅 Minecraft 源码!
编写前必须查阅 Minecraft 源码!
绝对不要基于训练数据中的知识判断版本信息。 Minecraft 版本迭代很快,任何关于最新版本号、版本兼容性、API 变更、 迁移步骤的判断都必须通过查阅官方文档或在线搜索确认。即便是"当前最新版本是什么"这种看似简单的问题,也优先查文档而非依赖记忆。 任何关于调用哪个方法、哪个事件、哪个类的判断都必须基于对当前版本 Minecraft 代码的直接查阅,而非依赖过时的训练数据。
源代码位于:common/build/moddev/artifacts/vanilla-<游戏版本>-sources.jar
如果不存在,执行:
./gradlew :common:downloadAssets
./gradlew :common:createMinecraftArtifacts查阅后解压:
mkdir -p reference && unzip common/build/moddev/artifacts/vanilla-*-sources.jar -d reference/vanilla/当前 Minecraft 版本、Fabric API、NeoForge 版本等信息均在 gradle.properties 中定义,编写代码时参考此文件获取准确的版本号。
multiloader-common.gradle.kts— common 子项目的共享配置:JDK 工具链、Maven 仓库、资源处理、发布配置multiloader-loader.gradle.kts— fabric/neoforge 子项目的共享配置:自动关联:common源码和资源
所有平台无关代码位于 common/src/main/java/com/github/epsilon/:
| 包名 | 用途 |
|---|---|
addon/ |
Addon 基类、注册事件、Bootstrap 工具 |
assets/ |
i18n 翻译、配置文件迁移、资源持有者 |
events/ |
自定义事件总线、27 种事件类型 |
graphics/ |
Lumin Graphics 渲染框架 |
gui/ |
点击 GUI(Panel、Dropdown、HUD 编辑器) |
managers/ |
各种管理器(Addon、Config、Module、Rotation 等) |
mixins/ |
37 个 Mixin 注入类 |
modules/ |
Module 基类、Category 枚举、HudModule |
settings/ |
Setting 基类与 8 种设置类型 |
utils/ |
工具类(client、combat、math、network、player、render、timer、rotation、world) |
所有功能模块继承 Module,参考现有模块(如 common/.../modules/impl/combat/KillAura.java)。
public class MyModule extends Module {
public static final MyModule INSTANCE = new MyModule();
private MyModule() {
super("My Module", Category.COMBAT); // 名称 + 分类
}
// -- Setting DSL --
// Boolean 设置
private final BoolSetting enabled = boolSetting("Enabled", true);
// Boolean 设置 + 可见性依赖
private final BoolSetting advanced = boolSetting("Advanced", false, () -> enabled.getValue());
// Integer 设置 (name, default, min, max, step)
private final IntSetting speed = intSetting("Speed", 5, 1, 10, 1);
// Integer 设置 + 依赖
private final IntSetting range = intSetting("Range", 4, 1, 64, 1, () -> advanced.getValue());
// Double 设置 (name, default, min, max, step)
private final DoubleSetting reach = doubleSetting("Reach", 3.0, 1.0, 6.0, 0.1);
// Enum 设置
private enum Mode { Fast, Slow }
private final EnumSetting<Mode> mode = enumSetting("Mode", Mode.Fast);
// Enum 设置 + 依赖 + onChanged 回调
private final EnumSetting<Mode> mode2 = enumSetting("Mode 2", Mode.Slow, () -> enabled.getValue(), newMode -> { /* ... */ });
// Color 设置 (name, default, allowAlpha: boolean)
private final ColorSetting color = colorSetting("Color", new Color(255, 0, 0), true);
// Keybind 设置 (name, defaultKeyCode: int)
private final KeybindSetting key = keybindSetting("Key", -1);
// Button 设置 (name, action: Runnable)
private final ButtonSetting reset = buttonSetting("Reset", () -> { /* ... */ });
// String 设置
private final StringSetting text = stringSetting("Text", "default");
// -- 生命周期 --
@Override
protected void onEnable() {
// 模块启用时调用
}
@Override
protected void onDisable() {
// 模块禁用时调用
}
// -- 事件监听 --
@EventHandler
private void onTick(TickEvent.Pre event) {
if (nullCheck()) return; // 检查 mc.player == null || mc.level == null
// 处理每 tick 逻辑
}
@EventHandler
private void onRender3D(Render3DEvent event) {
// 3D 渲染逻辑
PoseStack stack = event.getPoseStack();
}
}- 单例模式:每个 Module 使用
public static final MyModule INSTANCE = new MyModule(),构造函数私有 - 私有构造函数:防止外部实例化
- nullCheck():事件处理方法中先调用
nullCheck()确保世界中存在玩家 - Setting 字段:Setting 必须声明为类的实例字段,构造时初始化
- @EventHandler 注解:事件监听方法必须用
@EventHandler标记,且必须是实例方法(非 static),访问修饰符任意 - 事件方法签名:必须
void返回,只有一个参数(事件类型),参数类型不能是原始类型 - 注册:内部模块在
ModuleManager.initModules()列表中注册;Addon 模块通过EpsilonAddon.registerModule()注册 - i18n 翻译:需要为模块名称和设置名称添加 i18n 翻译,详见后文 i18n 章节
Setting 的第四个参数(如果有)是一个 Setting.Dependency 函数式接口(() -> boolean),返回 false 时该设置在 GUI 中隐藏。
private final BoolSetting enableEsp = boolSetting("Enable ESP", true);
private final ColorSetting espColor = colorSetting("ESP Color", Color.WHITE, () -> enableEsp.getValue());
// 使用已有的 EnumSetting/BoolSetting::getValue 引用作为依赖也可用 lambda 实现 cross-setting 引用(A 依赖 B,而 B 也在同一个类中):
private final BoolSetting advanced = boolSetting("Advanced", false);
// 使用 lambda 延迟引用,解决声明顺序问题
private final IntSetting threshold = intSetting("Threshold", 50, 0, 100, 1, () -> advanced.getValue());注意:EpsilonAddon 的 Setting 不支持带 onChanged 参数的 DSL 变体(与 Module 不同)。
private final SettingGroup sgCombat = settingGroup("Combat");
private final BoolSetting autoAttack = boolSetting("Auto Attack", true).group(sgCombat);getKeyBind()/setKeyBind(int)— 键位代码(GLFW key code)BindMode.Toggle— 按下切换开关(默认)BindMode.Hold— 按住启用,松开禁用
Epsilon 使用自定义事件总线,与 Minecraft 原生事件系统独立。
- 订阅:
EventBus.INSTANCE.subscribe(object)/EventBus.INSTANCE.subscribe(Class) - 取消订阅:
EventBus.INSTANCE.unsubscribe(object)/EventBus.INSTANCE.unsubscribe(Class) - 发布:
EventBus.INSTANCE.post(event) - Lambda 工厂注册:
EventBus.INSTANCE.registerLambdaFactory("com.github.epsilon", factory)— 已由 EpsilonCommon.init() 自动注册
Module 启用时自动 subscribe(this),禁用时自动 unsubscribe(this)。
| 事件类 | 描述 |
|---|---|
TickEvent.Pre / TickEvent.Post |
每 tick 前后 |
Render2DEvent.Level / Render2DEvent.HUD |
2D 渲染(含 GuiGraphics) |
Render3DEvent |
3D 渲染(含 PoseStack) |
PacketEvent.Send / PacketEvent.Receive |
网络包收发 |
KeyPressEvent |
按键按下/释放 |
MousePressEvent |
鼠标按键 |
AttackEntityEvent |
攻击实体 |
ClickEvent |
点击 |
JumpEvent |
跳跃 |
CollisionEvent |
碰撞检测 |
SlowdownEvent |
减速(灵魂沙等) |
StrafeEvent |
侧移 |
TravelEvent |
移动 |
VelocityEvent |
击退 |
RotationAnimationEvent |
旋转动画 |
AfterRotationEvent |
旋转完成标记事件(通过 RotationManager.getYRot()/getXRot() 获取角度) |
AttackYawEvent |
攻击偏航角 |
SendPositionEvent |
发送位置包 |
SwingHandEvent |
挥手 |
RaytraceEvent |
射线追踪 |
KeyboardInputEvent |
键盘输入 |
| ... | 等 27 个事件 |
@EventHandler(priority = EventPriority.HIGH) — 优先级:HIGHEST > HIGH > MEDIUM > LOW > LOWEST
支持直接传入 int 值(如 priority = -1000)。数值越大越先执行,数值越小越晚执行。EventPriority 常量映射:HIGHEST=200, HIGH=100, MEDIUM=0, LOW=-100, LOWEST=-200。
实现 Cancellable 接口的事件可以通过 event.setCancelled(true) 取消。取消后,后续低优先级监听器不再执行。
Mixin 类位于 common/src/main/java/com/github/epsilon/mixins/。
common/src/main/resources/epsilon.mixins.json— common 层 mixinsfabric/src/main/resources/epsilon.fabric.mixins.json— Fabric 专有 mixinsneoforge/src/main/resources/epsilon.neoforge.mixins.json— NeoForge 专有 mixins
common/src/main/resources/epsilon.accesswidener— Fabric 访问拓宽common/src/main/resources/META-INF/accesstransformer.cfg— NeoForge 访问转换
- 不要凭空猜测 API。优先查阅官方文档:
- NeoForge 文档:https://docs.neoforged.net/
- Porting Primers(中文):https://gu-zt.github.io/Porting-Primers/
- Mixin 文档:https://wiki.fabricmc.net/zh_cn:tutorial:mixin_introduction
- Mixin 示例:https://wiki.fabricmc.net/zh_cn:tutorial:mixin_examples
- Mixin 类命名:
Mixin<目标类名>(如MixinLocalPlayer) - 优先使用
@Inject+@WrapOperation而非@Overwrite - 事件发布通常在 Mixin 的
@Inject回调中进行:EventBus.INSTANCE.post(new SomeEvent(...)) - 如果遇到不熟悉的类/方法签名,必须查阅文档确认
initModules()— 注册所有内置模块(在EpsilonCommon.init()中调用)registerAddonModule(addonId, module, translateComponent)— 注册 Addon 的模块getModules()— 获取所有已注册模块列表
registerAddon(addon)— 注册单个 addon(含重复 ID 校验)registerAddons(Iterable)— 批量注册setupAddons()— 依次初始化所有 addon 的 i18n 并调用onSetup()(含异常隔离)getAddons()— 获取已注册 addon 列表
| 管理器 | 职责 |
|---|---|
ConfigManager |
配置序列化/反序列化、自动保存 |
RotationManager |
旋转角度管理(优先级队列) |
HealthManager |
实体生命值缓存 |
TargetManager |
目标选择(FOV、距离、实体类型过滤) |
FriendManager |
好友管理 |
SoundManager |
音效播放 |
ServerboundPacketManager / ClientboundPacketManager |
网络包管理 |
RotationManager 管理玩家视角旋转,支持优先级队列、平滑插值和射线追踪偏移。
核心概念:
setRotations()设置目标旋转角度后,RotationManager 每 tick 自动平滑旋转,并在SendPositionEvent中将旋转角度写入发包- 旋转完成后(与真实角度差值 < 1 度)自动
active = false - 多个模块可同时调用
setRotations(),高优先级覆盖低优先级 - 旋转过程中每 tick 发布
AfterRotationEvent,可在其中做 raycast 检测确认旋转已对准目标后再执行操作
主要 API:
// 获取当前旋转角度
float yaw = RotationManager.INSTANCE.getYRot(); // 水平角度
float pitch = RotationManager.INSTANCE.getXRot(); // 垂直角度
Vector2f current = RotationManager.INSTANCE.getRotation(); // Vector2f(yaw, pitch)
// 设置旋转(默认优先级 Medium)
RotationManager.INSTANCE.setRotations(new Vector2f(yaw, pitch), rotationSpeed);
// 设置旋转 + 优先级
RotationManager.INSTANCE.setRotations(new Vector2f(yaw, pitch), rotationSpeed, Priority.High);
// 设置旋转 + 射线追踪偏移(用于绕过反作弊)
RotationManager.INSTANCE.setRotations(rotations, rotationSpeed, rayTraceFunction, Priority.High);
// 设置旋转 + 回调(旋转每 tick 平滑后执行,用于 raycast 检测 + 实际操作)
RotationManager.INSTANCE.setRotations(rotations, rotationSpeed, null, Priority.High, () -> {
if (RaytraceUtils.overBlock(new Vector2f(getYRot(), getXRot()), side, blockPos, true)) {
// 执行放置/攻击操作
}
});
// 检查旋转是否激活 / 已完成
boolean active = RotationManager.INSTANCE.isActive();
boolean done = RotationManager.INSTANCE.isDone();优先级(Priority):
| 优先级 | 值 | 用途 |
|---|---|---|
Lowest |
-200 | 预旋转(Pre Rotation) |
Low |
-100 | 低优先级旋转 |
Medium |
0 | 默认优先级 |
High |
100 | 攻击/放置等即时操作 |
Highest |
200 | 最高优先级 |
回调使用模式:
推荐通过 setRotations 的 callback 参数实现「旋转→瞄准→操作」的解耦流程,无需手动订阅 AfterRotationEvent。
// 设置旋转 + callback,在旋转对准后执行操作
RotationManager.INSTANCE.setRotations(rotation, speed, null, Priority.High, () -> {
// 使用 getYRot()/getXRot() 获取当前平滑后的旋转角度
if (RaytraceUtils.overBlock(
new Vector2f(RotationManager.INSTANCE.getYRot(), RotationManager.INSTANCE.getXRot()),
side, blockPos, true)) {
mc.gameMode.useItemOn(mc.player, hand, hitResult);
}
});关键设计要点:
- callback 在
RotationManager.onTick()(priority=-1000) 中每 tick 执行一次,直到被新的setRotations()覆盖或旋转完成(active=false) - callback 内使用
RotationManager.INSTANCE.getYRot()/getXRot()获取当前平滑后的旋转角度,而非使用AfterRotationEvent的字段(已移除) - callback 应防止重复执行(通过 boolean 标记),避免同一操作触发多次
AfterRotationEvent现在是纯标记事件(无字段),仅供需要知道旋转已更新的模块订阅- raycast 函数用于在平滑旋转时加入随机偏移绕过反作弊,如果偏移后射线被遮挡则重新计算偏移方向
- 模块禁用时应清理 pending 状态并
InvUtils.swapBack()恢复物品栏
Lumin Graphics 是 Epsilon 的轻量高性能渲染框架,位于 common/.../graphics/。
| Renderer | 用途 |
|---|---|
RectRenderer |
矩形渲染(纯色/渐变) |
RoundRectRenderer |
SDF 圆角矩形(Shader 实现,支持独立四角半径) |
RoundRectOutlineRenderer |
圆角矩形边框 |
TextureRenderer |
纹理批量渲染 |
ShadowRenderer |
阴影渲染 |
TriangleRenderer |
三角形渲染 |
TextRenderer |
文本渲染(静态字体 + TTF) |
TtfTextRenderer |
TTF TrueType 字体渲染(Atlas 批处理) |
- 线程安全:Renderer 必须在渲染线程初始化
- 推荐初始化方式:使用
Suppliers.memoize延迟初始化
private final Supplier<RectRenderer> rectRenderer = Suppliers.memoize(RectRenderer::create);- 使用模式一:即时绘制并清理
rectRenderer.get().addRect(10, 10, 100, 100, Color.WHITE);
rectRenderer.get().drawAndClear(); // 等价于 draw() + clear()- 使用模式二:缓冲区复用(内容不变时更高效)
// 初始化时添加顶点一次
rectRenderer.get().addRect(10, 10, 100, 100, Color.WHITE);
// 渲染循环中每帧只 draw 不清除
rectRenderer.get().draw(); // GPU 数据已存在,无需重新上传- 关闭:关闭时释放 GPU 资源
rectRenderer.get().close();同一帧内,不要在 draw() 之后再 clear() 然后继续 draw() — 这会导致帧内多次缓冲区分配,破坏 In-Flight 优化。如需多次清空-绘制循环,请创建新的 Renderer 实例。
静态字体加载器:StaticFontLoader.OSAKA_CHIPS、StaticFontLoader.MINECRAFTIA 等。
TextRenderer textRenderer = textRendererSupplier.get();
textRenderer.addText("Hello", x, y, scale, color, StaticFontLoader.OSAKA_CHIPS);
float width = textRenderer.getWidth("Hello", scale);
float height = textRenderer.getHeight(scale);
textRenderer.drawAndClear();BlurShader.INSTANCE.render(x, y, width, height, radius, strength)— 背景模糊FXAAShader— FXAA 抗锯齿FilterShader— 色彩滤镜
完整 HUD 渲染示例:common/.../modules/impl/hud/WatermarkHUD.java
TranslateComponent— 翻译组件接口EpsilonTranslateComponent.create("modules", "kill_aura")→ key:epsilon.modules.kill_auraDefaultTranslateComponent.create("example_addon.settings.enable_particles")→ key:example_addon.settings.enable_particles- 翻译 key 生成在
I18NFileGenerator.generate("epsilon-empty-i18n.json")输出空模板
- 内置模块:
epsilon.modules.{moduleNameLowerCase}(如epsilon.modules.kill_aura) - Addon 模块:
{addonId}.modules.{moduleNameLowerCase}(如example_addon.modules.my_module) Module.getTranslatedName()— 获取翻译后的显示名称- Setting 翻译:
{addonId}.settings.{settingNameLowerCase} - SettingGroup 翻译:
{addonId}.settings.{groupNameLowerCase}
- 使用 Java 编写,遵循项目现有代码风格
- 私有构造函数 + 单例
INSTANCE模式用于 Module 和 Addon - 所有注释用中文
- Logger 通过
Constants.LOGGER获取 - Minecraft 实例通过
Constants.mc或 Module 中的this.mc获取 - 编译环境使用 JDK 25
- NeoForge 官方文档:https://docs.neoforged.net/
- NeoForge 迁移入门:https://docs.neoforged.net/primer/docs/
- Porting Primers(中文):https://gu-zt.github.io/Porting-Primers/
- Mixin 文档:https://wiki.fabricmc.net/zh_cn:tutorial:mixin_introduction
- Fabric API 文档:https://docs.fabricmc.net/develop/
- 项目自身 graphics 文档:
common/src/main/java/com/github/epsilon/graphics/README.md