Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Assets/Common/AddressRecords.json
Original file line number Diff line number Diff line change
Expand Up @@ -463,5 +463,10 @@
"Name": "EmAction:VTableAssignment",
"Pattern": "C7 41 0C 89 88 88 3C 89 71 08 48 8D 05 ?? ?? ?? ?? 48 89 01",
"Offset": 13
},
{
"Name": "Keyboard:WriteInput",
"Pattern": "41 57 48 81 EC 20 01 00 00 48 8B E9 48 8B FA 48 8B 89 10 0A 00 00",
"Offset": -10
}
]
152 changes: 137 additions & 15 deletions SharpPluginLoader.Core/Rendering/Renderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using ImGuiNET;
using SharpPluginLoader.Core.IO;
using SharpPluginLoader.Core.Memory;
using SharpPluginLoader.Core.MtTypes;

namespace SharpPluginLoader.Core.Rendering
{
Expand Down Expand Up @@ -68,7 +67,7 @@ public static TextureHandle LoadTexture(string path, out uint width, out uint he
/// Fonts must be registered before the first call to <see cref="IPlugin.OnImGuiRender"/>.
/// Ideally, fonts should be registered in the <see cref="IPlugin.OnLoad"/> method.
/// </remarks>
public static unsafe void RegisterFont(string name, string path, float size, nint glyphRanges = 0,
public static unsafe void RegisterFont(string name, string path, float size, nint glyphRanges = 0,
bool merge = false, int oversampleV = 0, int oversampleH = 0)
{
if (_fontsSubmitted)
Expand Down Expand Up @@ -232,10 +231,13 @@ Initializing Renderer with

if (ImGui.GetCurrentContext() != 0)
return ImGui.GetCurrentContext();

ImGui.CreateContext();
var io = ImGui.GetIO();
io.ConfigFlags |= ImGuiConfigFlags.DockingEnable;
// Set NavNoCaptureKeyboard because it doesn't include ctrl+tab and handle it by
// checking io.NavActive when deciding to block keyboard or not.
io.ConfigFlags |= ImGuiConfigFlags.NavNoCaptureKeyboard;
// Currently causes a freeze when dragging a window outside of the main window.
// Most likely the WndProc doesn't process events anymore which causes windows to think it's frozen.
// io.ConfigFlags |= ImGuiConfigFlags.ViewportsEnable;
Expand All @@ -247,14 +249,119 @@ Initializing Renderer with
{
_mouseUpdateHook = Hook.Create<MouseUpdateDelegate>(sMhMouse.GetVirtualFunction(6), m =>
{
// Prevent the game from doing any mouse updates if an ImGui window is focused.
if (ImGui.GetIO().MouseDrawCursor)
return;
var anyFocused = ImGui.IsWindowFocused(ImGuiFocusedFlags.AnyWindow);
var anyHovered = ImGui.IsWindowHovered(ImGuiHoveredFlags.AnyWindow);

if (anyFocused || anyHovered)
{
// Tell the game to not reset the cursor to the middle of the screen
// when Camera Mouse Controls are on.
// MonsterHunterWorld.exe+4898F0 - cmp [rax+000147A8],r15b(0)
MemoryUtil.GetRef<byte>(Gui.SingletonInstance.Instance + 0x147A8) = 0x1;
}

_mouseUpdateHook.Original(m);

if (anyFocused || anyHovered)
{
MemoryUtil.GetRef<int>(m + 0x17C) = 0; // Scroll wheel.
}

if (anyFocused)
{
// Zero mouse delta used for camera movement.
MemoryUtil.GetRef<ulong>(m + 0xFC) = 0L; // dX(int), dY(int).
MemoryUtil.GetRef<byte>(m + 0x188) = 0x0; // Mouse down menu.
MemoryUtil.GetRef<byte>(m + 0x108) = 0x0; // Mouse down combat.
_lastUpdateHadFocus = true;
}
else if (anyHovered || _lastUpdateHadFocus)
{
// Block mouse1 clicks. Use _lastUpdateHadFocus to more consistently block
// a click used to unfocus the ImGui window.
ref byte downMenu = ref MemoryUtil.GetRef<byte>(m + 0x188);
ref byte downCombat = ref MemoryUtil.GetRef<byte>(m + 0x108);
if ((downMenu & 0x1) == 0 && (downCombat & 0x1) == 0) // Wait for release.
{
_lastUpdateHadFocus = false;
}
downMenu &= 0xFE;
downCombat &= 0xFE;
}
});
}

var sMhKeyboard = SingletonManager.GetSingleton("sMhKeyboard");
if (sMhKeyboard is not null)
{
_keyboardUpdateHook = Hook.Create<KeyboardUpdateDelegate>(AddressRepository.Get("Keyboard:WriteInput"), (kb, kbState) =>
{
_keyboardUpdateHook.Original(kb, kbState);

if (_lastUpdateHadKeyboard)
{
// Block Escape/Enter if they were presumably used to exit an input field.
// If for some reason the menu key is being held here, this will supersede it
// and cause the menu to be re-toggled on the release of Escape or Enter.
if (Input.IsDown(Key.Escape))
{
_waitForRelease = Key.Escape;
}
else if (Input.IsDown(Key.Enter))
{
_waitForRelease = Key.Enter;
}
}

if (_waitForRelease == null)
{
// Block the key used to bring up the menu.
if (Input.IsDown(_menuKey))
{
_showMenu = !_showMenu;
_waitForRelease = _menuKey;
}
#if DEBUG
else if (Input.IsDown(_demoKey))
{
_showDemo = !_showDemo;
_waitForRelease = _demoKey;
}
#endif
}

unsafe
{
KeyboardState* state = (KeyboardState*)kbState;

if (_waitForRelease != null)
{
byte* vkTable = (byte*)(kb + 0x38);
byte vk = vkTable[(int)_waitForRelease];
uint vkMask = 1u << (vk & 0x1F);
// No longer down = Released.
if ((state->On[vk >> 5] & vkMask) == 0)
{
_waitForRelease = null;
}
else
{
// On the update a key is first "Down", it seems to only be set in
// KeyboardState::On. So negating that should effectively block it.
state->On[vk >> 5] &= ~vkMask;
}
}

var io = ImGui.GetIO();
_lastUpdateHadKeyboard = io.WantCaptureKeyboard || io.NavActive;
if (_lastUpdateHadKeyboard)
{
Unsafe.InitBlockUnaligned((byte*)state, 0, (uint)Marshal.SizeOf<KeyboardState>());
}
}
});
}

Log.Debug("Renderer.Initialize");

return ImGui.GetCurrentContext();
Expand All @@ -270,11 +377,6 @@ internal static void Render()
[UnmanagedCallersOnly]
internal static unsafe nint ImGuiRender()
{
if (Input.IsPressed(_menuKey))
_showMenu = !_showMenu;
if (Input.IsPressed(_demoKey))
_showDemo = !_showDemo;

var io = ImGui.GetIO();
var anyFocused = ImGui.IsWindowFocused(ImGuiFocusedFlags.AnyWindow);
var anyHovered = ImGui.IsWindowHovered(ImGuiHoveredFlags.AnyWindow);
Expand All @@ -293,6 +395,19 @@ internal static unsafe nint ImGuiRender()
{
if (ImGui.BeginMenu("Options"))
{
bool keyboardNav = (io.ConfigFlags & ImGuiConfigFlags.NavEnableKeyboard) != 0;
if (ImGui.Checkbox("Keyboard Navigation", ref keyboardNav))
{
if (keyboardNav)
{
io.ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard;
}
else
{
io.ConfigFlags &= ~ImGuiConfigFlags.NavEnableKeyboard;
}
}

ImGui.Checkbox("Draw Primitives as Wireframe",
ref MemoryUtil.AsRef(_renderingOptionPointers.DrawPrimitivesAsWireframe));

Expand Down Expand Up @@ -341,7 +456,7 @@ ref MemoryUtil.AsRef(_renderingOptionPointers.LineThickness),
ImGui.ShowDemoWindow(ref _showDemo);
#endif

ImGui.PushStyleVar(ImGuiStyleVar.WindowRounding, 5f);
ImGui.PushStyleVar(ImGuiStyleVar.WindowRounding, 5.0f);
InternalCalls.RenderNotifications();
ImGui.PopStyleVar();

Expand Down Expand Up @@ -473,16 +588,23 @@ private static nint GetCursorPositionHook(nint app, out Point pos)
private delegate void MouseUpdateDelegate(nint sMhMouse);
private static Hook<GetCursorPositionDelegate> _getCursorPositionHook = null!;
private static Hook<MouseUpdateDelegate> _mouseUpdateHook = null!;
private static bool _lastUpdateHadFocus = false;
private delegate void KeyboardUpdateDelegate(nint sMhKeyboard, nint kbState);
private static Hook<KeyboardUpdateDelegate> _keyboardUpdateHook = null!;
private static bool _lastUpdateHadKeyboard = false;
private static Key? _waitForRelease = null;
private static bool _showMenu = false;
private static Key _menuKey = DefaultMenuKey;
#if DEBUG
private static bool _showDemo = false;
private static Key _demoKey = DefaultDemoKey;
#endif
private static RenderingOptionPointers _renderingOptionPointers;
private static Vector2 _viewportSize;
private static Vector2 _windowSize;
private static Vector2 _mousePos;
private static Vector2 _mousePosScalingFactor;
private static float _fontScale = 1.0f;
private static Key _menuKey = DefaultMenuKey;
private static Key _demoKey = DefaultDemoKey;
private static bool _fontsSubmitted = false;

private static NativeArray<CustomFontNative> CustomFonts;
Expand Down