-
Notifications
You must be signed in to change notification settings - Fork 5
Addon IPhoneApp
首页 | 附属接口文档 | 下一页:IPhonePage
com.november.mcphone.api.client.app.IPhoneApp
一个 App。实现本接口并用 SPI 注册后即出现在手机中。
| 项 | 值 |
|---|---|
| 端 |
仅客户端(renderIcon 的签名含 GuiGraphics) |
| 注册 | SPI:META-INF/services/com.november.mcphone.api.client.app.IPhoneApp
|
| 起于 | 1.0.46 |
| 必须实现 |
getId / getDisplayName / getIconTexture / onPress
|
⚠ 实现类只能在客户端加载,被物品、方块、菜单、网络包或服务端事件顺带引用会使专用服务器启动即崩。 约定放在
yourmod.client包下,详见两端安全。
⚠ 不要继承内建的
PhoneApp基类:它把命名空间写死为mcphone,且位于core包,不属于 API。
| 方法 | 默认实现 | 调用时机 |
|---|---|---|
ResourceLocation getId() |
必须实现 | 目录构建时 |
Component getDisplayName() |
必须实现 | 绘制主屏、商店、管理器时 |
ResourceLocation getIconTexture() |
必须实现 | 由 renderIcon 默认实现读取 |
void onPress() |
必须实现 | 图标被点击且 openPage() 返回 null 时 |
void renderIcon(GuiGraphics, int x, int y, int size, float partialTick) |
绘制 getIconTexture()
|
每帧 |
IPhonePage openPage() |
null |
图标被点击时 |
int getBadgeCount() |
0 |
每帧,每个图标各一次 |
void onUninstall() |
空 | 玩家卸载该 App 时 |
boolean isSystemApp() |
false |
商店与管理器判定可否卸载时 |
List<RequiredMod> requiredMods() |
List.of() |
由 isAvailable() 默认实现读取 |
List<RequiredMod> companionMods() |
List.of() |
构建「联动 App」页与「关于」页时 |
boolean isAvailable() |
按 requiredMods() 判定 |
目录构建时一次 |
boolean isPreinstalled() |
true |
该 App 首次进入目录时 |
String getVersion() |
"1.0.0" |
详情页 |
String getAuthor() |
"" |
详情页 |
String getDescription() |
"" |
详情页 |
ResourceLocation getId()App 的唯一标识,形如 mymod:calculator。命名空间使用实现方自己的 modid。
id 冲突时,后登记的实现被直接丢弃。
Component getDisplayName()主屏图标下方、商店列表与 App 管理器中显示的名称。使用 Component.translatable 以支持多语言。
ResourceLocation getIconTexture()图标纹理,20×20 PNG-32。由 renderIcon 的默认实现读取;覆盖 renderIcon 后本方法可不再被使用,但仍须实现。
纹理缺失时原版绘制紫黑格,不会崩溃。
void onPress()图标被点击时调用,用于打开独立 Screen 或执行业务逻辑。
openPage() 返回非 null 时本方法不会被调用,但接口仍要求实现——留空即可,或写一条在旧版 MCphone 上的退路。
default void renderIcon(GuiGraphics g, int x, int y, int size, float partialTick)| 项 | 说明 |
|---|---|
| 默认实现 |
getIconTexture() 非 null 时,将整张纹理拉伸到 size×size
|
| 调用频率 | 每帧 |
partialTick |
本帧的插值系数,用于不按整 tick 跳变的平滑动画 |
因每帧调用,图标可以是动态的:覆盖本方法并按时间挑选一帧绘制。
private static final int FRAMES = 8;
private static final int FRAME_MS = 100;
@Override
public void renderIcon(GuiGraphics g, int x, int y, int size, float partialTick) {
int frame = (int) ((System.currentTimeMillis() / FRAME_MS) % FRAMES); // 横向雪碧图
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
g.blit(SHEET, x, y, size, size, frame * size, 0, size, size, size * FRAMES, size);
RenderSystem.disableBlend();
}⚠ 覆盖本方法后须自行开启混合,否则半透明像素被当作不透明绘制。 见坑 · 自绘图标要自己开混合。
资源包无法让图标动起来:皮肤贴图是独立纹理、不进图集,原版 .mcmeta 的动画机制对其无效。为 app/xxx.png 配 .mcmeta 不产生任何效果。动画只能由代码实现。
default IPhonePage openPage() // 默认 return null返回一页画在手机屏幕内部的界面,与内建 App 同等待遇:共用状态栏、导航栏、壁纸与返回键。
返回 null 表示改走 onPress(),由实现方自行 setScreen 跳出手机——仅在界面于 120×200 像素内放不下时才应如此。
接口定义见 IPhonePage。
default int getBadgeCount() // 默认 0主屏图标右上角的角标数,0 表示不绘制,大于 99 显示为 99+。
| 项 | 值 |
|---|---|
| 调用频率 | 每帧,每个图标各一次 |
| 约束 | 只能读取现成的值 |
⚠ 不得在此查询数据库、发送网络包或执行任何需要等待的操作。需要实时数据时自行按时间间隔限流(内建的美西螈 App 即如此)。
public record RequiredMod(String modId, String displayName) {}| 参数 | 说明 |
|---|---|
modId |
模组 id,与 ModList.isLoaded 用的是同一个值 |
displayName |
给玩家看的名字,例如 "Waystones(传送石碑)"
|
displayName 必须写死。不要在运行时从 ModList 查询——需要显示它的时刻,该模组正是没装的那一个。
该记录刻意不含下载链接。
default List<RequiredMod> requiredMods() // 默认 List.of()硬前置:缺失则整个 App 不可用。声明后 isAvailable()、商店的「联动 App」页、「设置 → 关于」的联动模组列表自动生效。
内建 App 一律不用本方法,改用 companionMods():MCphone 自身没有任何前置。本方法保留给附属模组中"缺了对方连加载都不该加载"的情形。
default List<RequiredMod> companionMods() // 默认 List.of()联动声明:本 App 沾了哪个模组的光。两种情形都用它声明:
| 情形 | 例子 | 是否须自己覆盖 isAvailable()
|
|---|---|---|
| 装了对方多一块内容,缺了照常可用 | 「阅读」装了 Patchouli 多几十本手册 | 否 |
| 没装对方就没有内容可给 | 「任务书」缺了 FTB Quests | 是 |
声明后玩家在两处看得到:
- 「设置 → 关于」的联动模组列表:一律列出,并标明装没装。
- 商店的「联动 App」页:仅在该 App 当前不可用时收录,并写明缺哪个模组。App 当前可用时不收录——它没有被任何模组卡住,列出只是噪声。
第二条在 1.8.12 之前不成立:那一页当时只认
requiredMods(),于是"靠某模组撑着、却声明为联动"的 App 在对方缺失时会从主屏、商店与该页同时消失,而那一页存在的全部理由正是回答"我怎么没有这个"。
default boolean isAvailable()该 App 在当前环境下是否存在。返回 false 则主屏与商店中均不出现。
默认实现:遍历 requiredMods(),任一 ModList.get().isLoaded(modId) 为 false 即返回 false。
| 项 | 值 |
|---|---|
| 调用时机 | 仅在目录构建时问一次,此后不复查 |
⚠ 不要放入随时间变化的条件——那属于
onPress()。 ⚠ 仅声明companionMods()而不覆盖本方法,等同于"永远可用"。见坑 · 联动声明不参与可用性判断。
default boolean isPreinstalled() // 默认 truetrue 首次被发现即上主屏;false 进应用商店等待玩家下载。
仅在该 App 首次进入目录时生效,此后以玩家的安装 / 卸载选择为准。
default boolean isSystemApp() // 默认 falsetrue 表示不可被玩家卸载(如「设置」)。管理器中卸载键置灰并写明原因。
default void onUninstall() // 默认空玩家卸载该 App 时调用,用于清理持久化数据。
default String getVersion() // 默认 "1.0.0"
default String getAuthor() // 默认 ""
default String getDescription() // 默认 ""App 详情页与管理器中显示的三行元数据。
玩家可在「设置 → App 管理器 → 该 App」中为其绑定一个键,在世界中按下即开机并直接进入该 App。
实现方不需要注册任何东西,绑定按 App id 走,附属与内建一视同仁。
快捷键与点击图标走完全相同的代码路径:先开机,再调用 openPage() / onPress()。因此 onPress() 中形如
if (Minecraft.getInstance().screen instanceof PhoneScreen ps) 的判断照样成立,无须为快捷键补分支。
绑定存于玩家客户端配置 config/mcphone-client.toml 的 appHotkeys,支持 Ctrl / Shift / Alt 组合键与鼠标键(含侧键),一个组合只能属于一个 App。冲突时界面提示占用者,玩家再按一次同一组合即强制绑定。
⚠ 冲突不被拦死,不要假设某个键一定归你的 App 独占。
(玩家侧说明见设置 → 每个 App 一个快捷键,1.9.2 起。)
首页 | 附属接口文档 | 下一页:IPhonePage