diff --git a/docs/agents/MESSAGES.md b/docs/agents/MESSAGES.md index b66598d..29533e9 100644 --- a/docs/agents/MESSAGES.md +++ b/docs/agents/MESSAGES.md @@ -1,5 +1,29 @@ # MESSAGES.md — Agent 交流墙(只追加,不改不删) +## [2026-10-25 17:52] Codex — 最新公开MTR4.1内部UI研究完成 + +- 用户要求读取最新4.1内部UI写法。重新ls-remote核对stonecutter HEAD=f05d99efab6f27b3868913438fce2da381e5798f,gradle.properties仍4.1.0-beta.2/Elementa743/UniversalCraft497;不冒称4.1正式版或发布包精确源码。新增docs/plans/mtr-4.1-ui-research.md并更新UI设计。 +- 采纳约束式布局、parent/child与稳定ID保留展开、释放判点击/拖动、重叠候选、父页/关闭提交;站点展开后各线路ETA默认可见。排除管理UI颜色/名称身份合并、移开关闭、全行draw/onTick重建和根WindowScreen直接嵌宿主。增加拖出再回原点不点击及刷新后滚动/焦点保持。 +- 直接复用有真实打包边界:NF shadow/relocate gg.essential与Kotlin,Fabric include;最新源码版本目录仅1.21.1/1.21.4。4.0六目标兼容不能靠改import证明,独立设置页复用仅候选,地图浮层先保持轻量共享方案;公共API维持纯JDK。 +- UTF-8/相对链接/代码围栏、全部MTR固定SHA路径、研究版本与依赖、diff检查通过。未复制实现/素材,未修改功能/版本/依赖、未构建或游戏实测。更新同一PR #1;任务释放。 + + +## [2026-10-25 17:43] Codex — 1.5.3游戏内UI调查与设计补充 + +- 用户要求增加游戏内UI优化设计并开始调查。新增docs/plans/v1.5.3-ingame-ui.md,联动主计划与API草案:共享图层菜单、固定站点卡片、单一提示、GUIpx拾取/候选消歧、三语言/键盘/小窗口、覆盖状态、外部聚焦所有者与用户暂时全图覆盖;没有加入特定导航模组适配或任意UI注入。 +- 源码发现Xaero enabled隐藏绘制但点击未同门控、按钮/地图双提示路径,JM原生标题/车辆自绘提示并存及玩家维度地标集合,设置缺项目Screen、语言仅9个列车键;单机保持运行仍仅依赖车辆层且screen精确匹配,站点ETA需新兴趣上下文。以上未本轮实机复现,不等同已修复。 +- 公共JM API固定4a57dee(1.21.1_2.0.0),有可取消PRE点击/拖动但不是完整release/wheel/key桥;兼容JM5现有drawMap mixin需独立验证。参考Create 7ff2760的tooltip仲裁/生命周期,不复制素材。新增U0–U4及六平台视觉/输入/暂停/费用验收。 +- UTF-8/Markdown相对链接/源码固定路径/语言键核对/git diff --check通过;未改生产代码/版本,未构建/启动游戏/发布。工作在docs/1.5.3-station-board-api更新PR #1,准备任务释放,实施仍待领。 + + +## [2026-10-25 17:34] Codex — 1.5.3站点到站板与统一API设计准备完成 + +- 用户指定目标1.5.3。仅完成公共源码核对与设计,未改生产代码/版本属性,未构建或发布。计划docs/plans/v1.5.3-station-board-extension-api.md,契约docs/api/overlay-api-v1-draft.md;草案并非已有API。 +- 单击站点展开固定到站板,默认折叠/按兴趣查询,按线路/方向/目的地/站台展示同源PIDS预测。原生缓存首次空/数量限制不能解释为完整无班次;全图目标维度由新服务端能力执行原生ArrivalsRequest。站点ETA恢复,车体悬停ETA/距离仍暂停。 +- API提供只读目录、可关闭区段/样式会话及同源站点订阅,Java17纯JDK契约;不加入特定第三方依赖或适配器。避免目前去重MapRoute充当有序导航路径;明确停站occurrence/路径歧义、半轨原生距离、复合rail ID、异步latest-wins、错代与生命周期。 +- 证据固定MTR f05d99e、Core 032baaf、Navigation df53387、howtogo 2b57bac。Core主线不是发布包内精确版本,P0必须实际依赖编译/原生对照。UTF-8/相对链接/固定源码SHA和路径/git diff --check通过,无游戏/性能实测。释放准备任务,实施任务待领。 + + ## [2026-10-22 22:42] Codex-CI — CI/CD六平台与发布演练验收 diff --git a/docs/agents/STATE.md b/docs/agents/STATE.md index b4b560b..94a13f9 100644 --- a/docs/agents/STATE.md +++ b/docs/agents/STATE.md @@ -7,11 +7,22 @@ - **v1.5.2 正式版(main)**:TSC 全网实时车厢以双锚点显示为抽象纯 2D 符号,固定轨道→线路→车厢;客户端无行车模拟,附近仅为无动态服务端能力时的回退。GitHub [v1.5.2](https://github.com/teamCreating/MTR-Map-Overlay/releases/tag/v1.5.2) 已正式发布并设为 Latest。 - 项目标识:MTR Map Overlay;mod ID `mtrmap`;Java 包 `io.github.teamcreating.mtrmap`;客户端命令 `/mtrmap` -- 最后更新:2026-10-22 18:21,操作者:Codex -- 版本管理:按用户要求固定 1.5.2,只有用户明确指示时才更改版本号;禁止自动递增。 +- 最后更新:2026-10-25 17:52,操作者:Codex +- 版本管理:用户已指定下一目标1.5.3;本轮完成设计准备,代码属性/已发布附件仍为1.5.2,进入实现时统一调整双分支。禁止自行递增其他版本。 - main 保留 NeoForge/Fabric MC 1.21.1;`compat/mtr-4.0-mc-1.20` 另提供 MC 1.20.1/1.20.4 Forge/Fabric + MTR 4.0.5 构建。v1.5.2 发布包含六个 JAR,按精确 Minecraft 版本及加载器选择。 - 中英文 README 已同步六个附件的依赖矩阵、官网文档及 Modrinth/CurseForge、Ko-fi/爱发电入口;仅修改文档及 badge 资产。 +## 1.5.3准备(设计完成,功能未实现) + +- 2026-10-25 17:52,Codex:新增[MTR4.1内部UI研究](../plans/mtr-4.1-ui-research.md),远端stonecutter HEAD仍f05d99e/4.1.0-beta.2;新Elementa+UniversalCraft与旧Screen并存。借鉴约束式布局、稳定键保留展开、释放时点击/拖动判定、候选列表及父页/保存逻辑;不直接套管理列表的颜色名称分组、移出关闭、全行遍历或WindowScreen根节点。NF重定位gg.essential/Kotlin而Fabric include,直接调用内部控件须实际依赖和旧4.0验证;公共API不泄漏这些类型。UI设计补拖出再拖回不点击、刷新保持展开/焦点、展开站点后各线ETA默认可见。文档/固定源码路径/版本检查通过;本轮无实现/依赖变更、构建或实机测试,更新PR #1。 + +- 2026-10-25 17:43,Codex:补充[游戏内UI调查与设计](../plans/v1.5.3-ingame-ui.md):共享图层菜单、固定站点卡片、单一悬停/GUIpx命中、候选消歧、三语言/键盘/小屏布局、分层数据状态及外部聚焦提示/暂时全图覆盖。源码发现Xaero隐藏按钮点击/双提示路径、仅车层维持单机运行、JM查看维度与地标集合差异;属于源码调查,未本轮实机复现。新增U0–U4验收,尤其车层关闭仍需本站ETA推进与JM5/6输入桥。API草案补所有者显示与用户覆盖状态,无特定模组适配或任意UI注入接口。已核对现有双分支源码和固定JM公开API;文档/引用/语言键核对通过,未改生产代码/版本/发布,也没有新UI的构建或游戏测试。更新同一PR #1。 + +- 2026-10-25 17:34,Codex:新增[站点到站板/扩展API计划](../plans/v1.5.3-station-board-extension-api.md)与[公共API v1草案](../api/overlay-api-v1-draft.md)。单击站点展开固定面板、默认折叠/按需订阅;各线路按方向/目的地和站台区分,复用MTR/TSC给PIDS的原生预测,不新增模拟。站点ETA纳入1.5.3;列车悬停ETA/下一站距离仍暂停。 +- 统一MapReadApi/MapViewApi/StationBoardApi:稳定只读目录、精确区段和声明式样式、独立可关闭会话;不主动适配MTR-Navigation/howtogo。当前去重绘制缓存不足以表达有序乘车区段,须新增原生ID/停站occurrence/路径能力并验证歧义;保留静态v8/车辆v1,新增可选能力。 +- P0必须核对实际4.1.0-beta.2/4.0.5依赖、原生PIDS对照及重复平台/多路径夹具;源码Core主线不能等同已发布MTR内嵌版本。原生到站候选限额不保证每线路完整,必须显式LIMITED;目标维度查询由服务端原生操作完成。 +- 本轮只验证文档UTF-8/相对链接/源码固定SHA和文件路径及git diff --check。没有新功能、API产物、构建或游戏实测,也未发布;下一步见任务看板P0实施项。 + ## 平台与依赖 | 项 | 值 | diff --git a/docs/agents/TASKS.md b/docs/agents/TASKS.md index 67ed030..ddde26e 100644 --- a/docs/agents/TASKS.md +++ b/docs/agents/TASKS.md @@ -6,9 +6,13 @@ ## 进行中 +### [待领,1.5.3实施] 原生能力验证→统一API→站点到站板 +- 准备设计已完成,见[实施计划](../plans/v1.5.3-station-board-extension-api.md)与[API草案](../api/overlay-api-v1-draft.md)。先P0验证实际MTR4.1/4.0依赖、PIDS到站对照、路径/停站occurrence映射,并做[UI设计](../plans/v1.5.3-ingame-ui.md)U0宿主输入/暂停桥验证;再按P1–P4交付只读目录、精确区段/样式会话、站点固定展开ETA与六平台验证。接口未实现,不能直接调用草案示例。 +- 不主动适配第三方导航模组,不恢复车体悬停ETA/距离,不新增模拟。用户指定目标1.5.3;本轮代码版本仍1.5.2,开始功能实现时同步双分支版本与协作文档。 + ### [待领,性能/兼容后续] 全网列车大网与复杂网络性能 - 2026-10-21 20:09,Codex:全图简化锚点功能、双地图/加载器接入、异常边界和NeoForge单列远处运行验证已完成,见[验证记录](../validation/global-trains.md)。仍需真实1000/5000列、多观看者、复杂网络、高速小半径、跨维度的性能/复杂网络矩阵(六平台小网格功能回归已完成);不能把codec大小当成FPS或总出站实测。 -- 继续使用TSC全网实测锚点,客户端无模拟/沿轨求解;版本1.5.2/静态v8/动态v1/纯2D保持。距离/ETA暂停,旧stash不恢复。 +- 继续使用TSC全网实测锚点,客户端无模拟/沿轨求解;版本1.5.2/静态v8/动态v1/纯2D保持。车体悬停距离/ETA暂停,站点ETA已纳入1.5.3计划,旧stash不恢复。 ### [功能] 非 TRAIN 轨道渲染开关 - `TrackSampler` 目前只采样 TRAIN 模式轨道。增加配置项按 TransportMode 过滤(BOAT/CABLE_CAR 可选)。 @@ -23,6 +27,12 @@ ## 完成 +- [完成] MTR4.1内部UI源码研究 — Codex,2026-10-25 17:52,见本条所在提交。核对最新4.1公开分支HEAD/版本,追踪Elementa/旧Screen、约束布局/分组/输入/文本/关闭提交及NF重定位与Fabric include;新增研究并补1.5.3 UI设计,文档与固定源码引用验证通过。未直接复用内部控件、未修改生产功能/依赖/版本、未实机测试,更新PR #1。 + +- [完成] 1.5.3游戏内UI优化调查与设计 — Codex,2026-10-25 17:43,见本条所在提交。核对主线/兼容UI及Create/JourneyMap公开源码,补布局/输入/悬停/设置/状态/聚焦/暂停设计与U0–U4验收;链接、固定源码、语言键核对及diff检查通过。仅调查设计,无生产代码/版本或发布变更,未启动游戏。更新PR #1。 + +- [完成] 1.5.3站点ETA与统一扩展API准备 — Codex,2026-10-25 17:34,见本条所在提交。核对MTR/TSC/Navigation/howtogo固定公共源码,提交交互、原生数据链、目录/区段/样式/ETA契约及P0–P4验收计划;文档和源码引用检查通过。只完成准备设计,未实现/构建/发布,二进制版本仍1.5.2。 + - [完成] GitHub Actions CI/CD — Codex-CI,2026-10-22 22:42。双分支六目标Runner构建与CI Gate、18项工具回归、远端产物复核、六包Candidate、Nightly完整验证/无变更跳过、Snapshot六包与Publish只读演练通过;Environment已配置,规则在本验收提交推送后启用。RELEASE_TOKEN按用户要求稍后配置,不触发正式发布。实现提交main 9ea00e9、compat 7f63367,证据见docs/validation/ci-cd.md。 diff --git a/docs/api/overlay-api-v1-draft.md b/docs/api/overlay-api-v1-draft.md new file mode 100644 index 0000000..657c1a2 --- /dev/null +++ b/docs/api/overlay-api-v1-draft.md @@ -0,0 +1,136 @@ +# Overlay API v1 草案(1.5.3目标) + +状态:2026-10-25设计草案,**没有已发布的API JAR,下面不是现有可编译用法**。背景/源码及实施门见[1.5.3计划](../plans/v1.5.3-station-board-extension-api.md)。 + +## 1. 契约 + +- 包:`io.github.teamcreating.mtrmap.api`;API主版本与模组版本独立。拟议compileOnly坐标为 `io.github.teamcreating:mtrmap-api:1.0.0`,尚未发布。 +- Java17、不可变值对象;不依赖任何MC/loader/Core/地图/导航模组类。API类由Overlay提供,第三方不得打包重复副本。 +- `MtrMapApi.findClient()` 返回Optional;安装/加载检查后才能引用此类。客户端提供门面,专服不加载客户端实现。注册/更新/close在客户端线程执行,异步解析结果切回该线程交付。 +- `apiVersion()` 与 `capabilities(WorldRef)` 区分契约版本及服务器数据能力;公共API存在不代表服务端支持ETA/路径解析。 +- 全部结果带状态,不用空集合同时表示无数据、尚未同步和真实空网。 + +## 2. 身份与目录 + +```java +record WorldRef(UUID serverEpoch, String dimensionId) {} +record StationRef(WorldRef world, String nativeId) {} +record PlatformRef(WorldRef world, String nativeId) {} +record RouteRef(WorldRef world, String nativeId) {} +record RailRef(WorldRef world, String nativeId) {} +record StopOccurrence(RouteRef route, long catalogRevision, + int ordinal, PlatformRef platform) {} +``` + +dimensionId采用MC标准 `namespace:path`,内部MTR斜杠写法只在适配器转换。station/platform/route的nativeId为规范化无符号hex字符串;rail保留Core的复合getHexId(有序端点坐标hex组合),不能将它解析成单个long。不能填显示名、`station:`展示前缀或线路颜色。各Ref须属于同一有效世界,不允许跨服重用。 + +`MapReadApi.catalog(world)` 返回Station/Platform/RouteStop关系、来源FULL_SERVER/NEARBY、revision/完整性及能力。`tracks(world, query)`按所需rail集合/有界范围返回只读几何快照,不提供live数组。返回真实XYZ;未知Y明确标未知,不能补一个高度假装真实。 + +目录revision仅在本API内有意义;第三方自己的hash不能直接充当catalogRevision。遇到重名、循环重复平台,必须使用本目录的StopOccurrence;索引失效后重新映射。 + +## 3. 范围选择与路径解析 + +```java +record RailSpan(RailRef rail, double startFraction, double endFraction) {} +record RouteSection(RouteRef route, StopOccurrence from, + StopOccurrence to, Optional pathVariant) {} + +// 缩略声明;具体builder/错误类型在P0验证后冻结。 +interface MapReadApi { + CatalogResult catalog(WorldRef world); + GeometryResult tracks(WorldRef world, TrackQuery query); + CompletionStage resolve(List sections); +} +``` + +选择器支持整条路线、已解析的路线区段或明确RailSpan。RailSpan允许起止反向,fraction在[0,1],表示该rail原生弧长的归一化距离。0.2→0.6只显示中间40%,不是整条轨道包围盒命中就全画。路线选择另保留routeId供同轨不同色带使用;几何可见与线路色带可见是不同集合。 + +目录须明确返回rail的canonical起止端点及原生长度参数约定;0对应canonical起点,不能随采样数组/渲染方向翻转。此长度遵循Core原生路径距离单位,不另按三维折线重算。缺少可验证的原生距离映射时返回UNAVAILABLE,不把XZ采样长度冒充原生长度。 + +RouteSection包含真实停站occurrence、可选原生路径variant和明确的环线跨圈意图;缩略声明未展开wrap字段。ResolveResult为EXACT、AMBIGUOUS、UNAVAILABLE、STALE或INVALID。EXACT才返回带几何修订的有序片段;多条原生路径不能静默选择第一条。没有可靠路径时不能退成站点直线。 + +resolve只读/截取现有原生path,不提供新的导航寻路器、ETA算法或客户端模拟。RouteSection能力在真实夹具通过后开放,未通过前仍可使用明确RailSpan;不能用整线过滤冒充精确区段支持。 + +## 4. 显示会话和样式 + +```java +interface MapViewApi { + ViewSession open(ViewOwner owner, ViewPurpose purpose, int priority); +} +interface ViewSession extends AutoCloseable { + CompletionStage replace(ViewRequest request); + void clear(); + void close(); +} +``` + +`ViewOwner` 为 `modId:sessionKey`,purpose为FOCUS或DECORATION。replace提交完整不可变请求,验证后原子安装;新请求失败不清除仍有效的旧请求,并向调用者返回原因。拓扑变化导致旧请求也失效时停止使用旧区段、回退下一有效视图/底图并通知调用方,不继续画错误路径。 + +同一句柄的replace按提交序号执行latest-wins:较旧异步解析完成不能覆盖较新请求;clear/close使未完成请求失效。close后不再交付监听通知,尚未完成的future以明确的取消/失效结果结束。查询与选择数量、坐标范围、请求排队和超时均有上限。 + +ViewRequest拟包含: + +| 字段 | 语义 | +| --- | --- | +| world / catalogRevision | 限定世界和解析基线 | +| selection | whole routes / resolved sections / exact rail spans | +| unmatched | KEEP、DIM、HIDE;FOCUS可变更底图范围,DECORATION不可隐藏底图 | +| styles | 声明式选择器与StyleOverride的有界列表 | +| landmarks | KEEP或仅相关停站/换乘点;不影响宿主原生玩家/路标 | +| trains | KEEP或HIDE;v1不提供依据线路名字猜测的车辆筛选 | +| clipBounds | 可选有限世界矩形,真正裁剪图元,不是整线bbox粗筛 | + +StyleOverride为可选颜色、opacity、相对widthScale、outline以及有限pattern(SOLID、COLOUR_WHITE、DASHED)。未填字段继承当前线路样式。数值必须有限/有范围,非法请求返回INVALID;不允许任意逐帧回调、宿主纹理对象或渲染器替换。 + +所有session只能操作自身句柄;clear撤销其请求但保留句柄,close幂等释放并停止回调。FOCUS优先级最高者控制范围,同优先级按激活顺序确定,replace不偷偷重新抢占;关闭后恢复下一会话。DECORATION按稳定优先级/所有者顺序叠加样式,不将多个导航范围求交。 + +用户全局enabled及各图层开关优先。样式和范围只作用显示,不改MapDataCache、MTR路线、轨道或配置文件。每次有效请求变化更新viewRevision,让两地图静态缓存正确失效;渲染不逐帧回调外部模组。 + +游戏内显示当前聚焦所有者;可选displayName为长度受限的纯文本,不参与owner身份判定。用户可“暂时看全图”而不销毁第三方会话/导航:安装成功与当前是否实际生效分开报告(例如用户覆盖导致SUSPENDED)。新的replace不取消该用户覆盖,恢复聚焦或换世界才解除。此为UI补充约定,具体状态类型实施时冻结;不开放第三方任意注入按钮/面板。 + +导航session在地图关闭后可保持有效,以便再次打开仍显示当前行程;这不会让关闭的地图继续订阅全图车辆。断线/换世界会话立即失效,不跨serverEpoch恢复。 + +## 5. 同源站点ETA + +```java +interface StationBoardApi { + BoardSubscription watch(StationRef station, BoardQuery query, + Consumer listener); +} +interface BoardSubscription extends AutoCloseable { + BoardSnapshot latest(); + void close(); +} +``` + +BoardSnapshot含generation、native sampledAt/serverNow、时钟估计误差、新鲜度、完整性与各服务分组状态。ArrivalEntry保留原生route/platform ID、destination、arrivalAt、departureAt、deviation、realtime、terminating;字段不是某辆MapCar的身份。 + +latest始终返回非null快照,首份有效响应前为LOADING。时间统一标明毫秒与服务端时钟域;收到时间不等于原生预测的生成时间,未提供真实sampledAt时标未知,不能伪造新鲜度。 + +线路是展示标题,方向/目的地和站台继续区分;不能用线路名称/颜色去重。BoardQuery区分到站显示与可登乘发车筛选,终到标志不丢失。默认内置站点面板使用到站语义;发车/登乘查询只过滤原生结果,不另算时间。 + +`LOADING / READY / LIMITED / STALE / UNSUPPORTED / NOT_FOUND / ERROR` 与READY下的空结果分开。缺组、截断、刚登记原生请求的空缓存都不能报完整无班次。过期值可灰显最后预测并带年龄,不能继续冒充实时。 + +内置展开面板和第三方消费者使用同一服务、同一站点缓存和去重请求。折叠面板只close自己的watch,不撤销别人订阅;最后兴趣关闭、超时或断线后服务端停止查询。额度、刷新最小间隔和优先级由Overlay控制,插件不能请求每帧全世界车站。 + +## 6. 典型调用流程(非特定模组适配) + +```text +调用方确认导航 + → 获取Overlay API版本、WorldRef和只读目录 + → 将本方行程映射为route/platform/stop occurrences + → resolve乘车区段,处理歧义/未就绪 + → 创建自己的FOCUS会话 + → replace(选中的区段 + unmatched=HIDE + 样式) +调用方调整/重算路线 → 原子replace +调用方取消导航 → close,恢复普通地图 +切服/拓扑失效 → 收到状态,重新获取目录再解析 +``` + +Overlay不主动订阅MTR-Navigation的NavigationApi,不反射howtogo的数据结构,也不对它们加运行时依赖。它们或其他作者可自行在上述流程中接入;准备阶段仅用公开源码验证这些输入形状能够表达实际需求。第一份集成示例应是无第三方依赖的测试消费者。 + +## 7. 发布与兼容门 + +- API签名、线程约束、错误状态、关闭语义及owner冲突规则冻结后才发布1.0.0。后续只做向后兼容新增;破坏性变化升级API主版本,不能跟着内部包/缓存随意移动。 +- 六构建检查公共契约字节码为Java17且没有平台/Core依赖;API单独artifact与mod内API类须签名一致。调用方compileOnly示例在1.20/1.21上均验证。 +- 测试覆盖两会话撤销/冲突、地图拖拽与面板点击、重复平台/环线跨圈/多路径、半轨截断/共线色带/白块相位、旧服务端能力、晚到响应、断线清理、原生ETA对照及费用基准。 diff --git a/docs/plans/mtr-4.1-ui-research.md b/docs/plans/mtr-4.1-ui-research.md new file mode 100644 index 0000000..e5b71c0 --- /dev/null +++ b/docs/plans/mtr-4.1-ui-research.md @@ -0,0 +1,76 @@ +# MTR 4.1内部UI源码研究与1.5.3取舍 + +Codex,2026-10-25。仅公共源码研究,没有反编译、修改MTR或实现Overlay功能。结论补入[游戏内UI设计](v1.5.3-ingame-ui.md)。 + +## 1. 研究版本:先区分开发源码和发布包 + +本次通过上游`git ls-remote --heads/--tags`重新核对:4.1开发分支`stonecutter`的远端HEAD为`f05d99efab6f27b3868913438fce2da381e5798f`,与本地研究副本一致;其[gradle.properties](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/gradle.properties)仍标`4.1.0-beta.2`,依赖Elementa 743、UniversalCraft 497。不能将其表述为已核实的“4.1.0正式版”。GitHub Releases页没有发布条目,不能据此确认Modrinth/CurseForge的最新发布包或二进制与源码完全一致。 + +本报告的“最新”限定为**调查时4.1公开开发分支HEAD**。版本目录提供1.21.1/1.21.4的Fabric/NeoForge,不证明这套新UI在本项目1.20.1/1.20.4 + MTR4.0.5中存在。研究固定上述SHA,不随远端分支移动;源码副本位于ignored `build/source-research/mtr`。 + +## 2. 实际架构:两代UI并存 + +新页面采用Elementa的组件树与约束布局,UniversalCraft桥接Minecraft版本;旧的Minecraft `Screen`/`AbstractWidget`体系仍保留。 + +- [WindowBase](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/src/main/java/org/mtr/screen/WindowBase.java)继承Elementa `WindowScreen`,新构造用Elementa V11,旧Screen父页兼容构造仍用V10;Esc释放时标记关闭,下一tick返回父页;`isPauseScreen()`固定false。 +- [ScreenBase](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/src/main/java/org/mtr/screen/ScreenBase.java)仍继承Minecraft Screen,注册原生widgets并在onClose返回新/旧父页,也不暂停游戏。 +- [ConfigScreen](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/src/main/java/org/mtr/screen/ConfigScreen.java)、DashboardScreen、PIDSConfigScreen等已采用新组件;`ScrollableListWidget`、BetterButtonWidget等旧控件仍在仓库。 + +因此不能只看到一个旧控件就认定4.1仍完全手写坐标,也不能认定所有旧控件已经被替代。公开class不等于承诺稳定的扩展API。 + +## 3. 最适合借鉴的六类写法 + +| 写法 | MTR源码与行为 | Overlay落点 | +| --- | --- | --- | +| 约束式组合布局 | [SingleTabBackgroundScreenBase](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/src/main/java/org/mtr/screen/SingleTabBackgroundScreenBase.java)使用父容器相对宽度、Sibling间距、Fill剩余高度和可换行标题;[BackgroundComponent](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/src/main/java/org/mtr/widget/BackgroundComponent.java)限制最大320×240并预留外边距 | 用“标题固定+剩余区域滚动+宽高钳制”描述站点卡片;避开继续散落绝对坐标。该320×240/40外边距是MTR独立页面参数,不直接套地图侧栏 | +| 分组与展开状态保留 | [ListItem](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/src/main/java/org/mtr/widget/ListItem.java)分parent/child,默认expanded=false,overwriteList按parentKey恢复展开状态,筛选可自动展开匹配组 | ETA更新后保留用户展开的线路、选中站和滚动位置;以原生route/platform/服务组身份作键,不能以ETA文字或行号作键 | +| 释放时决定点击/拖动与重叠消歧 | [MapComponent](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/src/main/java/org/mtr/widget/MapComponent.java)记录按下位置,释放时比较位移;重叠站点/站台进入候选列表,单项单动作可直接触发;弹窗位置钳制 | 保留“单击固定站点,重叠才选列表”;先由宿主桥证明按下/释放顺序。采用整个手势最大位移/拖动标志,避免拖远后回到原处误判点击;不照搬其小于1px端点阈值 | +| 控件按职责拆分 | [ButtonComponent](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/src/main/java/org/mtr/widget/ButtonComponent.java)处理可换行/省略和禁用/悬停,[ScrollPanelComponent](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/src/main/java/org/mtr/widget/ScrollPanelComponent.java)独立处理滚动内容和滚动条 | 绘制/布局、数据状态、动作分别建模;图层菜单按钮、站点标题、ETA行不各写一套输入逻辑。保留宽度/文字变化才重算省略的思想 | +| 设置页有统一关闭提交点 | ConfigScreen由通用单页背景承载标签、checkbox和数字输入;onScreenClose提交数字值、保存配置,并刷新相关资源 | 把图层动作统一通知、按用户操作批量保存;必要的详情页保存父页。Overlay不应照搬“所有关闭都刷新动态纹理”,也不能把返回子页错误当最终退出 | +| 名称/时间格式化与业务数据分离 | [FontRenderOptions](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/src/main/java/org/mtr/font/FontRenderOptions.java)及[FontRenderHelper](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/src/main/java/org/mtr/font/FontRenderHelper.java)区分字体、CJK、对齐、`|`分行、溢出;[RenderPIDS](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/src/main/java/org/mtr/render/RenderPIDS.java)使用原生到站时间、时钟偏移、实时/计划标记 | 名称展示通过版本适配器,不能解析显示字符串建线路关系。ETA原始时间与本地展示分开,计划班次明确标注;时间格式化不参与预测 | + +### 视觉语言值得参考,但不能整套照搬 + +[GuiHelper](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/src/main/java/org/mtr/tool/GuiHelper.java)定义深色列表底`#111111`、强调底`#333333`、hover `#444444`、透明背景`#CC111111`,以及8px字高/4px间距/16px标准行的基准。配合线路色块、chevron、图形按钮,适合减少Overlay菜单与MTR本体的视觉割裂。 + +MTR独立设置页使用Minecraft风格背景并有深色标题文字,列表/地图则是另一套深色面板;不能把标题`#404040`直接放到我们的深色站点卡片。Overlay建议采用其色块/间距层级,保留20–24 GUIpx的主要点击目标、清晰文字与键盘焦点,不把16px内部列表行当所有控件尺寸。 + +## 4. 不适合直接采用的部分 + +1. **移开即关闭的弹窗**:MapComponent.checkPopupHover在指针离开扩展范围时隐藏弹窗。可用于临时目标选择,不能用于用户要求的固定站点到站板;卡片必须与hover生命周期分离。 +2. **用颜色/名字归并服务身份**:ListComponent.setRoutes可按颜色+`||`前名称分组,另有flattenRoutesByColor。这是管理列表展示方法,不是原生route ID;到站板不能合并同色同名的不同路线/方向。 +3. **把滚动裁剪当列表虚拟化**:新ListComponent.draw调用iterateData后为每个未折叠/未过滤行创建绘制任务,consumer始终返回false;没有按可视行范围跳过。即使外层裁剪,CPU侧仍遍历M行。DashboardScreen.onTick还会重建当前tab的数据。大站点UI应在结构revision变化时重建分组,仅对可见V行更新文字/绘制,不能把MTR代码当O(V)的证据。 +4. **每帧排版、资源查找与flush**:ListComponent.draw创建builder/数组/list/lambda并endBatch;作为独立界面可以工作,嵌入宿主地图会增加分配和合批干预。保留本项目静态/动态分离,不将这些调用复制进每帧地图层。性能是源码风险分析,未做对比实测。 +5. **悬停缓存决定点击**:ListComponent在draw中设clickAction,点击时直接执行。Overlay必须在输入事件发生时核对当前布局/世界/数据revision及有效目标,不执行上一帧已经过期的行回调。 +6. **继承WindowBase解决暂停**:它始终不暂停,且拥有完整WindowScreen的输入根节点。我们的地图由Xaero/JM持有,不能再嵌一套全屏根窗口抢事件;只查ETA的单机运行和普通暂停菜单须由地图上下文判定。 +7. **把文本压扁当通用长名方案**:MTR列表用COMPRESS,部分按钮做省略;站点卡片仍以换行/省略+完整提示为主,防止中文终点和状态压到难读。FontRenderOptions.SCROLL仍注释未实现,ALTERNATE当前也按分行处理,不能承诺现成跑马灯/语言轮播。 +8. **只凭控件外观推断无障碍支持**:旧ClickableWidgetBase的updateWidgetNarration为空;新组件的示例鼠标回调也不能证明已有Tab/键盘/叙述支持。此前UI设计的可达性验收保留。 +9. **把PIDS方块渲染器当GUI到站板**:RenderPIDS.getArrivalString在>=60秒显示整数分钟、0秒及以下返回空串,计划班次加星号。这些呈现适合站内设备,地图面板仍需已到站/终到/覆盖不全/过期等状态;不实例化方块renderer只为格式化秒数。 + +## 5. 直接复用内部UI类的实际成本 + +[NeoForge构建脚本](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/build-neoforge.gradle.kts)把Elementa/UniversalCraft及Kotlin shadow并重定位到`org.mtr.libraries.*`;[Fabric构建脚本](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/build-fabric.gradle.kts)采用include,未做相同的gg.essential重定位。 + +因此源码里的`gg.essential.*`类型,不能未经实际依赖编译就假定在两个loader运行时都有同一全名。派生WindowBase、接受/返回Elementa对象、加载其Kotlin类型都会穿过这个边界。MTR4.0兼容目标还需要另一套实现,不能仅改import宣布六平台共用。Elementa带跨版本抽象,也没有自动解决Xaero/JM事件取消与面板占区。 + +建议分层取舍: + +| 层 | 本次建议 | 复用条件 | +| --- | --- | --- | +| 地图内浮层 | 复用交互/布局设计;继续本项目共享controller/layout/model和轻量Minecraft绘制 | 可控宿主输入、裁剪、缓存和三语言,保持6目标;不额外引入Elementa整套窗口 | +| 独立设置详情页 | 可评估MTR组件,但不作为本版前提 | 先用实际发布依赖完成NF/Fabric/旧4.0编译与加载原型;证明资源就绪、返回/保存/暂停正确后再选 | +| 名称与只读资源元数据 | 在内部版本适配器中优先调用已经存在且核实的方法 | 支持资源/语言重载;无法调用则可见回退,不能拖累公共API加载 | +| 对外API | 仍为Java17纯JDK DTO及声明式请求 | 不泄漏Elementa、WindowBase、MTR控件、Kotlin或地图宿主类 | + +若未来实际移植源码,保留MIT作者和许可证,并独立核对素材/字体许可;本次未复制实现或素材。 + +## 6. 对1.5.3设计的具体补充 + +- 持久选择/展开键采用`WorldRef + stationId + routeId + direction/destination + platformId`,结构与倒计时分开;同世界同站刷新不折叠、不同ID不误继承。同一服务重复停站若缺原生消歧仍按主计划标不确定。 +- 借鉴MTR的parent/child与chevron,站点整体仍默认折叠,点击一次固定展开;**展开站点后默认可见各线路ETA**。多方向/站台行可在内容过多时由用户主动收起,不能又把每条线路ETA全藏在第二层点击后。 +- 分组状态与排版按revision更新,当前可见行按秒刷新ETA;保留滚动锚点和键盘焦点,不用新ETA排序使指针下内容跳动。 +- 候选弹窗使用钳制位置/有界高度/滚动,单一明确目标直接打开卡片。拖动状态一旦越过阈值便不再恢复为点击,即使释放点返回原坐标。 +- 设置保存与返回使用明确的“一次关闭/提交”动作;Esc由最上层消费,避免同一事件关闭卡片和宿主地图。只借鉴WindowBase延后执行导航的思想,不强行改成所有界面都不暂停。 +- 测试新增:数据刷新后展开/滚动/焦点保持;同名同色不同ID分开;拖出再拖回不是点击;长中文/多语言换行;一份数据更新不引发每帧全列表重建;资源重载后样式/文本可用。 + +验证边界:已核对远端分支HEAD/版本、上述公共源码及构建打包规则;文档引用检查另记录于协作文件。本次未尝试继承内部组件、未修改依赖、未构建UI原型或游戏实测;1.5.3实施仍先经过原生数据与宿主输入桥验收。 diff --git a/docs/plans/v1.5.3-ingame-ui.md b/docs/plans/v1.5.3-ingame-ui.md new file mode 100644 index 0000000..958e40d --- /dev/null +++ b/docs/plans/v1.5.3-ingame-ui.md @@ -0,0 +1,144 @@ +# 1.5.3 游戏内UI:调查与设计 + +Codex,2026-10-25。调查基线 `254a6a3`,兼容分支只读核对。**仅设计,未实现、未启动游戏验收,也未修改版本属性。** 本文补充[站点ETA与扩展API计划](v1.5.3-station-board-extension-api.md),不新增导航求解或车体悬停ETA/下一站距离功能。 + +后续补充:[MTR4.1内部UI源码研究](mtr-4.1-ui-research.md)重新核对最新公开4.1分支(仍标beta.2),追踪Elementa/旧Screen并存、布局/分组/点击/返回/文本和loader打包边界。采纳稳定ID保留展开/焦点、约束式组合布局、重叠候选列表和一次提交思想,不把MTR内部组件视为稳定公共UI接口。 + +## 1. 调查结论与证据 + +| 现状 | 源码证据 | 对1.5.3的影响 | +| --- | --- | --- | +| Xaero自绘两个78×23 GUIpx按钮,固定在(5,60),只有线路/轨道开关 | [XaeroRouteRenderer](../../src/main/java/io/github/teamcreating/mtrmap/integration/xaero/XaeroRouteRenderer.java):WIDGET常量、renderToggleWidgets | 大字号/小窗口和其他模组按钮可能冲突;车辆/站点选项不易发现 | +| Xaero绘制受enabled限制,但点击入口没有相同限制;悬停按钮之后仍执行地图对象拾取 | 同文件onRender、onMouseClicked | 存在隐藏按钮区域仍可改图层、同一帧两份Overlay tooltip的代码路径;尚未本轮实机复现 | +| Xaero站点图标为12 GUIpx、站台5 GUIpx且scale<0.15隐藏;线路/停站回退拾取却用10/20世界格 | 同文件drawLandmarks、shouldDrawLandmark、pickTooltip | 视觉大小与拾取距离随缩放不一致,稠密区难选;拾取还扫描全部routes/stops/tracks | +| JourneyMap有宿主插件按钮,英文标题/提示写死;有150ms防重复切换 | [JourneyMapToolbar](../../src/main/java/io/github/teamcreating/mtrmap/integration/journeymap/JourneyMapToolbar.java) | 复用宿主入口,但状态应由共享模型生成;不能用时间防抖解决全部输入语义 | +| JourneyMap站点标题交给MarkerOverlay,列车由foreground另画tooltip | [JourneyMapLandmarkManager](../../src/main/java/io/github/teamcreating/mtrmap/integration/journeymap/JourneyMapLandmarkManager.java)、[ForegroundRenderer](../../src/main/java/io/github/teamcreating/mtrmap/integration/journeymap/JourneyMapForegroundRenderer.java) | 同一区域有两套提示所有者;新增固定面板要统一仲裁并处理宿主原生提示 | +| JourneyMap地标集合按玩家world生成,foreground轨道/车辆按地图查看维度读取;站台没有Xaero相同的缩远隐藏规则 | 同上两文件及JourneyMapIntegration | 跨维度站点交互须以查看维度/结构化ID为准;本轮是源码结论,未宣称复现所有界面症状 | +| 列车悬停已共用:车型名称、线路→终点、节数 | [TrainMapTooltip](../../src/main/java/io/github/teamcreating/mtrmap/integration/TrainMapTooltip.java) | 保留信息边界,优化截断/缓存/命中,不添加暂停中的ETA或距离 | +| 车辆状态固定画在左下角,networkSync关闭与服务端不支持共用nearby文案 | [ClientTrainStream](../../src/main/java/io/github/teamcreating/mtrmap/train/ClientTrainStream.java):drawStatus | 需分别解释覆盖范围、用户关闭同步、加载/过期,不能把静态网络完整性等同车辆流状态 | +| 显示设置主要经命令和文件修改,没有本项目配置Screen/ModMenu入口注册;三种语言资源目前只有列车相关键 | [命令](../../src/main/java/io/github/teamcreating/mtrmap/CommandRegistration.java)、[NeoForge配置](../../src/main/java/io/github/teamcreating/mtrmap/config/MTRMapConfig.java)、[Fabric配置](../../fabric/src/main/java/io/github/teamcreating/mtrmap/config/MTRMapConfig.java)、[语言资源](../../src/main/resources/assets/mtrmap/lang/zh_cn.json) | 增加地图内设置入口;保留既有命令与配置键,不假设loader自动提供完整设置页 | +| Xaero单机保持运行条件依赖trainCarsEnabled且屏幕类名精确匹配GuiMap | [TrainMapPauseMixin](../../src/main/java/io/github/teamcreating/mtrmap/mixin/client/xaero/TrainMapPauseMixin.java);车辆租约还记录当前screen实例 | 关闭车辆只查站点ETA、或打开子设置页,均需重新定义实时兴趣/暂停策略 | + +补充参考:Create固定公开源码`7ff2760e4924d35d4bc2e22c4b19923f0178cd16`的[XaeroTrainMap](https://github.com/Creators-of-Create/Create/blob/7ff2760e4924d35d4bc2e22c4b19923f0178cd16/src/main/java/com/simibubi/create/compat/trainmap/XaeroTrainMap.java)和[JourneyTrainMap](https://github.com/Creators-of-Create/Create/blob/7ff2760e4924d35d4bc2e22c4b19923f0178cd16/src/main/java/com/simibubi/create/compat/trainmap/JourneyTrainMap.java)在切换按钮悬停时抑制列车提示,并管理地图打开/子界面与请求生命周期。这里借鉴交互边界,不复制其素材或对外宣称同一实现。 + +JourneyMap API公开源码固定`4a57dee370a0ae70660ae66d3dc5363e670fc1ee`(分支1.21.1_2.0.0),研究副本在ignored build/source-research/journeymap-api: + +- [FullscreenEventRegistry](https://github.com/TeamJM/journeymap-api/blob/4a57dee370a0ae70660ae66d3dc5363e670fc1ee/common/src/main/java/journeymap/api/v2/common/event/FullscreenEventRegistry.java):有PRE可取消的点击/拖动、插件按钮、自定义工具栏与地图绘制后/按钮前的render事件。 +- [FullscreenMapEvent](https://github.com/TeamJM/journeymap-api/blob/4a57dee370a0ae70660ae66d3dc5363e670fc1ee/common/src/main/java/journeymap/api/v2/client/event/FullscreenMapEvent.java):没有对应的完整释放/滚轮/键盘事件组,不能把PRE/POST误当鼠标按下/释放。 +- [IOverlayListener](https://github.com/TeamJM/journeymap-api/blob/4a57dee370a0ae70660ae66d3dc5363e670fc1ee/common/src/main/java/journeymap/api/v2/client/display/IOverlayListener.java):原生marker点击可控制向其他overlay冒泡,但不等于任意面板区域的输入桥。 +- 项目已有MC1.20.4/JM5专用drawMap mixin及兼容代码;不能照搬JM6事件名称。以上公开API源码不是所有现有运行包的精确实现证明,实际依赖编译、顺序、取消效果与缩放换算仍须验收。 + +## 2. 界面结构与视觉方向 + +沿用Minecraft字体、紧凑深色面板、清晰边框和现有站点/车厢符号。线路颜色只用作辅助识别,开关另有勾选、文本和焦点框。保持地图为主要内容,不自动改变线路世界宽度、车厢LOD或缩放手感。 + +设计四个各司其职的区域: + +1. **地图入口**:Xaero默认一个带文字的“MTR”紧凑按钮;JM复用宿主addon入口。打开同一份图层菜单,不再常驻两块英文大按钮。 +2. **图层菜单**:总开关、线路、轨道、列车、站点;“更多显示选项”内放站台、车厂、空站、隐藏线路说明、布局与同步详情。两个地图共用设置语义,不强行统一宿主按钮位置。 +3. **站点面板**:选中站的固定到站板,有标题、收起、关闭和独立滚动区;默认不展开、不查询ETA。 +4. **状态提示**:一个紧凑状态条/图标,正常状态可收起;有延迟/仅附近/能力缺失时保留简短说明。展开可分别查看线路数据、车辆流和本站ETA状态。 + +概念线框,仅表达分区,不代表已渲染效果。图层菜单和站点卡片共用侧边区域,菜单打开时临时覆盖卡片绘制而不取消其选择;这种短暂弹层不改变ETA兴趣,关闭即回到原卡片。 + +```text +┌─ 宿主地图控件保留 ────────────────────────────────┐ +│ [MTR ▾] 宿主地图控件 │ +│ ┌ Central ───────── [收起] [×] ┐ │ +│ │ ● 绿色线 │ +│ │ → Airport · 2站台 2分15秒 │ +│ │ → Harbour · 1站台 已到站 │ +│ │ ▣ 红色线 │ +│ │ → North · 4站台 暂无可用预测 │ +│ │ 数据覆盖不全 · 部分班次可能未返回 │ +│ └──────────────────────────────┘ │ +│ 地图:线路/站点/车辆 │ +│ [MTR · 全图列车] [当前聚焦:某扩展 · 暂时看全图] │ +└─────────────────────────────────────────────────┘ +``` + +所有尺寸使用GUI逻辑像素,初始建议按钮高20–24、内边距6、站点面板宽220–300;只是实施起点,须实机检查中文长名/GUI scale后确定。不要将物理屏幕像素和GUI像素混用。 + +## 3. 布局、缩放与可达性 + +- 宿主适配器提供可用区域、已知控件占区、投影及输入能力;布局按屏幕边缘停靠,边距至少6 GUIpx。JM优先原生addon按钮,卡片避开其右侧控件;Xaero避免继续假定(5,60)永远安全。 +- 若宿主无法提供可靠占区,不宣称能自动识别所有第三方UI。提供左右停靠/紧凑模式,记住每种宿主的本地偏好;调整屏幕/GUI scale后重新钳制面板位置,不允许漂到屏幕外。 +- 可用侧栏宽度不足约200时改为底部卡片,最多占可用高度约45%;连标题和一行内容都容不下时使用紧凑标题+“详情”页。门槛均为待验证建议,极小窗口也必须可关闭/返回。 +- 不因展开卡片自动平移或放大地图。可另提供一次性的“定位此站”,仅在当前宿主已验证center操作时启用;不持续锁定镜头、不新增跟车。 +- 卡片内内容滚动,标题/关闭保持可见;列表只布局/绘制可见行。长线路名/终点允许最多两行后省略,完整文本以提示/详情查看;保留MTR多语言名称语义,不按逗号切description重新建线路关系。 +- 三语言en_us/zh_cn/zh_tw同步;不再将英文单词烘焙进按钮PNG。原生MC字体渲染可翻译文本,图标资源只表达图形;用户数据站名不机器翻译。 +- 开关、关闭、收起和列表项可Tab/Shift+Tab聚焦,Enter/Space激活,焦点进入可滚动区后方向键/PageUp/PageDown滚动;提供原生叙述标签。未聚焦本UI时不截获宿主方向键。 +- 颜色相近线路仍靠名称/编号区分;“实时/计划、过期、覆盖不全”必须有文字,不只改颜色。避免常驻闪烁/呼吸动画和自动滚动跑马灯。 + +## 4. 输入与悬停规则 + +宿主弹窗/输入框优先,不能只因当前screen属于地图就抢输入。Overlay内部顺序为:菜单/候选列表→固定面板→可见标记/车辆→线路→空白地图。绘制、命中、scissor必须使用同一布局结果。 + +| 动作 | 行为 | +| --- | --- | +| 左击MTR入口 | 打开/收起图层菜单,菜单内点击不平移地图 | +| 左击站点 | 按住/释放位移≤建议4 GUIpx且同一有效目标时固定展开;超过阈值让地图继续正常拖动 | +| 左击同站/另一站 | 同站聚焦不重订阅;另一站替换,不让旧响应覆盖 | +| 滚轮位于面板 | 只滚动面板;到列表边缘也不穿透缩放地图 | +| 滚轮位于地图 | 完全保留宿主缩放行为 | +| 拖动从地图开始 | 即使穿过面板也不半途抢走;面板内按下则捕获到释放,避免拖出后误操作地图 | +| Esc | 先关Overlay最上层弹层,再关固定站点卡,再交回地图;设置详情页先返回原页面。宿主已有弹窗时服从宿主 | +| 右键/双击 | 保留宿主菜单/路标语义,1.5.3不添加Overlay右键手势;同一次手势只触发一次逻辑动作 | + +站点按下不能直接取消全部地图输入后又声称可无缝拖动:输入桥必须验证“可延迟识别点击且宿主仍能拖动”的实际顺序。如果宿主marker点击API无法满足,使用经验证的screen按下/释放桥;不猜测事件时机。JM的150ms旧防重复机制不能掩盖丢失的合法键盘激活。 + +参考MTR MapComponent的释放时判定,但使用整个手势最大位移/拖动标志:一旦超过阈值,即使拖回按下点释放也不能触发站点点击。候选弹窗可临时随指针关闭,固定站点卡片不采用MTR鼠标移开即关的行为。 + +悬停只显示一张Overlay简要提示:优先UI控件,地图对象提示在菜单/面板区域内停止。站点提示只给站名、少量线路及“点击查看到站”;固定面板已选同站时不重复显示同份提示。列车维持车型、线路/终点、节数;缺字段用“—”,不做时间/距离推测。 + +拾取以屏幕GUIpx容差为准,再换算世界查询范围;只查当前真正可见、未被区段过滤/裁掉的元素。站点/站台密集重叠时生成有界候选列表,提示存在多个目标,点击选择而非永远命中第一项;同一候选结果用于hover和click,不靠名称/颜色合并。列车仍只有悬停,不新增点击详情功能;与站点重叠时hover候选保留两类身份,按屏幕距离/可见绘制顺序确定首项,站点列表入口仍可到达。 + +线路提示只在没有点对象候选时查询,并复用当前LOD的可见曲线/区段命中,不继续使用粗采样折线或每帧全网扫描。候选上限/截断明确显示“放大以选择”,不把被截断对象计成完整列表。 + +JM必须明确单一tooltip所有者:首选用共享命中与提示接管Overlay自己的marker标题;只有已验证共享hover/输入桥后才抑制原生标题,不能先清空title导致无提示。宿主路标/其他模组overlay的提示仍由宿主管理。桥不可用时保留静态原生提示并标明到站交互暂不可用,不接管整个地图。 + +## 5. 站点卡片、数据状态与外部聚焦 + +站点卡片沿用主计划NONE/PINNED_COLLAPSED/PINNED_EXPANDED状态机。每条线路标题下保留目的地/方向/站台子行;ETA变化不重新按最短时间排序整张表,避免指针下项目跳动。线路/服务组以稳定ID排序,结构变化才更新行位置,局部ETA文字至少按秒更新即可。 + +同站数据刷新保留用户展开状态、滚动锚点和焦点;使用原生ID/服务组键,不能直接沿用MTR管理列表的颜色/名称分组。站点首次展开后各线路ETA默认可见,不能再默认折叠所有线路让用户逐条点击;方向/站台过多时允许主动收起分组。 + +- 加载中:“正在获取到站信息…”;不先显示0秒或“无车”。 +- LIMITED:“数据覆盖不全”,保留已返回班次,各缺组标“暂无可用预测”。 +- STALE:“上次更新…前”,冻结/灰显最后已知状态;不无限倒计时至负数。 +- UNSUPPORTED:“服务端未提供本站到站信息”;可解释原生同维度附近回退,但不让“重试”伪装能解锁能力。 +- 有效空结果、站已删除、离线等分别提示;“重试”只能走既有限频请求,不触发整网刷新或绕过服务器额度。 + +状态条把**静态线路覆盖、动态车辆覆盖、本站ETA**分开:全图线路不代表全图车辆;零辆但成功收到完整帧是有效空网,不能显示加载中。用户关闭networkSync时写“已关闭全网同步 · 使用附近数据”,区别“服务端未提供全图车辆”;原数据层尚不能证明的原因标未知。技术字段/错误详情收在展开区,不常驻占地图。 + +外部FOCUS有效时显示“当前聚焦:扩展名称”,仅接收有长度限制的纯文本显示名,回退owner ID。提供“暂时看全图/返回聚焦”作为本地查看覆盖,保留第三方句柄和导航状态;不调用对方取消导航、不改MTR数据。最新外部replace不能悄悄取消用户的暂时全图选择;本地覆盖持续到用户恢复或换世界。无可靠显示名时也不反射第三方取名。图层总开关继续优先。 + +## 6. 内部结构与生命周期 + +拟新增共享`OverlayUiController`(选择/焦点/弹层/输入)、`OverlayUiLayout`(矩形/裁剪/占区)、`OverlayUiModel`(显示数据/文案状态)和两类host bridge。名称为内部设计,不纳入公共API v1;UI不能直接读TSC对象或让公共API依赖GuiGraphics。 + +设置写入统一适配服务:保持现有配置键,区别持久显示偏好与临时面板/导航状态。Fabric当前set会同步保存文件,多项设置按一次用户操作批量保存;命令/菜单/宿主按钮走同一更新通知。NeoForge持久化语义按实际API核对,不直接假定set已写盘。界面显示选项只改本机偏好,不改服务器设置。 + +地图内设置优先用同屏弹层,避免为了开菜单替换screen实例而丢车辆兴趣。若必要进入详情Screen,保存父screen和恢复策略,明确期间不绘制地图/暂停Overlay实时兴趣;不引入整套新UI框架或强制Cloth/ModMenu依赖。Overlay所有控件关闭时仍留下可重新启用的入口,但不能保留隐藏可点击区域。 + +实时兴趣由已验证的地图上下文决定:需要动态列车或已展开站点ETA,才在集成服允许实时推进;车辆开关关闭不应使ETA停住。只对实际地图上下文生效,普通暂停菜单、离开地图和不需要实时数据的设置详情不得自动取消暂停。暂停/子界面下ETA标“暂停”或停止兴趣,不能将不变预测装成实时。JM5/6也要验证实际暂停行为,不因Xaero已有mixin就假定其他地图正确。 + +关闭地图释放内置卡片兴趣、指针捕获、hover和布局引用;切服/世界epoch变化清空选择和未完成回调。拓扑更新保留相同有效stationId的卡片,站删除则显示已删除并释放查询。地标重建不能仅因MarkerOverlay实例变化清空选择。导航会话及用户暂时全图覆盖与内置卡片生命周期分离。 + +## 7. 开销与实施验收 + +布局/文本换行仅在尺寸、GUI scale、语言、结构数据或UI状态改变时重算;倒计时只改可见行文字。型号显示名缓存跟随资源重载失效,避免hover每帧重复查自定义资源。UI无逐帧网络发送,也不因鼠标移过车站订阅ETA。 + +站点命中静态索引按目录revision更新;查询处理局部候选,车辆复用已有视口/车体命中,不再复制一份全图车队。缓存空间有界,不能为每个历史车站保留永不清理的排版。暂停菜单、关闭地图和折叠卡片后的服务端请求/租约释放都计入费用验证。 + +| 阶段 | 交付/必测 | +| --- | --- | +| U0 宿主桥验证 | Xaero NF/Fabric及旧Forge映射、JM5/6的按下/释放/拖动/滚轮/键盘、渲染层序与暂停。先用小型诊断原型记录事件顺序,不能只构建通过 | +| U1 共享菜单/文案 | 保留配置键,图层/总开关及命令同步;三语言、文本按钮、键盘焦点;全关闭仍可恢复,无隐藏点击区 | +| U2 共享命中/面板 | GUIpx拾取、候选消歧、一次tooltip、固定卡片不穿透、列表虚拟绘制、拖动/切站/关图生命周期 | +| U3 状态/聚焦 | 原生ETA各状态、三种数据覆盖分开、外部聚焦说明/暂时看全图、用户关图层优先 | +| U4 六平台回归 | 六构建/契约检查与两地图实机;低分辨率、GUI scale 1/2/3/Auto、中文长名/同名站、稠密站台/车厢、跨维度、宿主弹窗和路标菜单 | + +最低视觉矩阵:1920×1080/1280×720及缩小窗口;用实际GUI可用尺寸记录布局,不将分辨率当GUI尺寸。高GUI scale下标题/关闭必可见;默认折叠时ETA请求数为0;展开一站不新增全图车辆成员过滤;快速重试有上限;同场景记录UI布局/命中/绘制耗时、分配和查询次数,与1.5.2比较,不能直接声称更快或零开销。 + +本轮检查只覆盖公共源码、现有实现和文档。上述候选布局/像素值、宿主输入桥及暂停策略尚未实机验证;不把调查结果当成已实现UI或已通过的可用性测试。 diff --git a/docs/plans/v1.5.3-station-board-extension-api.md b/docs/plans/v1.5.3-station-board-extension-api.md new file mode 100644 index 0000000..098fac1 --- /dev/null +++ b/docs/plans/v1.5.3-station-board-extension-api.md @@ -0,0 +1,132 @@ +# 1.5.3 准备:固定站点到站板与统一地图扩展 API + +Codex,2026-10-25。基线 main `bf264e3`。**这是源码核对后的设计与验收计划,尚未实现或发布。** 目标版本为1.5.3,本轮不改已发布1.5.2的二进制、标签和版本属性;进入实现时再统一切换双分支版本。 + +## 1. 本版范围 + +1. 站点默认折叠;点击站点后展开并固定信息面板,鼠标移开不消失。展开后查看该站各线路、方向/站台的下一班车ETA。 +2. ETA复用MTR/TSC生成给PIDS的原生到站数据。提供同一份只读到站板给内置界面及扩展调用,不再实现预测、制动或时刻表模拟。 +3. 第三方通过统一API声明线路显示区段和样式,例如确认导航后只显示实际乘车段。Overlay不监听特定导航模组的事件,不加入Navigation/howtogo专用适配器。 +4. 保留全图列车同步、纯2D和现有六平台目标。此次恢复的是**站点ETA**,不恢复列车悬停ETA/下一站距离的暂停草稿。 +5. 游戏内UI统一图层设置入口、固定站点面板、悬停与输入优先级、数据状态和第三方聚焦提示;具体调查与布局见UI补充设计。 + +统一接口的草案见 [overlay-api-v1-draft.md](../api/overlay-api-v1-draft.md)。接口名称尚未冻结,不能视为已有可调用API。 + +游戏内UI的源码调查、图层菜单/站点卡片布局、输入仲裁、状态提示、外部聚焦提示与六平台验收见[1.5.3游戏内UI设计](v1.5.3-ingame-ui.md)。这是同版范围的补充设计;单击固定/默认折叠、原生ETA和第三方统一接口边界保持。 + +## 2. 公共源码证据 + +研究副本只读保存在被忽略的 `build/source-research/`,没有修改第三方仓库或反编译JAR。 + +| 来源 | 固定提交 | 已确认事实 | +| --- | --- | --- | +| MTR | `f05d99efab6f27b3868913438fce2da381e5798f` | gradle.properties标为4.1.0-beta.2;RenderPIDS→ArrivalsCacheClient→PacketFetchArrivals→ArrivalsCacheServer→Core ArrivalsRequest | +| TSC | `032baafbf9e3b246d1f9cc2d6b4972f1b31254ea` | 原生请求可按platform集合,当前源码也可按station ID展开;结果包含线路/站台、arrival/departure、实时标志、偏差和终到标志 | +| MTR-Navigation | `df53387951c58817da8dc7737d39893cbd9b5dac` | 已有带route/platform ID、起止停站索引的JourneySelection及可关闭监听句柄;可作为声明式乘车段输入的用例 | +| howtogo | `2b57baca2c0e69f0ebe8566f152d4d6e497584a5` | 有只读铁路数据接口,MtrMapOverlay读取器通过旧包名反射内部缓存;说明需要稳定的只读目录/几何接口,不能把内部MapDataCache当API | + +来源:[MTR PIDS](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/src/main/java/org/mtr/render/RenderPIDS.java)、[客户端缓存](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/src/main/java/org/mtr/data/ArrivalsCache.java)、[原生请求包](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/src/main/java/org/mtr/packet/PacketFetchArrivals.java)、[服务端缓存](https://github.com/Minecraft-Transit-Railway/Minecraft-Transit-Railway/blob/f05d99efab6f27b3868913438fce2da381e5798f/src/main/java/org/mtr/data/ArrivalsCacheServer.java)、[Core请求](https://github.com/Minecraft-Transit-Railway/Transport-Simulation-Core/blob/032baafbf9e3b246d1f9cc2d6b4972f1b31254ea/src/main/java/org/mtr/core/operation/ArrivalsRequest.java)、[Core结果](https://github.com/Minecraft-Transit-Railway/Transport-Simulation-Core/blob/032baafbf9e3b246d1f9cc2d6b4972f1b31254ea/src/main/java/org/mtr/core/operation/ArrivalResponse.java)。 + +第三方契约:[JourneySelection](https://github.com/teamCreating/MTR-Navigation/blob/df53387951c58817da8dc7737d39893cbd9b5dac/src/main/java/io/github/teamcreating/mtrnav/api/JourneySelection.java)、[NavigationApi](https://github.com/teamCreating/MTR-Navigation/blob/df53387951c58817da8dc7737d39893cbd9b5dac/src/main/java/io/github/teamcreating/mtrnav/api/NavigationApi.java)、[howtogo读取器](https://github.com/Dong-awa/howtogo/blob/2b57baca2c0e69f0ebe8566f152d4d6e497584a5/src/main/java/bili/dongsz/howtogo/client/MtrMapOverlay.java)。这是结构参考,不是已测试的互操作声明。 + +**版本边界**:MTR源码以动态 `transport-simulation-core:+` 依赖Core,上述Core主线源码不能当作已发布beta.2内嵌Core的精确提交。1.20分支的公开4.0源也有同一PIDS链路,但包前缀不同。P0必须针对项目实际4.1.0-beta.2/4.0.5依赖编译并用原生对象验证所选方法;不使用较新Core独有字段,不依赖反编译结论。 + +### 原生数据能复用什么,不能直接保证什么 + +- `ArrivalsCacheClient`是3秒刷新缓存,`ArrivalsCacheServer`为1秒缓存;初次读取可能仅登记请求并返回当前空缓存,不代表已经确认“无班次”。 +- `PacketFetchArrivals.runServer`使用玩家的serverLevel,不能查询玩家在主世界时地图正在查看的其他维度。 +- 服务端缓存构造请求传入 `10, -1`。Core按关联siding收集、排序并限制结果;这不是按线路公平取一条。忙线路可能占满候选,不能将缺失线路解释为当天停运。 +- 原生getArrival/getDeparture已包含预测偏差,不应再次叠加getDeviation。getRealtime=false是计划班次,不等于数据损坏。 +- 原生PIDS的ETA格式化使用服务端时钟偏移。Overlay应保留原始时间、采样时间和新鲜度,不能拿客户端未经校准的系统时间直接相减。 +- ArrivalResponse不提供可普遍唯一关联实际车辆与停站occurrence的完整身份;本站到站板不需要先解决车体关联,不能借机把站点ETA贴到某列地图车上。 + +## 3. 站点交互与到站板 + +### 3.1 固定与折叠规则 + +状态为 `NONE / PINNED_COLLAPSED / PINNED_EXPANDED`。未选中时仅有原来紧凑站点标记/悬停,ETA默认不订阅。**单击站点一次即进入PINNED_EXPANDED**,固定在屏幕边缘,不跟随鼠标;移开鼠标、拖动地图或站点移出视口均不收起。 + +- 标题折叠按钮进入PINNED_COLLAPSED,仅显示站名和简要信息,释放内置ETA兴趣;再次展开恢复请求。 +- 点击另一站替换当前面板;点击同一站只聚焦,不造成闪烁/重复订阅。空白区域仍可平移地图,不取消固定。 +- 关闭按钮/首次Esc关闭面板,下一次Esc才交给地图。地图关闭、断线或切换查看维度时清理面板和未完成请求;重新打开默认折叠。 +- 拖动不能误触站点:在按下/释放之间判断位移阈值,面板控件先消费输入,再判断站点,再交给宿主。Xaero现有click hook仅处理两个开关,须补共享命中及按下/释放逻辑;JM5/6各有宿主桥。 +- 面板是GUI坐标层,固定宽度/小屏自适应并可滚动;内部点击、滚轮不穿透地图。只消费站点与面板命中的输入,不破坏宿主路标/平移。 + +### 3.2 分组与状态 + +按线路分标题,每条线路下按**目的地方向和站台**显示下一班:`线路色/名称 → 终点 · 站台 · ETA`。相同颜色/名称不合并;对向站台、不同分支目的地不压成一条。初版保守保留不同站台的行,不猜测它们是同一班车。 + +- 以原生route/platform ID、destination及环线信息建立服务分组;若站台反复出现而原生结果不能消歧,明确标记,不能伪造停站索引。 +- 到站前倒计时;arrival≤now