-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Revamp input handling #94
Conversation
Game input block now fully works in games that use Windows messages to handle mouse movement, clicks and keyboard input. The reason that this wasn't working before is that the WndProc hook failed to hook onto the specific thread that handles all/some of a game's input. With SetWindowsHookEx we can target specific threads to inject into and make sure we read messages from all of the game's threads. However, Reshade somehow manages to do this without SetWindowsHookEx and I presume that it either latches on to the "correct" (main?) thread or gets hooked into all threads in some way I don't understand. Reshade uses proxy dll injection. Games that utilize their own input system or DirectInput or others will still not be able to block input. It is possible to hook into DirectInput , Xinput etc to block, but if a game has its own input system then it is very likely that there will be no general solution to block its input.
I have with great confidence tracked this down to being relevant only to games that use Raw input now. Should be able to fix this soon. Meanwhile you can just comment out WM_INPUT. |
Thank you for doing this work! I feel like this is going in the right direction, though I would address a few architectural shortcomings:
Overall, the input handling code looks good and way more nuanced than what we have currently, so I'd gladly integrate this once it gets in a good state. Thank you again! |
So. The input freeze seems to be a fairly documented issue, granted that it is the same one that we are suffering currently. Some threads about the problem(s); The ones with _LL at the end are desktop global hooks, however, they seem to be suffering from the same issues nonetheless.
More on that from MSDN (only specifies _LL here) and StackOverflow
I tried all solutions above and there's likely more that I have forgotten, but it seems that using I also tried;
The issue has so far only popped up for me if we modify raw mouse movement input to I'm getting really tired of troubleshooting this. If no one knows something that could help, I suggest that we drop raw mouse input blocking until a solution is found - OR I delve into hooking the functions specifically and hope that the problem lies with the |
Fixed this to the best of my ability. I assume Atomics inside of the Input struct isn't necessary at all, but I just stuck with them because the syntax is so nice. Not sure if it has any performance impact? |
I tried hooking into
|
Man, that's a treasure trove of information 😄 Okay so I read a bit more about Btw, I've gone back and checked the code a bit. I'm still very worried about the hooks -- mostly because they run on very fundamental bits of the windows event loop which are going to run a bazillion times per second... Ngl, the thought of adding overhead to every single one of those calls (even if it's just a Due to its usage in the practice tools, one of the core needs that I had while designing hudhook was to mess with the game as little as possible in order to not influence the underlying execution in any manner that could be unpredictable. This is already super hard to juggle as is. Integrating this PR in its current state would mean forgoing that design principle altogether. Don't get me wrong, I think this is super useful and I'd be glad to integrate it in some capacity, but I think it would be worth it to change its design so that it is opt-in -- i.e. an extra set of hooks that you can add at the beginning if you so wish, and that leaves everything else the same. It would be even better if we found a totally different way that didn't require hooking, or that drastically limited hooking; but I can work with the opt-in thing until we understand the problem space better and find something better. This might tie in with the idea to expose some internals of the library in order to build external plugins. I want to allocate some cycles to designing that, but lately it's been quite hard for me to do any progress at all due to real life commitments (and the need to unwind a bit). (as always, thank you so much for all this work!)
IIRC loads and stores should compile down to a single operation anyway on x86/x64, and their only usage is instruction reordering. Should be completely fine 😅 |
Is there a chance that hooking only those could be enough to block the input? |
Went and checked Reshade out of curiosity... Found this: https://github.com/crosire/reshade/blob/5ad47a818e919e25db08af94f31abe3744f0bbcc/res/exports.def#L452 So what I did overlook about Reshade is that it's actually a proxy DLL: hooking in that case might be a bit different because it happens at startup time. The address that the host application sees in the first place is the hooked function, so there's no altering of the control flow instructions iiuc. Just find the original function and call it. That's a bit different than what we (are forced to) have. I would still be a bit scared of going that way, honestly, but I can see how that might do the trick (then again maybe the design goals of Reshade are different). |
It only does global hooking if it is specified to do so. The current implementation only hooks into the specified threads of the process. If we'd use WH_MOUSE_LL it would indeed be global.
Yeah, I thought about that too. Not sure if it would be a problem though, granted that nothing gets stuck on the message handler. Although - I don't know if there's even much overhead added? Would this implementation really be that different from hooking the WNDPROC? Besides, Reshade is doing this and a lot more and it seems stable enough.
Completely understandable. I do not know how important blocking game input is for those use cases and I have little understanding of how much these hooks can affect a game. Definitely needs a lot of testing.
Absolutely, I agree - maybe we could even make it an opt-in build command? Not sure what's preferable.
Unfortunately, if I understand you correctly, I'm pretty sure this is impossible. I'm of course not all-knowing in the matter but nothing so far has told me that there is any other way other than hooking in some shape or form.
I like the sound of that. Reshade has been a huge source of inspiration for me so far in many different cases and the way he structured it for plugins is quite nice.
Don't worry about it mate, I'm super content that you're still around to give feedback.
That sounds alien to me, but I get the jist. Awesome.
I'm just glad I can give back! Cheers!
Absolutely - for most games. If it uses DirectInput then we're all outta gum. Hooking
Yep, perhaps I never said so. I don't know enough about what differences it makes, but I have noticed that some things get easier when you use a proxy DLL. Quite hard to hook into something like There's also an implementation for Reshade to be used for injection according to dll_main.cpp And as you can see in the hook_manager.cpp there's export hooks, function hooks and vtable hooks implemented. |
Hmmm. I was thinking about doing something like this for an opt-in. On ImguiRenderer init;
and then they would need their own msg_procs;
I'm not sure if there's a better way to do it, but this seems ok to me. It'd be a nice way to opt-in for other hooks as well, such as SetCursorPos or DirectInput. |
I tried alleviating this in the latest commit. However, I don't know if there is any safe way to do the same as transmute for function pointers at the moment? |
This looks already significantly better, thank you! There are things from this PR that I'd like to integrate in isolation, because I think they can stand on their own. In particular, the What I'd like to do is start a fresh branch with only that, make a few changes to it (e.g. pass a Then, we can rebase this PR on main, which will reduce the size of the diff significantly and could pave the way to new ideas and approaches. Let me know what you think! Would you like me to help with that, or would you prefer taking a crack at it yourself? |
Hmm, but wouldn't this approach make it really clunky to integrate the other hooks? Or do you mean that we would make two different functions like I suggested above, but only integrate the wnd_proc one to begin with?
I can do it when I understand 😆 |
I mean to integrate things gradually and have as much decoupling as possible so that both approaches can eventually coexist. I wouldn't want to use the multiple hooks approach myself, but I also think the work you have done on them is really valuable; figuring out a way of having them both would furthermore have the advantage of forcing us to rethink a cleaner and more modular API that plays well into the long term plan of extensibility. This is not trivial, but I think it's the best course of action. I'll get to that myself as soon as I can and send over a review request! |
Understandable, however, I do not see the need to couple the current
Cool! |
Oh, I didn't mean to do that. It's a good idea that the function stands on its own, I'm just going to keep using the current window proc approach (subclassing via |
Yeah I definitely understand the caution, just not sure I understood how you wanted to implement it fully. I will just have to wait and see 😄 |
I finally had the time to test this branch against Dark Souls III and Elden Ring. I used the branch as-is. Unfortunately, the inputs that I receive are duplicated in both games for some reason, and two cursor are actually shown. The underlying input is indeed blocked, though, which is a great result. I wish there was a way of doing this that didn't involve all these hooks -- I have a poor understanding of them and I'm afraid rerouting core Windows events functionality might result in unintended side effects like these. I just want to thank you again and reiterate that the research and effort you put into this is amazing. Unfortunately we can't merge it currently, but I feel like if we push more and seek to understand the situation better, the day when we will crack will come sooner rather than later! out.webm |
Interesting. The duplicated input is something I never experienced myself, but neither would I probably notice because it wouldn't make any difference on my app.. 😄 As for the cursor, you have to put If any game you try likes to lock the cursor somewhere you can also put
Thank you very much! Don't worry about it, all the work I put in is also massively benefitting me in many ways. No shame in saying no, quite the opposite - I'm glad to be able to discuss this and get viewpoints from someone with far more experience. You've been very patient with me so far and I appreciate that tremendously. |
Oh, and by the way - I found a way to fix this. It works in all games but one that I have tried so far. It is Star Wars: Empire at War and I currently don't have the faintest idea how the game is registering where the mouse is hovering. It could be DirectInput, an internal input manager or something else. Don't know. |
Closing this PR as the viable parts of it were gradually upstreamed over time and the underlying code changed so much that the conflicts are probably unsolvable. Hoping to work around this via a transparent overlay |
As shown in #36 (comment), I have found a way to block all input in most games by studying OSS Reshade and a bunch of forums.
Current issues;
should_block_messages
is set to true for longer periods of time (?), game input stops being received altogether in some games. Seems to be of a greater issue in games that use Raw input. ALT + TAB may fix it.SetWindowsHookEx
need to be heavily tested & some may be completely unnecessary (most likely Post/Get/Peek-Message).Any questions, help or thoughts on this is very much appreciated. I'm just a noob brute-forcing my way to success.