sschoener / cities-skylines-detour

Proof of concept for a simple detour for functions in Cities: Skylines (or more generally: Unity 5 x64 on Windows.)
MIT License
85 stars 9 forks source link

Detour for virtual / override methods? #2

Closed boformer closed 9 years ago

boformer commented 9 years ago

Do you know a way to detour virtual methods? Maybe by modifying the virtual method table?

Your RedirectionHelper class is one of the most powerful tools for Skylines Modding. A virtual method detour would make it perfect.

sschoener commented 9 years ago

I have not looked into that and I am currently quite busy. I will happily take a look at that as soon as I have the time, but I doubt that there is a simple solution.

The first problem that comes to mind is this: If you patch the virtual function table, you have to do it for each class that overrides the method in question. That is problematic because in effect you would have to monitor all assemblies that are loaded at any time and go through all the classes and apply the redirection. (That's not hard, but it requires some more effort.) But if you are doing this, then you could just as well redirect the method itself in each of the child-classes. An alternative approach may be to patch all call-sites of the virtual function, but I'd argue that this is much harder.

boformer commented 9 years ago

But if you are doing this, then you could just as well redirect the method itself in each of the child-classes.

Yep, that would be just fine.

sschoener commented 9 years ago

But this is quite straight-forward and should be possible using just reflection. So may I consider this issue resolved?

boformer commented 9 years ago

Hmm, can you give an example?

How can I replace a virtual method of a class (extending is not an option)?

sschoener commented 9 years ago

My point was that if you know all classes that derive from your target class and override the virtual method in question, then you could simply patch the methods in the subclass (where the patching is done with the code from the repository). If you do not know the classes that override your target method, you can crawl through the assemblies currently loaded and check every type. (You should also check for assemblies that are (re)loaded during runtime.) Coming to think about it, you do not have much of a choice: Subclassing is inherently open, so going over all types in all assemblies seems like the only safe thing to do.

boformer commented 9 years ago

where the patching is done with the code from the repository

The problem is that it does not seem to work for methods tagged as virtual or override. The RedirectionHelper only works for sealed methods. That's why I was asking.

sschoener commented 9 years ago

Works fine for me.

Test setup:

public class TestClass
{
    public virtual void TestMethod()
    {
        DebugLog.Log("TestClass.TestMethod");
    }

    public void Detour()
    {
        DebugLog.Log("TestClass.Detour");
    }
}

public class TestSubClass : TestClass
{
    public override void TestMethod()
    {
        DebugLog.Log("TestSubClass.TestMethod");
    }

    public void DetourSub()
    {
        DebugLog.Log("TestSubClass.DetourSub");
    }
}

Test code:

TestClass obj = new TestClass();
TestSubClass subObj = new TestSubClass();
RedirectionHelper.RedirectCalls(typeof(TestClass).GetMethod("TestMethod"), typeof(TestClass).GetMethod("Detour"));
subObj.TestMethod();
RedirectionHelper.RedirectCalls(typeof(TestSubClass).GetMethod("TestMethod"), typeof(TestSubClass).GetMethod("DetourSub"));
obj.TestMethod();
subObj.TestMethod();

Prints out:

TestSubClass.TestMethod
TestClass.Detour
TestSubClass.DetourSub

As I said, you have to do the detour manually for each subclass. This seems inevitable because the superclass can hardly now all its subclasses -- inheritance is inherently open.

boformer commented 9 years ago

Thanks for the clarification. Your detour method is really helpful!