using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using SleddingMouseFix;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Mod), "Sledding Mouse Stutter Fix", "1.0.0", "mitch", null)]
[assembly: MelonGame(null, null)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("SleddingMouseFix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+995f2301cfd0105f783a5991938cce51e25272ed")]
[assembly: AssemblyProduct("SleddingMouseFix")]
[assembly: AssemblyTitle("SleddingMouseFix")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace SleddingMouseFix
{
public class Mod : MelonMod
{
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
private delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr param);
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
private struct POINT
{
public int X;
public int Y;
}
private const int GWLP_WNDPROC = -4;
private const uint WM_NCHITTEST = 132u;
private const int HTCLIENT = 1;
private static IntPtr _hwnd;
private static IntPtr _prevProc;
private static IntPtr _myProcPtr;
private static WndProcDelegate _keepAlive;
private static bool _installed;
private static long _handled;
private static long _passedThrough;
private static MelonPreferences_Category _cat;
private static MelonPreferences_Entry<bool> _enabled;
private static MelonPreferences_Entry<bool> _logStats;
private double _nextCheckMs;
private double _nextStatsMs;
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWindowLongPtrW(IntPtr h, int i, IntPtr v);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetWindowLongPtrW(IntPtr h, int i);
[DllImport("user32.dll")]
private static extern IntPtr CallWindowProcW(IntPtr p, IntPtr h, uint m, IntPtr w, IntPtr l);
[DllImport("user32.dll")]
private static extern bool EnumThreadWindows(uint tid, EnumWindowsProc cb, IntPtr p);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int GetClassNameW(IntPtr h, StringBuilder b, int max);
[DllImport("user32.dll")]
private static extern bool IsWindowVisible(IntPtr h);
[DllImport("user32.dll")]
private static extern bool GetClientRect(IntPtr h, out RECT r);
[DllImport("user32.dll")]
private static extern bool ScreenToClient(IntPtr h, ref POINT p);
[DllImport("kernel32.dll")]
private static extern uint GetCurrentThreadId();
public override void OnInitializeMelon()
{
_cat = MelonPreferences.CreateCategory("SleddingMouseFix");
_enabled = _cat.CreateEntry<bool>("Enabled", true, (string)null, "Short-circuit WM_NCHITTEST inside the client area to stop the mouse-movement freeze.", false, false, (ValueValidator)null, (string)null);
_logStats = _cat.CreateEntry<bool>("LogStats", false, (string)null, "Log how many hit-tests were handled each 10s (diagnostic).", false, false, (ValueValidator)null, (string)null);
((MelonBase)this).LoggerInstance.Msg("Sledding Mouse Stutter Fix loaded. Enabled=" + _enabled.Value);
}
private static double NowMs()
{
return (double)Stopwatch.GetTimestamp() * 1000.0 / (double)Stopwatch.Frequency;
}
public override void OnUpdate()
{
double num = NowMs();
if (num >= _nextCheckMs)
{
_nextCheckMs = num + 1000.0;
EnsureHooked();
}
if (_logStats.Value && num >= _nextStatsMs)
{
_nextStatsMs = num + 10000.0;
if (_installed)
{
((MelonBase)this).LoggerInstance.Msg("hit-tests handled=" + _handled + " passedThrough=" + _passedThrough);
}
}
}
private void EnsureHooked()
{
try
{
if (!_enabled.Value)
{
if (_installed)
{
Unhook();
}
return;
}
IntPtr intPtr = FindGameWindow();
if (intPtr == IntPtr.Zero)
{
return;
}
if (_installed && intPtr == _hwnd)
{
if (GetWindowLongPtrW(_hwnd, -4) == _myProcPtr)
{
return;
}
_installed = false;
}
else if (_installed && intPtr != _hwnd)
{
Unhook();
}
_hwnd = intPtr;
if (_keepAlive == null)
{
_keepAlive = MyProc;
_myProcPtr = Marshal.GetFunctionPointerForDelegate(_keepAlive);
}
IntPtr intPtr2 = SetWindowLongPtrW(_hwnd, -4, _myProcPtr);
if (intPtr2 == IntPtr.Zero)
{
((MelonBase)this).LoggerInstance.Warning("could not subclass window (err " + Marshal.GetLastWin32Error() + ")");
return;
}
_prevProc = intPtr2;
_installed = true;
((MelonBase)this).LoggerInstance.Msg("attached to game window 0x" + _hwnd.ToInt64().ToString("X"));
}
catch (Exception ex)
{
((MelonBase)this).LoggerInstance.Warning("EnsureHooked: " + ex.Message);
}
}
private void Unhook()
{
try
{
if (_installed && _hwnd != IntPtr.Zero && _prevProc != IntPtr.Zero && GetWindowLongPtrW(_hwnd, -4) == _myProcPtr)
{
SetWindowLongPtrW(_hwnd, -4, _prevProc);
}
}
catch
{
}
_installed = false;
}
private static IntPtr FindGameWindow()
{
IntPtr found = IntPtr.Zero;
IntPtr firstVisible = IntPtr.Zero;
try
{
EnumThreadWindows(GetCurrentThreadId(), delegate(IntPtr h, IntPtr p)
{
if (!IsWindowVisible(h))
{
return true;
}
if (firstVisible == IntPtr.Zero)
{
firstVisible = h;
}
StringBuilder stringBuilder = new StringBuilder(256);
GetClassNameW(h, stringBuilder, stringBuilder.Capacity);
if (stringBuilder.ToString().IndexOf("Unity", StringComparison.OrdinalIgnoreCase) >= 0)
{
found = h;
}
return true;
}, IntPtr.Zero);
}
catch
{
}
if (!(found != IntPtr.Zero))
{
return firstVisible;
}
return found;
}
private static IntPtr MyProc(IntPtr h, uint msg, IntPtr w, IntPtr l)
{
if (msg == 132)
{
int x = (short)((long)l & 0xFFFF);
int y = (short)(((long)l >> 16) & 0xFFFF);
POINT p = new POINT
{
X = x,
Y = y
};
if (ScreenToClient(h, ref p) && GetClientRect(h, out var r) && p.X >= r.Left && p.X < r.Right && p.Y >= r.Top && p.Y < r.Bottom)
{
_handled++;
return (IntPtr)1;
}
_passedThrough++;
}
try
{
return CallWindowProcW(_prevProc, h, msg, w, l);
}
catch
{
return IntPtr.Zero;
}
}
public override void OnDeinitializeMelon()
{
Unhook();
}
}
}