Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - Micktu

#1
Mods / Re: New code injection method
August 11, 2016, 07:27:52 AM
So there have been dozens of new installs, and nobody complained yet, so I assume the method is working an stable. Feel free to use it people, it's the Injection folder in the repo.

I'll make it handle invokes properly later.
#2
Quote from: SynsPlayground on August 10, 2016, 08:53:17 PM
will definately be giving a good ole test run, seems very usefull.

Edit #1 peliminary testing seems to work perfectly as described so far, next will be testing with objects from other mods to see how well it works and if it can work with other non-vanila things  :)

Big thanks for deep testing! Theoretically it should work well with whatever other mods do.

Also, it's not in the description, but you can copy planned and unfinished buildings as well. And if these have settings to be applied, they also get copied. Copy of a copy of a copy...
#3
Quote from: CJoker3221 on August 10, 2016, 11:27:08 PM
Need to restart for this? Or can just play with regular save? :D
Edit: Got it! Also, looking forward to the replace material thing :D

No need to restart, it does not affect saves at the moment. It will, but still there will be no need to restart.
#4
Quote from: Lumaan on August 10, 2016, 05:02:49 PMhard to get use to *Habit of having to go into the menu

Ha,  I noticed this as well.  8)
#5
I've had this mod up on workshop for some time, but I think not a lot of people know about it.

The basic idea is that you don't have to dig through menus in order to build new stuff. It lets you order new constructions by copying selected buildings. It also retains temperature settings, etc. for buildings you copy. The full description can be found on the workshop page.

Some of the planned features are:

  • Replace material: rebuild something with material of your choice. Good for updating walls.
  • Target quality: rebuild until you manage to produce something of quality of your choice. Good for beds.
  • Walls into stone: order to mine out stone and replace it with a wall.
It's also pretty experimental as it makes use of some advanced dark magic instead of CCL to do its work. So if it happens to crash on you, please tell me when it did and what operating system you're on.

Links for version 0.30:
#6
Mods / Re: New code injection method
August 10, 2016, 09:09:11 AM
Anyway, the current working example is now up on workshop, imma cross my fingers and hope it works for everyone. The source is at https://github.com/micktu/RimWorld-BuildProductive if you want to take a look at usage.

Another problem I stumbled upon is that sometimes hooking into certain methods ends up in segfault on stack allocation for no apparent reason in random places within execution chain. E.g you can't hook to Designator.DesignateThing(), it's called from a Command's delegate, and I think it happens when the hooked method is called from within a delegate. Hope I arrive at a better explanation later. Ah, and it only happens with x64 compiler. I'll try some things like adding prolog to such functions, or maybe try to wrap them in a delegate of my own and see if it works.
#7
Mods / Re: New code injection method
August 10, 2016, 09:02:18 AM
>I never hit any issues with recursive calling, i need some kind of example to implement a fix.
It's obvious: I check for return address. If you Invoke, the return address is not the function you're checking against, but mono_runtime_invoke's address. Therefore your function gets called again and you crash with stack overflow.

>Also stack allocation for methods is not issue at all, just like methods below 5 bytes.
It's not, reliable detection of what can be copied and replaced is. It's not trivial unless you write your own disassembler thing to analyze the instructions. Which is a good solution, but it increases complexity manyfold.

>And yes, methods can be very large, especially methods with exception handling (also they have return opcode in unexpected locations)
What I meant is that I assumed that methods that do not make use of stack variables are usually small, and this turns out to not be true.



#8
Mods / Re: New code injection method
August 09, 2016, 09:05:08 AM
So when I found out that on x64 stack alloc sometimes is a single instruction and can't be reliably replaced with a jump, I weighted different solutions and tried copying the whole method as a rescue. I assumed it's rare, and it was only small methods that can be this way and that it will work in most cases.

Turned out I was wrong, it's very common:



And the methods can be huge:

But the good news is that it works perfectly well. It is even is able to resolve its trampolines successfully in its new location (thanks mono programmers).

Unfortunately this won't work on x86 because of relative addressing that has to be handled, and that increases complexity too much to be nice. But then again, it's not a problem on x86 because I'm yet to see a stack alloc pattern that's shorter than 5 bytes.

By the way I'm still patching with relative jumps in x64 because the chances that memory pages will end up 2Gb apart are maybe one in a thousand years.
#9
Mods / Re: New code injection method
August 09, 2016, 08:14:09 AM
After a little though I believe checking for Invoke wouldn't work, because there are other places that might want to Invoke your method if it's a delegate.

We need figure out a way to call private methods directly if possible, maybe through IL preprocessing. RawCode please try to think about it.

Or I'll make an alternative check that does not involve call stack. Someeting dumb like setting a flag when you enter your method and clearing it when you leave it. Not thread-safe of course.
#10
Mods / Re: New code injection method
August 08, 2016, 09:25:57 PM
Oh no no no, var is a production-quality blessing. I scold people for not using that, because BaseContainerInfoHandler baseContainerInfoHandler = new BaseContainerInfoHandler() is obviously fun and improves readability immensely. That's exactly why Java is unusable, as well as C++ before C++11.

I agree on everything else though, no magic number, no ambiguity. That thing was rewritten about 4 times, I'm sure every line is in its place besides a couple of line breaks and brackets. I went ways to actually make it clean and readable, and I'm satisfied with the result.

This is also the last time I argue about style.

Back on topic, what I overlooked is that you have to be able to Invoke() from inside of your stuff to call private base methods, but now you obviously end up in a stack overflow and crash. I'll make it do an extra check later.
#11
Mods / Re: New code injection method
August 08, 2016, 05:53:56 PM
>It could use some polish and needs some work to match the coding style and structure before being added to CCL though.
Nice joke, the polish and coding style of college students, right. Actually, you know what, don't touch the source code, I'll make it an assembly and you can use that if you wish.

>I'm eager to see a complete working example
So make it. Test it, mr. part of the community.
#12
Mods / Re: New code injection method
August 08, 2016, 05:00:36 PM
It's finished!

Tested on Windows, OSX, and Ubuntu x64 (and I assume x86 would work too). I also believe it's stable and reliable for most methods. Still can't hook into IEnumerable<> generators though.

It can also stack overrides on the same function multiple times (if multiple mods need to hook to the same thing), and correctly handles CCL-detoured methods, so you can hook to these too.

You need these files (these are direct links): HookInjector.cs Platform.cs AsmHelper.cs - they will be moved into master branch some time later.

Define your override:
internal static void PreLoadUtility_CheckVersionAndLoad(string path, ScribeMetaHeaderUtility.ScribeHeaderMode mode, Action loadAct)
{
    Log.Message("!!! CheckVersionAndLoad");
    PreLoadUtility.CheckVersionAndLoad(path, mode, loadAct);
}


And then hook it up:
HookPatcher = new HookInjector();
HookPatcher.Inject(typeof(PreLoadUtility), "CheckVersionAndLoad", typeof(VerseExtensions));

As you can see, if you don't specify the target method name, it defaults to YourClass.SourceClassName_SourceMethodName().

You don't need to create multiple injectors (although it won't break if you do), one is enough.
#13
Mods / Re: New code injection method
August 08, 2016, 09:46:01 AM
So, thanks again, RawCode-sensei! It does work as expected.

Implemented like this so it should work on x64 out of the box (maybe will have to go with libmono instead of __Internal on *nix though):


        [DllImport("__Internal")]
        public static extern IntPtr mono_jit_info_table_find(IntPtr domain, IntPtr addr);

        [DllImport("__Internal")]
        public static extern IntPtr mono_domain_get();

        [StructLayout(LayoutKind.Sequential)]
        public struct MonoJitInfo
        {
            public IntPtr d;
            public IntPtr n;
            public IntPtr code_start;
            public uint unwind_info;
            public int code_size;
            // The rest is omitted
        }



            var infoPtr = Platform.mono_jit_info_table_find(Platform.mono_domain_get(), targetPtr);
            var ji = (Platform.MonoJitInfo)Marshal.PtrToStructure(infoPtr, typeof(Platform.MonoJitInfo));


Now I can finally get rid of terrible, terrible size scan code.
#14
Mods / Re: New code injection method
August 08, 2016, 08:44:28 AM
Cecil cannot patch assemblies at runtime. You can only get away with Cecil (and it works perfectly well) if you patch the assembly before loading it.

Anyway, we're beyond patching assemblies, we're patching native code here.
#15
Mods / Re: New code injection method
August 08, 2016, 08:28:55 AM
Thanks, I'll verify one I'll get to my PC.

Edit: Yes, that seems just about right! Let's implement.

Actually it wasn't bloat, it was only the beginning of bloat, considering I can't get away with a static address on *nix and have to dlinfo/dlsym modules etc. So it sucked.

This is so much better.