工作区可能同时有多个 agent 会话/维护进程在改代码(2026-09 已有并行修改相互踩踏的教训:Create 集成与 ArmsDealerPriceProvider 曾在编译中途被改,造成非本会话引入的编译失败与缓存混乱)。为避免互相踩踏:
- 启动任何 Gradle 构建前必须先探测。只要命中以下任一信号,即视为「其他 agent 进程正在编译/改代码」,本轮不得启动编译(不得内联重试、不得 clean、不得临时移走文件规避):
tasklist(Windows)或其他ps可见java.exe且命令行含gradle的进程;~/.gradle/daemon/<版本>/下 daemon 日志的 mtime 在最近 1–2 分钟内仍在更新;- 工作区任意
*/src/源码文件近 60 秒内出现成批 mtime 变化(其他 agent 活跃写入); - 同一文件两次读取内容不一致(正被其他进程改写)。
- 命中后:立即停下编译动作,向用户报告探测到的信号与冲突风险,等待用户发起下一轮对话再继续。不要自动重试,也不要自行修复属于其他 agent 领域(
create/集成、villager/等)的文件。 - 确认可以编译时:优先用
--no-build-cache/--rerun-tasks或先删除编译输出再执行,避免把陈旧的UP-TO-DATE/FROM-CACHE快照误报为真实编译结果;报告结果时注明采用的重编译方式与平台清单。 - 单次 Gradle 调用只能构建一个 MC 版本(
-Pactive_versions=<v>);八平台全量验证需串行执行。
维护者的机器同时是日常用机,任何编译都不得占满性能:
- 每次本地 Gradle 调用必须带
--max-workers=2(默认 worker 数 = CPU 核数,会吃满全核;CI 不受此限)。 - 多条 Gradle 调用严禁并行:即使分属不同平台/任务也一律串行(与「单次调用只构建一个 MC 版本」叠加执行);全平台长跑放入后台任务执行、不阻塞对话,但后台内部仍串行。
- 禁止上调内存参数:
gradle.properties的-Xmx3G+org.gradle.daemon=false是基准,不得为提速改大堆或打开 daemon;NeoForm / ForgeGradle 反编译等 fork 进程的内存保持管线默认值。 - 重管线是占用大头:
clean/--rerun-tasks触发的 NeoForm 全管线(反编译/重映射/重编译)单平台即可满核数分钟。「改动涉及平台必须 clean 确认」纪律不变,但全平台 clean 矩阵必须:带--max-workers=2、后台串行执行,且启动前确认维护者不在重度使用机器。 - 编译启动前做两道门:并发协作探测(上文四信号)+ 机器负载确认(前台重负载/游戏/渲染进行中不得启动,先征得同意或等空闲时段),都过才允许执行。
./gradlew :<module>:compileJava -Pactive_versions=<v> # 快速编译单个平台
./gradlew :<module>:jar -Pactive_versions=<v> # 打包
./gradlew :common:test # common 单元测试 (JUnit 5)- Java: 21(legacy 平台)/ 25 +
--enable-preview(v26_1_2/v26_2)。toolchain 由各模块 build.gradle 指定。 - 每次 Gradle 调用只能构建一个 MC 版本(NeoGradle userdev IDEA 扩展冲突,历史限制)。用
-Pactive_versions=<v>覆盖gradle.properties的默认值(当前默认 1.21.1,便于 IDEA 直接导入 v1_21_1 模块;CI 与各 run 配置均显式传-P,不受默认影响)。 - NeoGradle 全平台统一 7.1.38(含 v1_21_1/3/4/5——曾用 7.0.171,与 Gradle wrapper 9.5.1 配置阶段不兼容已升级)。wrapper 9.5.1 满足全部模块(forge_26_1_2 的 ForgeGradle 7 要求 ≥9.3)。
- forge_1_20_1 发布产物必须走
:forge_1_20_1:renameJar(net.minecraftforge.renamer插件,official→SRG 重映射):1.20.1 生产运行时是 SRG 名,jar直出产物会在第一个改名过的 Minecraft 成员调用处NoSuchMethodError/NoSuchFieldError(2.0.0-beta 的CriteriaTriggers.register线上事故即此因);发布物为build/libs/csgobox-forge-1.20.1-<mod_version>-srg.jar,普通jar产物仅 dev 用。 - 物品注册表必须是编译期常量(v2.0.1 联机事故教训):禁止在
RegisterEvent里按config/csbox/内容或本地模组环境注册物品——物品注册表是同步且启动期冻结的,客户端与服务端一旦不同就会以「Failed to synchronize registry data from server / 模组版本不匹配」拒绝连接(两侧版本号还会显示成一样,极难排查)。箱子 = 数据(config/csbox/*.json+ 物品的csgobox:box_id标签 + 服务端同步的BoxRegistry),发放走/csbox give <玩家> <箱子> [数量];要新增"随模组发布的默认箱子"就在各平台ModItems里加一个固定物品(fixedBoxItem),不要恢复动态注册。 - NeoForge 核心平台模块:
v1_21_1(NeoForge 21.x,旧 API)+v26_1_2/v26_2(NeoForge 26.x,decoupled API);另有 Forge 四平台与fabric_1_21_1(见下文与「新平台补充矩阵」)。已归档(EOL)平台v1_21_0/v1_21_3/v1_21_4/v1_21_5/v1_21_8/v1_21_10/v1_21_11于 2026-08-09 从仓库删除,最后状态在 tageol-legacy-21x-1.0.6,复活需从该 tag 检出。 - v1_21_1 有 compileOnly TACZ 依赖(永恒枪械工坊:零,检视视口集成):jar 不入库(~57MB,仓库惯例
*.jar全局忽略、只提交 pom),首次构建已自动引导补齐(gradle/bootstrap-local-deps.gradle共享挂件:v1_21_1 / forge_1_20_1 任务接线、fabric_1_21_1 配置期接线——Loom 对modCompileOnly急切解析发生在任何任务之前,任务接线不生效,2026-11-04 实测;手动等效scripts/bootstrap-dev.sh [平台],幂等);原手动脚本scripts/download-tacz.sh(填充local-repo/com/tacz/并从 jarjar 提取simplebedrockmodel)保留,CI 仍显式执行(产物齐全时构建内引导零开销跳过)。运行时经ModList.isLoaded("tacz")检测,无 TACZ 环境功能静默降级。forge_1_20_1同样有 TACZ 依赖(official 1.20.1 构建),脚本scripts/download-tacz-1201.sh(产物同机制,jar 入local-repo/不提交,CI 自动执行)。fabric_1_21_1亦有 TACZ 依赖(TaCZ: Refabricated,Fabric 移植,mod id 同为tacz):脚本scripts/download-tacz-fabric.sh(cursemaven 主路由 + Modrinth CDN 兜底),依赖声明用modCompileOnly(生产 jar 为 intermediary 命名,须由 Loom 重映射到官方映射;与 NeoForge 平台的 plain compileOnly 实质不同),运行时经FabricLoader.isModLoaded("tacz")检测,另需从 jarjar 提取simplebedrockmodel-fabric(pom 随库提交)。dev 环境注意:Refabricated 要求fabricloader >= 0.17.2,与 dev 钉的 0.16.14 冲突——dev runClient/runServer 无法加载 TACZ 联测,生产环境不受影响(详见docs/PORTING-fabric.md§TACZ 适配)。 - Superb Warfare(卓越前线)零依赖 3D 预览(2026-10-25):SBW 官方双线(NeoForge 1.21.1 / Forge 1.20.1,mod id
superbwarfare,Kotlin + Kotlin for Forge,geckolib/sbm 等已 jarjar 自捆绑;GitHub master 分支是过时的 MCreator 老码、mod idtarget,勿作依据,跟进上游以发布 jar javap 为准)。其枪械是普通注册物品(43 把,无需 NBT,入包自动初始化),模型经separate_transforms把 gui 透视指到 2D 图标、3D(GeckoLibGeoItemRenderer+ 包内models/displaysettings/*.item.json,fixed 透视按展示框一格标定)只在其余透视渲染。集成方式与 TACZ 相反——无 compileOnly、不触碰任何 SBW 类:v1_21_1/forge_1_20_1/fabric_1_21_1的AnimRenderOps.renderItem3D各有isSuperbWarfareItem(命名空间门控:物品存在即证明模组已载,无需 isModLoaded)分支,改用ItemDisplayContext.FIXED走 vanilla ItemRenderer,异常降级 2D 图标;GeckoLib 自定义渲染器无 quad 可测,auto-fit 与居中经离线顶点探测(fixedContextBounds:吞掉全部顶点的空MultiBufferSource+VertexConsumer以恒等栈跑一次 FIXED 渲染记录顶点包络,块单位、含 fixed transform 与骨骼链;结果按 BakedModel 缓存,探测失败/无顶点回退原尺度)接入同款fitScaleFor0.82 规则——2026-11-12 L4 实测发现 IGLA 等长枪按调用尺度原样渲染、在终端机报价大图溢出预览框数倍后修复。26.x 平台无 SBW(其 mc 范围[1.21.1,1.22))不加分支。示例箱compat-packs/superb-warfare-pack/(id 取自 0.8.9.2 jar 实测;SBW 资产保留所有权利,只能引用 id、不得搬运贴图/geo/音效)。待人工 L4:装 SBW+KFF 实机复核 FIXED 透视 auto-fit 观感(终端机报价 / 检视屏 / 批量总览三处);fabric 侧 ReFabricated 的模型接线未核验(门控天然失效即维持现状,最坏为平面图标)。 - SBW dev 测试环境(2026-10-25 部署,
scripts/download-sbw-dev.sh):向v1_21_1/runs/{client,server}/mods/投放 superbwarfare-0.8.9.2.jar + kotlinforforge jar,并把superb-warfare-pack的箱子和价格表(幂等合并、既有键优先)部署进两侧config/csbox/——全部落在 gitignore 的 runs/ 内,零 build.gradle 改动、CI 无感知。注意 NeoGradle userdev 的运行目录是runs/<name>/(v1_21_1/run/是 8 月 shader 测试的遗留,别投错)。IDEA 用既有MC 1.21.1 - Client/Server共享运行配置直接启动(runClient 连 runs/client/ 的 SBW)。KFF 版本坑(runServer 实证):必须 5.12.0(CF 351264 file 8335665),不能用 SBW mods.toml 下限 5.8.0——kfflang 5.8.0 硬链接net.neoforged.fml.Bindings(仅存在于更新 FML),我们 NeoForge 21.1.248 的 FML loader 4.0.43 只有IBindingsProvider,5.8.0 在 SBW 的@EventBusSubscriber注入处必炸ClassNotFoundException。runServer 已验证:SBW/KFF/geckolib/sbm 全链路加载、csgobox:sbw_crate/sbw_terminal注册、物品 id 校验零告警。 - 同步开发模块
forge_26_1_2(MinecraftForge 26.1.2-64.1.0,Java 25):已注册在settings.gradle(-Pactive_versions=forge-26.1.2),随 1.0.6 发行 纳入 git 管理,自 2.0.0 线起纳入同步开发——与v26_1_2基准保持特性同步(同一mod_version,经scripts/port-forge-2612.py机械转换 + 手工适配,见「平台模块镜像纪律」§forge 同步),build.gradle的 2.0.0 线排除清单已随首轮同步删除;不在 CI 矩阵(手工/门禁脚本发布);自 2.0.0 起纳入正式发布(与 v26_1_2 同步发行,勿当测试平台对待)。测试流程与发布门禁见docs/TESTING-FORGE-2612.md。 - 正式发布模块
forge_26_2(MinecraftForge 26.2-65.1.1,Java 25,2026-08-14 首建):注册在settings.gradle(-Pactive_versions=forge-26.2)。2.0.0 线已追平:以forge_26_1_2(1.0.6/2.0.0 同步线)为基准整模块迁移,经scripts/port-forge-262.py机械移植(包名forge_26_1_2 → forge_26_2+ Forge 26.1.2→26.2 API 映射)+ 手工适配(Options.hideGui移除 →utils/HudVisibility、setScreen→setScreenAndShow、advancement 包迁移、PIP 渲染器保持 Forge 欧拉角方案——event.register(new Icon3DRenderer())+getRenderState().addPicturesInPictureState,与 NeoForge 26.2 的 Quat/Supplier 方案不同),build.gradle的 2.0.0 线排除清单已删除,PlatformSmokeTest已改为断言 2.0.0 物品存在。不在 CI 矩阵、不参与 3 平台镜像纪律与 AnimRenderOps 漂移门禁(与forge_26_1_2同策略)。2026-08-18 全面审计确认:test-forge-262.sh7/7 PASS,5 平台clean compileJava全通过,版本四同步 OK,资源一致性已补齐(4 个物品定义从forge_26_1_2补入)。测试流程与迁移记录见docs/TESTING-FORGE-262.md。 - 正式发布模块
forge_1_20_1(MinecraftForge 1.20.1-47.4.22,Java 17,ForgeGradle 7.x,2026-08-18 首建):注册在settings.gradle(-Pactive_versions=forge-1.20.1)。2.0.0 线功能向 MC 1.20.1 的回移,以forge_26_1_2为基准、复用common/全部纯 Java 逻辑;三大重写区域:Networking 改SimpleChannel(14 packet)、Capability 走LazyOptional+AttachCapabilitiesEvent、渲染层GuiGraphics直调(无 PIP 系统,普通物品AnimRenderOps.renderItem3D降级 2D、supports3D()返回 false,但 TACZ 枪械经renderGunModel3D全 3D——RenderSystem.getModelViewStack()在 1.20.1 返回PoseStack,用pushPose+mulPoseMatrix而非 26.x 的 Matrix4fStack 方案);DataComponent 存储回退 ItemStack NBT,StreamCodec/RegistryFriendlyByteBuf改FriendlyByteBuf手动序列化。TACZ 检视(TaczInspectViewport+BoxItemCodec.validateTacz)与 TACZ 依赖同v1_21_1机制(scripts/download-tacz-1201.sh,gun tag 读顶层 ItemStack NBT)。JEI 已接入(cfd750f,JEI 15.x 开箱概率查询,4 文件jei/包 +BoxJeiSync静态桥,mods.toml可选依赖[15.20,),与 NeoForge 三平台机制一致)。不在 CI 矩阵、不参与 3 平台镜像纪律与 AnimRenderOps 漂移门禁(与其他 forge 模块同策略),自 2.0.0 起纳入正式发布。测试流程见docs/TESTING-FORGE-1201.md,迁移计划见.opencode/plans/2026-08-18-forge-1-20-1-port.md。
common/不得 import 任何net.minecraft.*/net.neoforged.*(编译环境无 MC classpath,违反即编译失败)。共享资源(纹理/音效/lang/配方/成就)也放在common/src/main/resources/。该约束由:common:checkCommonArchitectureGradle task 自动化执行(挂载在compileJava依赖上,任何编译/测试都会触发,含 forge_26_1_2 把 common 源码编进自身 classpath 的场景)。- 平台模块通过
srcDir project(':common').file('src/main/resources')共享资源。 common/src/main/mcshared/(2026-09-27 新增)— 跨时代共享的 MC 绑定源码根:八平台经srcDir project(':common').file('src/main/mcshared')直编(forge_1_18_2排除mcshared/wthit/**:1.18.2 无 WTHIT 构建),存放各平台逐字节一致(包名归一化后哈希相同)、只依赖 1.18.2~26.2 全线稳定 MC API 与 loader 中立第三方 API(WTHIT/Cloth)的类。当前 7 类 + 1 package-info(共 8 文件):mcshared/box/BulkOpenResult、mcshared/packet/PacketValidation(原包私有,已改 public)、mcshared/gui/terminal/TerminalOfferItems、mcshared/gui/config/SharedClothConfigScreen(Cloth 配置屏唯一实现,2026-11-10 自八平台副本注入式下沉:平台薄壳传CsgoBox.CONFIG(实现 commonCsboxConfigAccess)+ 文案工厂(1.18.2 传TranslatableComponent::new,其余Component::translatable)+ 保存回调(specsave()/ fabricsaveConfig()))、mcshared/terminal/TerminalRoundData、mcshared/util/WorldClock(世界钟:game ticks × 50 → ms 的唯一换算点,谈判倒计时/终端状态域专用——getGameTime()的 tick 域调用(OpenBlockGuard/BoxConstraintTracker/批量屏)保持裸 tick,不得经此)、mcshared/wthit/RecyclerBlockProvider。该目录合法 import MC,checkCommonArchitecture只扫src/main/java/src/test/java、不覆盖它;check-platform-drift.py两个家族中这些文件已随之消失。下沉新文件前必须先验证各平台副本归一化哈希一致;已知暂不可下沉:wthit/CsgoBoxWthitPlugin(引用平台方块类)。- 依赖方向:
平台 → common,common不依赖任何平台。 premium_supply_box/ItemPremiumBox永久移除(2026-08-19):该「军火商高级箱」物品已从代码、资源、配置与全部文档中彻底删除,禁止以任何形式再次引入(含历史条目复活)。forge_26_1_2/forge_26_2的PlatformSmokeTest含反向守卫(premiumBoxItemIsPermanentlyRemoved)断言该字段永不回归,新增字段/资源/交易时勿再使用该命名。
3 个 NeoForge 平台模块不是纯拷贝:26.2 有 API 适配(如 BuiltInRegistries.ITEM.get() 返回 Optional、spawnAtLocation(ServerLevel,...)、lookup()、MouseButtonEvent 事件、setScreenAndShow、PIP 渲染器等)。禁止用 v26_1_2 整文件覆盖 v26_2——会破坏适配(历史教训,曾导致 v1_21_10 编译失败;该平台现已与其余 legacy 一并归档)。
跨平台改动的正确姿势:
- 先改基准模块:new 用
v26_1_2(legacy 唯一模块v1_21_1直接改) scripts/mirror.sh new <rel-path>— 仅用于无适配差异的纯新增文件(目标已存在会警告跳过,--force覆盖,--dry-run预演不写盘)- 有适配差异的文件用定点合入(
v26_1_2→v26_2手工适配;幂等合入脚本范例与scripts/port-12111.py已随 EOL 平台删除) - 每平台
compileJava验证(增量缓存可能造假象——改动涉及平台时用clean编译确认)
forge_26_1_2 与 v26_1_2 保持特性同步,但 loader 不同(MinecraftForge vs
NeoForge),整文件覆盖同样禁止。同步纪律:
- 基准仍是
v26_1_2(先改基准模块); scripts/port-forge-2612.py做机械转换(包名v26_1_2 → forge_26_1_2+ NeoForge→Forge import/API 映射),只负责纯机械文件与新增文件;- 有适配差异的文件(入口
CsgoBox.java、Networking、ModItems、ModCapability、 packet handler、GUI/渲染层、AnimRenderOps 等)走手工适配,不得被脚本覆盖 (--force仅用于确认无本地改动时重灌); - forge 侧专有的修复(如
GuiItemMovePIP 高清 3D、items/模型定义)保留在 forge 模块内,同步时手工合入对应 v26_1_2 改动; - 每次同步后:删除
build.gradle中已同步的 2.0.0 排除项 →clean compileJava(-Pactive_versions=forge-26.1.2)→scripts/test-forge-2612.sh门禁 → 必要时 L4 运行时回归。漂移盘点用scripts/port-forge-2612.py --dry-run。
gradle.properties 的 mod_version= + neoforge.mods.toml(模板变量 ${mod_version} 自动注入)+ CHANGELOG.md + README.md。发布流程见 docs/RELEASE.md。一致性检查:scripts/check-version.sh(CI 的 common-test job 已接入)。
- 未经维护者明确给出新版本号,严禁修改
gradle.properties的mod_version—— 改版本号即发布动作,只能由维护者拍板执行。 - 教程仅在线版:源在
docs/tutorials/(文件名带版本号仅为归档习惯,与mod_version解耦),推送到 Gitee 在线仓库后由/csbox help的可点击链接打开;包内不再内置教程,config/csbox/不再生成_tutorial_v*.md(历史落盘文件不清理,可手动删除)。
CsgoBox.java— 平台入口;CONFIG为public static final(static 块初始化,勿改顺序);registerDynamicBoxItems注册config/csbox/*.json动态 item(用RegisterEventdeferred supplier,不要用FMLCommonSetupEvent.enqueueWork——registry 已 freeze)CsboxConfig.java— NeoForgeModConfigSpec/ ForgeForgeConfigSpec,builder 每个define*用.get();bulkOpenCount(0=无上限)服务端权威。v2.0.x 起带公开set*写方法(供 Cloth Config GUI 用)config/CsboxClothConfigScreen.java(八平台各一份,薄壳)— 可选 Cloth Config 配置屏:唯一实现在common/src/main/mcshared/.../gui/config/SharedClothConfigScreen(2026-11-10 注入式下沉,此前八平台各 ~105 行同文副本),条目读 commonCsboxConfigAccessgetter、写set*、保存经注入回调(CsgoBox.CONFIG_SPEC.save()/ fabricCsgoBox::saveConfig),不迁移配置存储(TOML/JSON 仍是唯一来源);平台壳只负责传入本平台CsgoBox.CONFIG、文案工厂(1.18.2TranslatableComponent::new)与保存回调,类名与create(Screen)签名不变——各平台ClothConfigHook/CsgoBoxModMenuHook注册点零改动(服务端永不加载该类,见各 hook 的 issue #2 注记);Cloth 未装时无 Configure 按钮、功能不变packet/PacketCsgoProgress.java— 开箱/钥匙/箱子扣减助手(服务端权威 RNG 与 10-tick 防连点已上移OpenService/ commonOpenBlockGuard)。钥匙消费铁律(2026-11-10):多钥匙(keysany-of)的批量消费必须走tryConsumeKeysExact(对全部 accepted ids 先计数、总量不足分毫不动、再统一扣减)——any-of/单 id 重载是边扫边扣语义,count > 1 时部分匹配会销毁已扫钥匙后失败(2026-11-10 修复的丢钥匙 bug);单开 count=1 可继续用 any-of 重载packet/PacketCsgoBulkProgress.java— 批量开箱(异步线程池BULK_COMPUTE_POOL+ 主线程 finalize;finalize 扣减顺序:时间窗 → 钥匙 → 点数 → 箱子已在更早阶段扣减,任一失败不产生白扣)gui/CsboxBulkOverviewScreen.java— 批量开箱总览屏(Shift+右键进入;点「开启」直接发包PacketCsgoBulkProgress并进CsboxProgressScreen,无二次确认屏;服务端权威复核库存与扣减)common/box/BoxDefaults.java— 默认_villager_prices.json写入 + 遗留terminal.json一次性迁移(空文件视为合法未配置;损坏保留原位 +terminal.json.corrupt-<时间戳>备份;不生成任何默认箱子/终端配置)。原教程安装器(writeTutorialIfMissing/refreshTutorials)已随本地教程功能整体移除common/box/BoxGrades.java/BoxRegistryStore.java/BoxStripGenerator.java— 等级常量与纯函数(v2.x 起档位等级按位置推导levelAt:内部列表高→低,level = size - index;gradeLevel(id)仅映射 legacy 五档 id)/ 泛型注册表容器(回调契约固化)/ 泛型开箱滚动条(2026-08 重构下沉,平台BoxDefinition/BoxRegistry/packet 引用指向 common)common/logic/OpenBlockGuard.java— 服务端权威开箱冷却(10 tick,isBlocked/block/tick),八平台 packet 与ModEvents#serverTick共用common/config/CsboxConfigDefaults.java— 八平台CsboxConfig默认值与取值范围唯一来源(枚举默认以常量名字符串存储)common/box/BoxOdds.java— 纯概率数学(totalWeight/gradeChance/itemChance/percent/ v2.0.1 起weightedItemChance/positiveItemWeightSum);JEI 分类、/csbox info、箱子 tooltip 概率行共用同一来源(与服务端 roll 同口径)item/ItemCsgoBox.java— 箱子物品;appendHoverText显示 5 档概率行 + 每档物品清单(v2.0.1 加权时逐物品显示概率);buildGradeMap()构建加权池;applyIcon应用每箱图标(26.x 支持 ITEM_MODEL,1.21.1/1.20.1 仅 CMD)box/BoxItemCodec.java— v2.0.1 物品解析(weight/count:[min,max]/#tag展开 /loot_table/enchant/ 缺模组 id 区分;item_spec标记);forge_1_20_1 用 NBT 变体、v1_21_1 保留 TACZ 特判。路径注意(2026-11-01 核对):PriceTable/PriceRange/LegacyPriceMigration/PriceTableRegistry位于 common(common/src/main/java/com/reclizer/csgobox/box/);BoxItemCodec/BoxItemResolver/GradeGroup/BoxDefinition/BoxJsonLoader(连同BoxRegistry/LoadError/BulkBoxContext)自 v2.0.x 重构后位于各平台box/包(非 common)box/BoxItemResolver.java(v2.0.1 新增,每平台一份)— 开箱/购买时解析item_spec(count 区间、随机附魔、loot_table 掷表),仅服务端调用box/GradeGroup.java— v2.0.1 第 7 字段itemWeights(平行列表,默认全 1 = 均匀);itemWeightAt/positiveItemWeightSumbox/BoxDefinition.java— v2.0.1 新字段enabled/requires/icon/discount/stock/maxPerPlayer/cooldownSeconds/permission;discountedPrice/missingRequirement;v2.x 档位可配置:可选顶层"grades"数组(低→高,{id, name?, color?, weight?, items, stock?},1..MAX_GRADES档)整体替代random+grade1..5(缺省 = 默认 5 档,legacy 行为不变);等级按位置推导(BoxGrades.levelAt),协议零改动(GradeGroup逐档同步 id/name/color/weight);saveToFile(forge 线)对非 legacy 形态的箱子回写grades格式。v2.x 玩法字段(全部可选、缺省=旧行为):hide_odds(隐藏概率行)/allow_bulk(批量门禁)/keys(多钥匙 any-of)/wear:[min,max]/bonus{chance,mode:double|box}(单开)/announce_above(全服播报)/available_from/until(ISO-8601 时间窗)/open_cost(开箱扣武库点数)/pity.first_open(首开必出,内存态)/negotiation{rounds,timeout_minutes,price_bias}(终端谈判参数,服务端-only 零 wire)/upgrade{target,consume}(/csbox upgrade,全员可用);自定义档位stock走TierStockManager(内存态);sound已接入 JSON 配置(自定义开箱音效,未知 id 播放时回退默认音);texture仅 wire 同步、无 JSON 入口与消费者(预留)box/BoxJsonLoader.java— v2.0.1 文件名校验 / enabled+requires 门控 / 空档位 warning //csbox validate干跑(validateFile)/ 新字段解析box/PriceTable.java(v2.0.1 新增)— 全局终端价格表config/csbox/_prices.json的纯函数解析/查价(物品 id → 固定非负整数或[min, max]随机范围,PriceRange;id#变体子键支持 TACZ 等 NBT 变体定价;未命中 = 无价格(终端机不售、拆解 0,装载器对未定价 id 物品报错,无默认价回退));lookupRange+PriceRange.sample(IntUnaryOperator)服务端每次报价/拆解采样;box.schema.json已移除物品级price,残留字段由BoxJsonSchemaValidator报错box/PriceRange.java(v2.0.1 新增)— 固定价/范围价值对象([min, max]闭区间、UNPRICED哨兵、sample取整),纯 Java 无 MC 依赖;八平台GradeGroup.priceForIndex返回PriceRange,网络流按[min, max]双 int 序列化box/LegacyPriceMigration.java(v2.0.1 新增,位于 common)— 旧版适配:每次loadAll//csbox reload前把残留的旧price自动转移进_prices.json(同物不同价按平均值四舍五入;表已有价优先;#tag/loot_table/非法价保留报错;表损坏则整体中止不写入),并幂等清除已迁移字段(八平台BoxJsonLoader各在 loadAll + reloadPreserving 调一次)box/PriceTableRegistry.java(v2.0.1 新增)— 当前价格表的跨平台持有者:八平台BoxJsonLoader每次 load/reload 发布(/csbox validate 干跑不发布),武库拆解台只读消费,按PriceTable.recycleYield(表价 × 90% 向上取整) 计价,表外未定价物品不可拆解(产出 0)- Create/自动化联动(v2.0.1):武库拆解台开放物品处理 capability——Forge 三平台
ArmoryRecyclerBlockEntity覆盖getCapability(ForgeCapabilities.ITEM_HANDLER+ 内部RecyclerHandler),NeoForge 1.21.1(v1_21_1)用RecyclerAutomation(@EventBusSubscriber(Bus.MOD)+RegisterCapabilitiesEvent.registerBlockEntity, 同包ArmoryRecyclerBlockEntity.RecyclerHandler为 public)。输入槽只收 grade 印记 物品、输出槽只可提取武库点数;Create(1.20.1/1.21.1)机械臂/传送带/管道可直接自动化。v26_1_2/v26_2(NeoForge 26.x)刻意未做——该线 capability 已迁移 transfer API (Capabilities.Item.BLOCK=ResourceHandler<ItemResource>)且 Create 无 26.x 构建, 待 Create 26.x 发布后按 transfer API 适配 common/logic/BoxConstraintTracker.java(v2.0.1 新增)— 开箱约束(max_per_player / cooldown)内存追踪,八平台共用common/terminal/TerminalStockManager.java(v2.0.1 新增)— 终端库存内存态(stock售罄即止,重启回满;补货设定已于 2.0.2 移除),八平台共用command/CsboxCommand.java—/csbox命令树;showInfoOverview末尾输出「物品来源模组」命名空间统计(v26 系BuiltInRegistries/ forge_1_20_1ForgeRegistries);v2.0.1 起/csbox validate [box]干跑 + info 展示新字段command/EditorCommand.java(八平台各一份,v2.0.1 新增)— 独立类注册/csbox editor子命令(全员可用,无需 OP),聊天栏输出可点击的网页配置工具链接(https://wikkd.github.io/CS2-Box/);链接能装下时经common/box/EditorPack(Gson 清单 → zlib deflate → base64url)内嵌config/csbox/*.json+_prices.json到 fragment(#pack=),网站解压后自动导入工作区草稿(超限/读失败回退纯链接,版本键与站点 versions 对齐:1.21.1/26.1.2/26.2/forge-26.1.2/forge-26.2/forge-1.20.1/fabric-1.21.1/forge-1.18.2),并在输出前追加一行本地配置统计(common/box/EditorPackStats.summarize扫描config/csbox/的箱子数 / 物品条目数 / 未定价 id 数,lang 键commands.csgobox.editor.stats中英双语,目录缺失或空统计静默跳过;_前缀文件与坏 JSON 容错跳过);CsboxCommand的/csbox help复用EditorCommand.EDITOR_URL聚合同一链接,并另给可点击入口:在线教程(Giteedocs/tutorials/),help 全员可用(info/reload/validate/give权限不变);新增/调整需八平台同步并逐平台编译验证scripts/add-biome.py— 向has_structure/*群系标签追加 biome(去重/幂等/--dry-run/--replace),让武库商小屋在模组群系生成(机制见docs/BIOME-INTEGRATION.md)scripts/boxgen.py/scripts/check-ids.py(v2.0.1 新增)— 箱子配置生成器 / compat-packs id 核查工具scripts/sync-box-editor-data.py(v2.0.1 新增)+box-editor/— 可视化网页配置工具(纯前端零构建,file://可离线打开,可整目录发布 GitHub Pages;中英双语、跟随八平台版本控制 TACZ 变体/Data Components 可见性(TACZ 专属字段另有手动开关可强制启用/禁用;撤销/重做、校验项点击定位、物品 复制/批量粘贴、价格批量导入(固定价或min-max范围价)、分享链接、#pack=游戏链接导入(/csbox editor内嵌 config/csbox 自动导入 + 物品/生物注册表 id 同步进浏览器本地补全,解码在app.js的loadHashPack,损坏 JSON 经导入对话框预填原文修正并以原文件名存为新草稿;导入幂等——fragment 读毕即window.history.replaceState摘除(app.js 的history常量遮蔽全局 history,必须用window.history),箱子按源文件名+FNV-1a 指纹slot防重导,重复打开/刷新不再复制草稿)、未知 id 校验(物品/生物/价格键对照注册表镜像,缺失即问题列表点名)、#pack=导入冲突对话框(同名不同内容逐件询问覆盖/保留/跳过)、草稿徽标(版本+相对时间,tooltip 带源文件名)、#workspace=整工作区分享(全草稿+价格表压缩进链接,导入幂等)、价格页 EV 面板(逐档期望值与拆解回收期望、未定价/空档位警告分级)、应用到游戏(File System Access API 直写config/csbox/*.json+_prices.json,无showDirectoryPicker的浏览器自动隐藏入口)、悬浮教程、折叠记忆;改动后跑npm run test:smoke(Playwright 冒烟,CIbox-editor-smoke.yml已接线));实时预览 + 轻量校验 + 旧版内联price自动迁移(平均/已有价优先,与LegacyPriceMigration一致);数据生成式:schema/示例/版本元数据内嵌进js/data.js,修改docs/box-schema/*.schema.json或docs/examples后必须重跑该脚本,勿手改data.js)。本地部署套件:server.mjs(零依赖静态服务器)/build.mjs(产出dist/,已 gitignore)/start.bat+start.sh/package.json(npm start|build|sync);scripts/sync-box-editor-data.py亦可由npm run sync触发(Windowspython为占位符时用 venv python 直跑);GitHub Pages 自动发布走.github/workflows/box-editor-pages.yml(Settings → Pages → Source: GitHub Actions)compat-packs/— 官方联动数据包示例(Apotheosis / Iron's Spells / TACZ 各一个箱子包 + README,v2.0.1 示范 requires/weight/count 区间/enchant)docs/box-schema/box.schema.json(v2.0.1 新增)— 箱子配置 JSON Schema(IDE 补全用;含 v2.xgrades自定义档位定义)utils/AnimRenderOps.java— 动画渲染唯一适配点(各平台一份,// era: legacy|decoupled头标注):屏与逻辑助手只经它调用渲染原语(blitTextured×3 变体 /fill/fillGradient/scissor/scissorDisable/setBlendNormal/flush/renderBlurredBackground/renderItem2D/renderItem3D/supports3D,共 13 个公开 op)。跨平台签名一致性由scripts/check-animops-drift.sh守护(CIcommon-testjob 已接线)。新增原语须三平台同步补,否则漂移检查失败。Shader 兼容:supports3D()返回!isShaderModActive()(检测iris/oculus),shader 激活时renderItem3D内部回退renderItem2D(公开 op 表面不变,drift 仍过);见docs/SHADER-COMPAT.md。Modern UI 软兼容:装modernui不降级 3D(官方声明兼容 vanilla GUI 系统模组),首次supports3D()调用打印渲染环境诊断([csgobox] 3D preview env: ... modernui=...);策略与验证清单见docs/MODERN-UI-COMPAT.mddocs/SHADER-COMPAT.md/docs/DESIGN-jade-wthit-integration.md/docs/DESIGN-rarity-mapping.md/docs/DESIGN-terminal-protocol.md— 批次 B 与立项待办的设计/兼容文档(Jade/WTHIT 待联网接线,RFC 与终端机协议待评审)utils/IconListTools.java— 2D 物品网格(26.x/1.21.8+ 有 per-item bounding box 居中;渲染原语已委托 AnimRenderOps)utils/GuiItemMove.java— 3D 拖拽预览(renderRotAngleX/Y纯数学保留,渲染委托AnimRenderOps.renderItem3D)utils/ButtonPalette.java(v26_1_2 / v26_2 / forge_26_1_2 / forge_26_2 / forge_1_20_1)— 按钮调色板常量(CLOSE 等),仅 v1_21_1 未采用utils/HudVisibility.java(v26_2 / forge_26_2)— 26.2 无Options.hideGui,用Minecraft.gui.hud.toggle()/isHidden()包装common/utils/—ColorTools/OverlayColor(三档 token:surface/panel/divider)/GuiRegion(容器化布局)/EntityChineseMapadvancement/OpenedBoxTrigger.java—csgobox:opened_boxtrigger +Stats.CUSTOM累加event/BoxOpenedEvent.java— NeoForge 事件总线开箱通知(post-event,KubeJS 兼容,见docs/KUBEJS-EVENTS.md)
common/src/main/resources/data/csgobox/recipe/(单数 recipe)。csgo_key3 仅锻造台(smithing_transform)。
时代注意(2026-11-06 修复):单数目录是 1.21(24w21a)布局——forge_1_20_1 / forge_1_18_2 只读复数名,靠各平台 recipes/ / advancements/ / loot_tables/(1.20.1 另有 structures/)时代映射副本生效(字段翻译:icon/result 的 "id"→"item"、ItemPredicate.items 裸字符串→数组;1.18.2 的 csgo_key3_smithing 用旧 minecraft:smithing,无 template 项),两平台 processResources 同时排除死单数目录;common 继续单源,改共享配方后必须同步重生成两平台副本。1.18.2 无 netherite_upgrade_smithing_template 物品。
common有 JUnit 5(2026-11-01 核对:37 个测试类 / 339 用例,其中BoxJsonSchemaValidatorTest54 用例):./gradlew :common:test(CI 独立common-testjob 跑一次,不再随各平台矩阵重复执行)common架构约束检查由:common:checkCommonArchitecture自动挂载在编译上(见「架构约束」节)- AnimRenderOps 跨平台签名漂移检查:
scripts/check-animops-drift.sh(3 平台,CI 已接线,本地改门面后必跑) - 平台层最小测试:八平台均有
PlatformSmokeTest(JUnit 5,验证入口类可加载,不初始化 MC 运行时;v1_21_1/v26_2于 2026-10-25 补齐——此前文档声称 v26_2 有而实际缺失):./gradlew :<module>:test -Pactive_versions=<v> - GameTest 集成测试(2026-11-07 起六平台有真实用例):
<module>/src/main/java/…/gametest/CsgoBoxGameTests.java,同一套 6 用例(config→registry 加载、单开 E2E 发放/消耗/防连点、缺钥匙拒开、first_open 保底、max_per_player、terminal 拒绝)跑在 v1_21_1 / v26_1_2 / v26_2 / forge_1_18_2 / forge_1_20_1(headless 命令统一./gradlew :<module>:gameTestServer -Pactive_versions=<v>,forge 两平台为别名→runGameTestServer;exit 码=失败用例数,CIgametest.yml五平台门禁)。注册机制分三代:NeoForge 21.1 / Forge legacy 用@GameTestHolder注解;NeoForge 26.x 无注解扫描——RegisterGameTestsEvent事件(userdev run 模板自动带neoforge.enableGameTest=true)+ TEST_FUNCTION 注册表(RegisterEvent)+ 每用例独立 environment(= batch);fabric 走fabric-gametestentrypoint(fabric-api)+ 复用EMPTY_STRUCTURE。模板.nbt分时代:1.18.2/1.20.1 复数structures/(2975/3465),1.21.1/26.x 单数structure/(3955/4790/4903),DataVersion 只能升不能降。forge_26_1_2/forge_26_2 无覆盖(上游 26.x 无注册路径:gatherTests无调用方、无事件);fabric_1_21_1 套件已就位但 dev run 被上游阻断(Loom officialMojangMappings 半重映射崩 server.LanguageMixin,与 runServer 同因,见 PORTING-fabric.md)——均见 TESTING-PIPELINE.md。26.x 总数显示 7:vanilla 数据包自带 1 个 always_pass 示例用例。开箱执行器接缝:packet 族BoxOpenExecutor.execute;NeoForge 26.xOpenService.openSingle(行为保持提取)。编写须知(gzip 模板/DataVersion/mock 玩家三代差异/fixture 定价/独立 batch/1.18.2 无 assertTrue)见docs/TESTING-PIPELINE.md§L3;测试流程分层(L1 单测 → L5 渠道 soak)总览同文档 - 其余平台暂无自动化测试;运行时回归清单见
docs/RELEASE.md质量门 - 代码审查标准与流程见
docs/CODE-REVIEW.md(专属审查清单:CONSTRAINT-001 / 镜像纪律 / 版本四同步 / AnimRenderOps 漂移 / 并发权威等);PR 描述模板.github/PULL_REQUEST_TEMPLATE.md由 CIpr-checks.yml校验;GameTest 集成测试 CI 见gametest.yml;分支保护设置见docs/CI-PROTECTION.md
| 文件 | v1_21_1 | v26_1_2 | v26_2 | forge_26_1_2 | forge_26_2 | forge_1_20_1 |
|---|---|---|---|---|---|---|
| TACZ compat (2 文件) | ✅ | — | — | — | — | ✅ |
ButtonPalette |
— | ✅ | ✅ | ✅ | ✅ | ✅ |
HudVisibility |
— | — | ✅ | — | ✅ | — |
| JEI (4 文件) | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ |
| REI (3 文件) | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ |
| EMI (3 文件) | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Jade (3 文件) | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ |
| The One Probe (3 文件) | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ |
| WTHIT (3 文件) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
PacketSyncBoxDefinitions |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Networking(Forge 专用) |
— | — | — | ✅ | ✅ | ✅ |
| Create 集成 | 4 文件 | ❌ | ❌ | — | — | 4 文件 |
| 文件数 | 104 | 96 | 97 | 84 | 85 | 105 |
- 刷新纪律:文件数格子易过期(上一次「刷新」只改了标题没改数字),改矩阵必须实际
find <module>/src/main/java -name '*.java' | wc -l重数后再填。
| 项目 | fabric_1_21_1 | forge_1_18_2 |
|---|---|---|
| TACZ compat | ✅ 2 文件(compat/TaczInspectViewport(Impl),TaCZ: Refabricated,检视 + 3D 枪械渲染) |
✅ 1 文件(门面恒不可用,数据级联动:物品校验,编译对包内签名桩——scripts/tacz-stub-1182-src/ 由 Gradle taczStubJar 任务在构建内编译,fresh clone 零准备) |
| JEI / REI / EMI | ✅ 4 / ✅ 3 / ✅ 3 | ✅ 4 / ✅ 3 / emi/BoxEmiReload 守卫桩,1.18.2 无 EMI 构建) |
| Jade / TOP / WTHIT | ✅ 3 / ❌ / ✅ 2(+mcshared 1) | ✅ 3 / ❌ / ❌(1.18.2 无 WTHIT 构建,mcshared/wthit 排除编译) |
| Create | create/CreatePlatformItems 反射降级,无 Fabric 构建) |
|
PacketSyncBoxDefinitions / Networking |
✅ / —(Fabric custom payload) | ✅ / Networking(SimpleChannel,FG5) |
| 文件数 | 103 | 94 |
- TACZ:
v1_21_1(unofficial 1.21.1 port,scripts/download-tacz.sh)、forge_1_20_1(official 1.20.1,scripts/download-tacz-1201.sh)与fabric_1_21_1(TaCZ: Refabricated 1.21.1 Fabric 移植,scripts/download-tacz-fabric.sh,modCompileOnly)有compileOnly依赖,其它平台不需要;forge_1_20_1的 gun NBT 在 ItemStack 顶层 tag(无 DataComponent 系统),BoxItemCodec.validateTacz直接读写stack.getTag(),枪 tag 的GunFireMode规范化在内联修正 - ButtonPalette:
v26_1_2/v26_2的 26.x 辅助类,Forge 三平台已同步移植(forge_1_20_1 亦接入),仅v1_21_1未采用(非功能阻塞) - JEI:NeoForge 3 平台已同步;
forge_1_20_1已接入(JEI 15.x,mods.toml可选依赖[15.20,));forge_26_1_2/forge_26_2缺失(已知待办,Modrinth 无 JEI 26.x Forge 构建) - REI:
v1_21_1(16.x)/v26_1_2(26.1.x)/v26_2(26.2.x)NeoForge 已同步——@REIPluginClient+ 动态显示生成器(/csbox reload/ 文件热重载后无需手动刷新),forge_1_20_1已接入(REI 12.x,mods.toml可选依赖[12.0,));forge_26_1_2/forge_26_2缺失(与 JEI 同策略,暂不接入) - Networking vs PacketSyncBoxDefinitions:Forge 用
SimpleChannel,NeoForge 用CustomPacketPayload,平台差异正常;forge_1_20_1同为SimpleChannel(Forge 47.x API)。Forge 三平台已补齐全量定义同步(PacketSyncBoxDefinitions,sync_box_definitions:玩家加入 //csbox reload/ 文件热重载时服务端广播整份BoxRegistry,客户端clear + register覆盖,forge_1_20_1同时刷新 JEI),与 NeoForge 三平台架构对齐——专用服务器下客户端定义内容(权重/价格/物品清单/JEI 概率)始终以服务端为准 - Validator:
BoxJsonSchemaValidator走common/唯一实现(八平台共用,无平台本地副本)。教程仅在线版(Giteedocs/tutorials/,/csbox help可点击链接,八平台同文):包内嵌教程复制器、/csbox reload tutorial子命令与死字段MODVERSION已随本地教程功能移除(联网下载类TutorialFetcher/TutorialSources更早随 v2.0.1 删除),运行时零网络外联 - 代码审查标准与流程见
docs/CODE-REVIEW.md(专属审查清单:CONSTRAINT-001 / 镜像纪律 / 版本四同步 / AnimRenderOps 漂移 / 并发权威等);PR 描述模板.github/PULL_REQUEST_TEMPLATE.md由 CIpr-checks.yml校验;GameTest 集成测试 CI 见gametest.yml;分支保护设置见docs/CI-PROTECTION.md