ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Schedule I 0.4.7 IL2CPP Beta 的 QoL 套件与堆叠上限 Mod:基于 MelonLoader 的 Unity IL2CPP 托管补丁实战

Schedule I 0.4.7 IL2CPP Beta 的 QoL 套件与堆叠上限 Mod:基于 MelonLoader 的 Unity IL2CPP 托管补丁实战 【免费下载链接】universal-modderPoint Claude at any game. Skills, tools and the fal MCP that let Claude Code mod almost any PC game you own: recon, reverse engineering, fal-generated art/3D/audio, in-game testing, showcase videos.项目地址https://gitcode.com/gh_mirrors/un/universal-modder点击查看免费下载本文基于 universal-modder 仓库知识库中的实战记录字段笔记完整还原了在 Schedule I 0.4.7 IL2CPP 测试分支上通过 MelonLoader 0.7.3 HarmonyX 编写两个 C# Harmony ModIncreasedStackLimit-Latest与PocketPlug的全过程从环境搭建、IL2CPP 逆向阅读、堆叠上限系统剖析到手机应用、指南针等 QoL 功能的运行时 uGUI 构建再到以 MelonLoader 日志 游戏内截图为神谕的验证闭环以及 12 条可直接复用的 symptom→cause→fix 实战陷阱。读完本文你将掌握在 Unity IL2CPP 游戏上实施 managed-patch 路线的完整方法论并了解如何用 Harmony 补丁 Il2CppInterop 直接字段写入来修改读取定义字段而非实例 getter的硬编码逻辑。前置背景为何选择 managed-patch 路线Schedule I 0.4.7 测试分支是IL2CPP 后端的 Unity 游戏Steam 3164500beta 分支buildid 25698382版本号 0.4.7f9而对应的alternate-beta分支是Mono后端0.4.7f6。两条分支的 C# 逻辑相同Mono 版反编译后近乎原始源码因此被用作可读性参考。按 unity.md 引擎手册 的识别方法IL2CPP 安装存在GameAssembly.dll与Game_Data/il2cpp_data/Metadata/global-metadata.dat逻辑已被编译为原生代码Mod 必须通过生成的互操作程序集工作。两个 Mod 选择了managed-patch托管补丁路线Harmony 补丁 通过 Il2CppInterop 程序集直接写入字段不使用 AssetBundle所有 UI 均以 uGUI 在运行时构建。这条路线的选择理由与 mod-any-game 技能 中加载器 API 无法覆盖目标时使用托管代码补丁的判定一致——S1APIifBars 的社区 API 库在 0.4.7 当时处于损坏状态issue #305无法依赖因此完全绕开它自研。本仓库中关于神谕Oracle验证方法的专题笔记 oracles-how-agents-know-a-mod-works.md 概括了本案例采用的验证哲学运行中的游戏本身就是神谕对代码的阅读不是并强调诚实列出未验证项。环境搭建Setup游戏与启动方式项目值游戏Schedule ISteamLinux经 GE-Proton11-6 运行测试分支betaIL2CPP0.4.7f9buildid 25698382可读参考分支alternate-betaMono0.4.7f6与匹配的 IL2CPP 构建 C# 相同反编译近乎源码加载器MelonLoader 0.7.3MelonLoader 0.7.32026-05-14 发布以version.dll代理方式安装。Steam 启动选项WINEDLLOVERRIDESversionn,b %command%与 unity.md 引擎手册 中Proton/Linux 下 MelonLoader 需设置WINEDLLOVERRIDESversionn,b的说明一致。首次 IL2CPP 启动时MelonLoader 会用 Cpp2IL 与 Il2CppInterop 生成MelonLoader/Il2CppAssemblies耗时约 30 秒日志中出现的Failed to restore 885 methods / 3 fields是无害的可忽略。构建环境.NET SDK 10目标框架net6.0。需要引用的程序集MelonLoader/net6/{MelonLoader,0Harmony,Il2CppInterop.Runtime,Il2CppInterop.Common}.dll加载器与互操作核心MelonLoader/Il2CppAssemblies/*.dll由首次启动生成的游戏互操作程序集。反编译工具ilspycmd 11.1。按 reverse-engineering 技能 的说明Unity IL2CPP 应先用 Cpp2IL/Il2CppDumper 处理GameAssembly.dllglobal-metadata.dat得到类型、字段、方法签名和供 ILSpy 阅读的 dummy DLL方法体为原生代码需要时再用 Ghidra/IDA 配合生成的脚本命名函数。无 Steam 的实验室lab使用umu-run设置GAMEIDumu-3164500与PROTONPATHGE-Proton并在游戏目录放置steam_appid.txt。Steam 必须处于运行状态。实验室环境下的存档落在Saves/TempPlayer/而非你的 SteamID 目录——这是预期行为。S1API 状态S1APIifBars在 0.4.7 当时已损坏S1API#305 问题。这两个 Mod 不依赖它这正是本案例能独立成行的原因之一。游戏内幕必须学会的引擎事实堆叠上限Stack limits堆叠上限存储于物品定义上ScheduleOne.Core.Items.Framework.BaseItemDefinition.StackLimit是一个public int0.4.7 中它移动到了ScheduleOne.Core.dll。BaseItemInstance.StackLimit只是读取_definition.StackLimit。关键发现多个系统直接读取定义字段本身——DeliveryInstance、DeliveryShop.WillCartFitInVehicle、商店Cart、Supplier死投递点、SpecialCustomerLeader。因此旧 Mod 只补丁实例 getter 的做法会让这些系统仍然停留在原版尺寸。写入定义字段才能覆盖一切。写入位置遍历Registry.GetAllItems()。Registry是PersistentSingleton在Awake中被填充。需要后置PostfixRegistry.AddToRegistry(ItemDefinition item)以捕获运行时新增的定义。新产品混合product mixes通过Object.Instantiate(DefaultWeed)创建Meth、Cocaine、Shroom 同理它们会复制模板的当前StackLimit因此必须记住原始值否则倍数会复合叠加。0.4.7f9 的原版堆叠上限只有 1、10、20 三种取值共 227 个物品。物品分类使用定义类而非EItemCategory枚举桶后者太粗糙ProductDefinitionWeed、Meth、Cocaine、Shroom 子类继承自PropertyItemDefinition后者同时是混合器类因此先检查产品。其他类PackagingDefinition、QualityItemDefinition、SeedDefinition、SoilDefinition、AdditiveDefinition、SporeSyringeDefinition、ShroomSpawnDefinition、BuildableItemDefinition。弹药没有自己的类型通过ItemDefinition.Equippable上的Equippable_RangedWeapon.Magazine找到0.4.7f9 上对应的是shotgunshell。枪械与近战武器同理检查 equippable 上的Equippable_RangedWeapon/Equippable_MeleeWeapon。指南针CompassCompassManager单例为每个任务条目创建一个ElementQuestEntry.CreateCompassElement使用ParentQuest.IconPrefab。Element.DistanceLabel是一个 TMPText子对象。游戏只在50 米内填充距离文本使用UnitsUtility.FormatShortDistance其显示遵循公制/英制设置。交易是Contract任务Contract.Contracts。Contract.Customer是NetworkObject因此要对其调用GetComponentNPC()。联系人应用中显示的肖像来自NPC.MugshotSpriteRelationCircle.HeadshotImg。Element 子对象是QuestElement(Clone)包含Text与任务图标克隆。Element 容器Quests是800×30 且带RectMask2D任何绘制在标记下方的内容都会被裁剪除非设置RectMask2D.padding本案例使用(0,-60,0,0)。手机应用Phone apps原生应用是AppT : PlayerSingletonT。无法从 IL2CPP Mod 中继承该泛型注入泛型派生类型不现实因此像 S1API 的 PhoneApp 那样手工构建应用在HomeScreen.transform.parent/AppsCanvas下添加页面。克隆HomeScreen.appIconPrefab到appIconContainer再将其加入homeScreen.appIcons与homeScreen.uiPanel。通过重复调用App.SetOpen实现开关AppsCanvas.SetIsOpen、HomeScreen.SetIsOpen(!open)、Phone.ActiveApp page。挂接Phone.closeApps与GameInput.RegisterExitListener用DelegateSupport.ConvertDelegateGameInput.ExitDelegate转换委托。AppsCanvas 是1201×655横屏。竖屏应用是 655×1201 的页面旋转90°原生 Messages 应用同样如此。从代码打开手机先调用GameplayMenu.Open()再SetScreen(EGameplayScreen.Phone)单靠Phone.SetIsOpen只改变状态。NotificationsManager.SendNotification(title, subtitle, sprite, duration, sound)显示游戏自带的通知 toast。金钱系统MoneyMoneyManager.ChangeCashBalance(delta, visualize, sound)修改现金CreateOnlineTransaction(name, unit, qty, note)是带RunLocally的 ServerRpc会修改银行余额并追加到ledger。分类账ledger仅限当前会话不保存。每周存款上限是ATM.WeeklyDepositLimit 10000一个const仅在ATMInterface内部强制。ATM.WeeklyDepositSumstatic一旦达到 10000 还会触发Quest_CleanCash。在 IL2CPP 上在线余额的 SyncVar 属性无法从 C# 直接使用需调用sync___get_value_onlineBalance()。经销商DealersDealer.AllPlayerDealers、Dealer.Cash、Dealer.SetCash。NPC.MSGConversation.CreateSendableMessage(text)添加玩家消息选项带onSent一个Il2CppSystem.Action与ShouldShowCheck回调。构建步骤Build steps将 MelonLoader 0.7.3 安装进 IL2CPP 安装目录并启动一次以生成互操作程序集。克隆 Mod 仓库并运行dotnet build src -c Release -p:GameDirSchedule I -p:DeployToGametrue从 Steam 启动。设置存放于UserData/Mod.cfg。验证神谕与开发命令文件按 oracles-how-agents-know-a-mod-works.md 的方法论本案例的神谕是MelonLoader 日志MelonLoader/Latest.log加一个由 Mod 读取的开发命令文件。该命令文件仅在UserData/PocketPlug.dev存在时生效命令包括命令作用load调用LoadManager.StartGame(SaveGames[0])phone open、app id打开手机、打开指定应用click app path调用按钮的onClickshot调用ScreenCapture.CaptureScreenshot即使游戏窗口在另一个 Wayland 工作区也能工作dump name写出带矩形、旋转、颜色、字体的变换树fakedeal在真实 NPC 上添加指南针元素money记录现金与银行余额已在游戏中验证Verified in game堆叠上限227 个物品中 133 个被提高霰弹枪子弹是唯一保持不变的可堆叠物品开关与实时配置重载均正常。设置应用的开关与 /- 按钮会写入配置并立即重新应用。银行应用的存款与取款守恒现金 1542→1442→1542银行 0→100→0。通知能正常显示。指南针标记显示绿环肖像、名字与游戏单位距离如 45ft已通过截图确认。用户实测Play-tested by the user堆叠上限与银行应用期间发现并修复了一个重复 bug见陷阱 4。尚未在游戏中验证Not yet verified真实站点的就绪提醒、交易提醒、经销商转账存档中无已招募经销商或活跃交易、无尽滑板、以及实体 ATM 上的无上限补丁。12 条陷阱Gotchassymptom → cause → fixfroggy 原版 IncreasedStackLimit能用但送货忽略它。原因它只后置补丁了实例 getter而送货、购物车、死投递代码直接读取BaseItemDefinition.StackLimit。修复写入定义字段。提高某些堆叠上限会引发 bug。用户反馈而非本团队复现弹药超过原版上限后行为异常。修复绝不碰武器、弹药从RangedWeapon.Magazine检测或上限为 1 的物品且绝不降低任何上限。自定义手机应用打开后滚动列表为空。原因应用页面旋转 90°RectMask2D在画布空间裁剪把所有内容都裁掉了。修复改用模板Mask一个 alpha 为 1、showMaskGraphic false的Image。银行应用在真实点击时复制了金钱尽管onClick.Invoke()单独调用一次是对的。原因被点击的 uGUIButton会成为选中对象游戏的 Submit 输入随后再次触发onClick。修复在 Mod 的按钮上设置navigation.mode None并对金钱操作加 0.3 秒防护。NPC.MugshotSprite对无外观数据的 NPC 抛 NullReferenceException且异常发生在 IL2CPP getter 内部。修复用 try/catch 包裹。指南针标记下的名字与距离不可见。原因30 像素高的Quests容器带有RectMask2D。修复向下扩展padding。Harmony 堆叠的[HarmonyPatch]特性不会给出多个目标。修复用TargetMethods()围绕每个ATMInterface方法遮盖每周总和。由于上限是 const无法直接补丁Mod 临时将ATM.WeeklyDepositSum换成巨大的负数再恢复真实值并补上后续存款。MelonPreferences 在 Wine 下编辑后不重载MelonPreferences.cfg。修复category.SetFilePath(own file)约每秒轮询一次其 mtime并调用LoadFromFile。开发命令文件在 Wine 下执行了两次因为删除滞后于读取。修复记住文件的最后写入时间跳过已处理过的文件。Steam 启动的 Proton 前缀compatdata/3164500在首次 Steam 启动前不存在。全新的实验室前缀会在Saves/TempPlayer/下保存。在两者之间移动存档对主机无碍因为PlayerManager.TryGetPlayerData会先为主机加载Player_0。实验室是 Mono但其中的旧 Mod 是 IL2CPP 构建它引用了Il2CppInterop与Il2CppScheduleOne.*。后端不匹配的 Mod 会在加载时失败因此信任已安装 Mod 前务必检查其引用。每 2 秒出现约 200 ms 的规律性卡顿。原因对七种站点/花盆类型使用Object.FindObjectsOfTypeT()轮询在此 IL2CPP 场景中每次遍历约耗时 190 ms。修复改为遍历Property.OwnedProperties[i].BuildableItems缓存每项的TryCast类型一次每帧只检查 12 项。该问题由开发用性能探针发现记录超过 150 ms 的帧与超过 2 ms 的特性。修复后加载后零卡顿。资源与成本图标Lucide 的 SVGISC 协议用rsvg-convert渲染再用 Pillow 合成到渐变方块上PocketPlug 仓库的scripts/make-icons.py。指南针环与圆形遮罩程序化绘制无生成艺术。成本与时间约两个工作日会话含侦察、两个 Mod 与游戏内测试循环每次重启到进游戏的循环约 2 分钟。开放问题Open questionsVerification 一节中尚未验证的功能。联机行为两个 Mod 仅限单人。堆叠上限需要每个客户端配置一致。ATMInterface.ProcessTransaction是一个协程掩码就位后它是否还需要额外处理它读取的是真实值因此应当无需改动。延伸阅读本字段笔记在知识库索引中的位置INDEX.mdSchedule I条目engineunity-il2cpproutemanaged-patchstatusworking。Unity 引擎通用手册unity.md——含 IL2CPP 与 Mono 的识别、MelonLoader/BepInEx 安装与 Proton 启动选项、Harmony 补丁类型说明。逆向工具链reverse-engineering——IL2CPP 的 Cpp2IL/Il2CppDumper 流程。验证方法论oracles-how-agents-know-a-mod-works.md。仓库知识库规则knowledge/README.md 与 CONTRIBUTING.md。赞分享【免费下载链接】universal-modderPoint Claude at any game. Skills, tools and the fal MCP that let Claude Code mod almost any PC game you own: recon, reverse engineering, fal-generated art/3D/audio, in-game testing, showcase videos.项目地址https://gitcode.com/gh_mirrors/un/universal-modder点击查看免费下载相关推荐免费 PhotoGIMPGIMP 的 Photoshop 风格布局补丁安装指南免费 PhotoGIMPGIMP 的 Photoshop 风格布局补丁安装指南 PhotoGIMP 是一个由社区维护的免费 GIMP 界面补丁面向习惯 Adbbport 文件 Mod 与 XML 补丁CUSA03173 的 Mod 管理、补丁编译与 TAA 锐化实战bbport 文件 Mod 与 XML 补丁CUSA03173 的 Mod 管理、补丁编译与 TAA 锐化实战 bbport 项目为《血源诅咒》Bloodbuniversal-modder 的 Unity 游戏 Mod 实战指南从引擎识别、BepInEx/Harmony 到 IL2CPP 攻防universal modder 的 Unity 游戏 Mod 实战指南从引擎识别、BepInEx/Harmony 到 IL2CPP 攻防 本文以 Unity上一篇xai-grok-shell 0.2.67 版本解析JSON Schema 约束输出、空闲检测增强与搜索工具加速下一篇1 {section .foo .unnumbered keyval}创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进