rimworld吧 关注:230,838贴子:4,024,007
  • 17回复贴,共1

关于 Rimatomic 的 2 个小问题的解决办法

只看楼主收藏回复

总所周知,现在 1.5 玩 Rimatomic,即边缘核能,都会遇到 2 个很明显的问题:
1. 点击"边缘核能加工台"时引发的报错

2.建造存储池后,在任意工作台的清单的详细界面处点击【放到最合适的存储区】引发的报错

那么,遇到了问题,要怎么解决呢?且看下方分解。


IP属地:广西1楼2024-08-12 20:13回复

    问题 1 描述及复现过程:
    描述:点击边缘核能加工台时,左下角面板显示文本报错,如处于开发者模式,同时还会弹出 Debug log 窗口。
    复现过程:
    1.首先建造“边缘核能加工台”
    2.使用鼠标点击 "边缘核能加工台"
    3.此时加工台的左下角信息面板会显示报错信息,如果处于开发者模式,则会弹出 Debug Log 窗口
    错误信息,即红字,内容如下(只节选有用部分):
    GetInspectString exception on TableRimatomicsMachining6428:
    System.InvalidCastException: Specified cast is not valid.
    [Ref 6E829599]
    at RimWorld.CompReportWorkSpeed.get_Props () [0x00000] in <f0ac5eb9b52e4cc396c70fc9a4ee15e5>:0
    at RimWorld.CompReportWorkSpeed.CompInspectStringExtra () [0x000d4] in <f0ac5eb9b52e4cc396c70fc9a4ee15e5>:0
    at Verse.ThingWithComps.InspectStringPartsFromComps () [0x00020] in <f0ac5eb9b52e4cc396c70fc9a4ee15e5>:0
    at Verse.ThingWithComps.GetInspectString () [0x00013] in <f0ac5eb9b52e4cc396c70fc9a4ee15e5>:0
    at RimWorld.InspectPaneFiller.DrawInspectStringFor (Verse.ISelectable sel, UnityEngine.Rect rect) [0x00000] in <f0ac5eb9b52e4cc396c70fc9a4ee15e5>:0
    UnityEngine.StackTraceUtility:ExtractStackTrace ()


    IP属地:广西2楼2024-08-12 20:23
    回复
      从报错中,我们可以知道并掌握以下信息(不知道就学):
      1.抛出的GetInspectString异常,即GetInspectString exception 是由 TableRimatomicsMachining6428 抛出的
      2.抛出异常的调用链顺序为
      RimWorld.InspectPaneFiller.DrawInspectStringFor(...) ->
      Verse.ThingWithComps.GetInspectString () ->
      Verse.ThingWithComps.InspectStringPartsFromComps () ->
      RimWorld.CompReportWorkSpeed.CompInspectStringExtra () ->
      RimWorld.CompReportWorkSpeed.get_Props ()
      即始作俑者是 DrawInspectStringFor(...) 但是一直调用到了 RimWorld.CompReportWorkSpeed.get_Props() 时发生了错误。熟悉 C# 的朋友会很容易知道 get_Props() 这个其实是调用了 Props 属性的 get 方法。
      3. 抛出的异常类型为 System.InvalidCastException: Specified cast is not valid. 即类型转换错误。
      知道了这些信息之后,接下来我们祭出我们的大杀器——dnspy,目的只有一个,看看 CompReportWorkSpeed 这个类的逻辑是什么,以及分析为什么会抛出这个异常:

      很容易的就可以发现这里 return 的是 (CompProperties_ReportWorkSpeed)this.props 这个玩意儿。
      这里就进行了一个显式的类型转换,即将类型为 CompProperties 的 props 强制转换为 CompProperties_ReportWorkSpeed 类型。
      做过 Mod 的都知道 CompProperties 是什么玩意了,所以接下来去查看边缘加工台的 xml 文件,善用 Rimpy 或者rimsort 的打开 mod 文件夹功能,然后再善用 VS Code 的打开文件夹功能+全局搜索功能,很容易知道“边缘加工台”的xml 定义位置:


      IP属地:广西3楼2024-08-12 20:45
      回复
        技术贴


        IP属地:上海来自Android客户端4楼2024-08-12 20:48
        回复
          往下拉找到<comps>标签块:

          可以很清晰的看到,这里比较熟悉的玩意儿:
          <li>
          <compClass>CompReportWorkSpeed</compClass>
          </li>
          好了,问题就是这个了,至于为什么,我其实还没想好通俗易懂的解释,专业术语说太多有卖弄学识的嫌疑,
          所以现在直接贴出 2 种解决方案:
          第一种,直接对mod文件进行修改,将其改为:
          <li Class="CompProperties_ReportWorkSpeed">
          <workSpeedStat>WorkTableWorkSpeedFactor</workSpeedStat>
          </li>
          当然你也可以直接删掉,但是那样加工台的工作速度就不会受到影响,比如温度,室外等,但不符合mod的原期望。
          第二种,新建一个Mod,然后根目录新建一个 Patches 文件夹,再新建一个 xml 文件,写入内容:
          <?xml version="1.0" encoding="UTF-8"?>
          <Patch>
          <Operation Class="PatchOperationFindMod">
          <success>Always</success>
          <mods>
          <li>Dubs Rimatomics</li>
          </mods>
          <match Class="PatchOperationSequence">
          <success>Always</success>
          <operations>
          <li Class="PatchOperationReplace">
          <xpath>Defs/ThingDef[defName="TableRimatomicsMachining"]/comps/li[compClass="CompReportWorkSpeed"]</xpath>
          <value>
          <li Class="CompProperties_ReportWorkSpeed">
          <workSpeedStat>WorkTableWorkSpeedFactor</workSpeedStat>
          </li>
          </value>
          </li>
          </operations>
          </match>
          </Operation>
          </Patch>
          然后重启游戏,即可发现:


          IP属地:广西5楼2024-08-12 21:06
          回复
            附上完整的 Patch 截图:

            以及如果采用直接修改的效果图:

            最后注意,上述的两个方法不兼容,你只能选一个用,要么直接修改原文件,要么自己写一个Patch


            IP属地:广西6楼2024-08-12 21:11
            回复
              问题 1 轻松结束,现在进行问题 2,须知,本文提供的问题 2 解决方法需要用到 C# 编程知识。

              问题 2 描述及复现过程:
              描述:建造“存储池”后,在任意工作台的任意清单的详细界面处点击【放到最合适的存储区】抛出报错异常。
              复现过程:
              1.在地图上建造“存储池”
              2.在任意工作台添加任意清单
              3.点击清单的【详细...】打开窗口,点击【放到最合适的存储区】,处于开发者模式时,此时弹出报错窗口。
              4.在拆除掉“存储池”后,重复 2-3 步骤可正常弹出右键菜单,如下图所示。

              报错信息内容如下:
              Exception filling window for RimWorld.Dialog_BillConfig: System.ArgumentNullException: Value cannot be null.
              Parameter name: key
              [Ref 7EA47B8F]
              at System.Collections.Generic.Dictionary`2[TKey,TValue].FindEntry (TKey key) [0x00008] in <eae584ce26bc40229c1b1aa476bfa589>:0
              at System.Collections.Generic.Dictionary`2[TKey,TValue].ContainsKey (TKey key) [0x00000] in <eae584ce26bc40229c1b1aa476bfa589>:0
              at RimWorld.Dialog_BillConfig.FillOutputDropdownOptions (System.Collections.Generic.List`1[Verse.FloatMenuOption]& opts, System.String prefix, System.Action`1[T] selected) [0x000ed] in <f0ac5eb9b52e4cc396c70fc9a4ee15e5>:0
              at RimWorld.Dialog_BillConfig.DoWindowContents (UnityEngine.Rect inRect) [0x00875] in <f0ac5eb9b52e4cc396c70fc9a4ee15e5>:0
              at Verse.Window.InnerWindowOnGUI (System.Int32 x) [0x001a6] in <f0ac5eb9b52e4cc396c70fc9a4ee15e5>:0


              IP属地:广西7楼2024-08-12 21:20
              回复
                求大佬把写的补丁发一下


                IP属地:广东来自Android客户端8楼2024-08-12 21:35
                回复
                  同样的对报错信息进行分析,这次可以知道:
                  1.抛出的异常是因为 RimWorld.Dialog_BillConfig 这个玩意儿。异常类型为 Value cannot be null. 且 Parameter 的 name 是 key。
                  2.调用链还是跟问题 1 一样的分析,但是最终的 Dictionary`2[TKey,TValue].FindEntry() 是查询字典的方法,直接看最终调用是死路。所以退回到 RimWorld.Dialog_BillConfig.FillOutputDropdownOptions 的调用上,依旧是Dnspy:

                  可以发现有这里有 2 处地方调用了 System.Collections.Generic.Dictionary`2[TKey,TValue].ContainsKey,结合之前的报错信息可知是因为 key 的值为 null 而引发的报错。这里 key 的值分别是 storageGroup.GroupingLabel 和 slotGroup.GroupingLabel,那么问题就因为是这两个玩意儿为空,但是现在还不知道会进入哪个分支,也就是我们不知道是因为 storageGroup.GroupingLabel 为空时报错还是因为 slotGroup.GroupingLabel 为空时报错。
                  现在进入打信息(收集信息)时间,首先从存储池的 xml 入手,找到其 thingclass:

                  然后继续 dnspy:

                  分析过程省略,可以发现该类存在属性 GroupingLabel,在未开启dnspy的显示编译器生成的隐藏类型和方法时可以看到这个属性的get方法是:

                  很明显是返回null。


                  IP属地:广西9楼2024-08-12 21:40
                  回复
                    因为省略了分析过程的缘故,这里不好展开说明解决办法的推导过程,所以仍然,直接贴出解决办法:
                    边缘核能系列作者没有提供源代码,所以自己修改代码再编译一遍显然不可能,那么只有一条路了,祭出第二个大杀器——Harmony,不懂 C# 的吧友可以不用看了,等好心人自制mod发布到工坊给你们订阅就好了。
                    首先问题的根本是 .GroupingLabel 的值为 null,而该类作为存储的一个建筑没有考虑到这一点(毕竟是1.5更新的),所以我们只需要利用 Harmony 写一个 Postfix 的 patch 修改这个属性的返回结果即可,即,让其不为空。
                    下面是参考代码,非常简单(只节选关键部分):

                    Harmony harmonyInstance = new Harmony("xxx.x.x.x.xxx");
                    harmonyInstance.Patch( original:AccessTools.TypeByName("Rimatomics.Building_storagePool").PropertyGetter("GroupingLabel"),
                    postfix: new HarmonyMethod(typeof(RimatomicsPatches), nameof(RimatomicsPatches.GroupingLabelPatch)));

                    public static void GroupingLabelPatch(ref string __result)
                    {
                    __result = "GroupPlural".Translate();
                    }
                    代码仅供参考,写成这样的形式也是可以的,但是这样你的工程文件就要添加边缘核能DLL的引用:
                    [HarmonyPatch(typeof(Rimatomics.Building_storagePool))]
                    public class XXXCLASSPATCH
                    {
                    [HarmonyPostfix]
                    [HarmonyPatch(nameof(Building_storagePool.GroupingLabel), MethodType.Getter)]
                    public static void xxxxxxPatchName(ref string __result)
                    {
                    __result = "GroupPlural".Translate();
                    }
                    }
                    patch 怎么写,见仁见智,效果达成就好,最终实现不报错:


                    IP属地:广西10楼2024-08-12 22:02
                    收起回复
                      至此,Rimatomic 的两个问题就完成解决,相信除了问题 2 大多数吧友不能解决外,问题 1 还是很容易就能轻松解决的。


                      IP属地:广西11楼2024-08-12 22:07
                      回复
                        技术帖子值得顶一波


                        IP属地:河南来自Android客户端12楼2024-08-12 22:34
                        回复
                          话说有没有考虑发到github上呢


                          IP属地:辽宁来自iPhone客户端13楼2024-08-13 08:58
                          回复
                            低技术力缘友帮顶,最希望能在创意工坊看到的一集


                            IP属地:内蒙古来自Android客户端14楼2024-08-13 10:17
                            收起回复
                              Mark


                              IP属地:江西来自Android客户端15楼2024-08-13 17:15
                              回复