UnityMod开发教程 10 访问和Patch internal修饰的类

一. 访问

  虽然前面的教程已经覆盖了很多可能遇到的情况,但还是有一种对新手来说相对棘手的情况没有讲到,那就是被internal修饰的类,internal修饰符让类只能被自己程序集内的其他成员访问,我们的Mod显然不在此列,那怎么办呢?很显然,反射。
  我们使用Type t = Assembly.Load(程序集名称).GetType(类名);的方式即可获取这个类,然后使用Activator来创建实例。
假如现在在游戏的Assembly-CSharp.dll里有这样一个类

namespace Logic
{
    internal class Food
    {
        public int addHP; //HP回复量
        public int addEnergy; //能量回复量
        
        public Food(int hp, int energy)
        {
            addHP = hp;
            addEnergy = energy;
        }
        
        public void SomeMethod()
        {
            //...
        }
    }
}

  现在,我们来创建一个Food实例

Type t = Assembly.Load("Assembly-CSharp").GetType("Logic.Food");
object food = Activator.CreateInstance(t, 10, 20);

  这样,我们就获得了一个回复10点HP,20点能量的Food。
  另外,如果你需要多次访问这个类,应该先保存好类型再多次使用,而不是每次创建都加载一次程序集来获取,这样会影响性能。

二. Patch

  在上面我们通过程序集加载的方式获得了Food类型,那是不是可以直接使用这个类型来Patch呢?答案是不行的,因为我们获取到的是一个不确定的类型,但是Harmony进行Patch需要一个知道一个具体的类型,那怎么办呢?我们可以从和它相关的类入手。
  使用Dnspy分析这个类,查看都有谁使用了它,然后挑一个合适的类进行我们的工作。这个类需要不是intelnal的,我们要可以直接Patch到这个类。我们假设有一个People类,它有一个Cook方法可以得到一个Food。

namespace Logic
{
    public class People
    {
        public string Name;
        public int Age;
        //...
        
        public Food Cook()
        {
            Food food = new Food(this.Age, this.Age*2);
            return food;
        }
    }
}

  既然这个People的Cook方法可以得到Food类,那我们就可以从它下手,进行如下Patch。

public static UnityModManager.ModEntry mod;
public static HarmonyInstance harmony;
public static bool patchFinish = false;
public static bool Load(UnityModManager.ModEntry modEntry)
{
    mod = modEntry;
    harmony.PatchAll(Assembly.GetExecutingAssembly());
    return true;
}

public static void MyPostfix()
{
    mod.Logger.Log("间接补丁的Postfix方法被调用");
}

[HarmonyPatch(typeof(People), "Cook")]
class PeoplePatch
{
    public static void Postfix(object __result)
    {
        if (patchFinish) return;
        var original = __result.GetType().GetMethod("SomeMethod", AccessTools.all);
        var postfix = typeof(Main).GetMethod("MyPostfix");
        harmony.Patch(original, null, new HarmonyMethod(postfix));
        patchFinish = true;
    }
}

  这样,我们就通过手动补丁的方式对intelnal的类进行了修补,间接补丁的入手点很多,比如如果它有父类,可以从父类入手,如果是某些方法的返回值或者参数,也可以从这些方法入手。还要稍微注意一下我们GetMethod时候使用了AccessTools.all,这是我们之前讲过的工具,all代表了全类型标志,这样可以保证我们获取到非公开的方法。
  讲到这里,我们几乎可以解决能遇到的各种Patch了,剩下的基本就是要靠发挥想象力或者再进行一些其他技术的发挥。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。