Repository navigation
Releases
Releases · zanewebb/SpaceMouseRepo
v0.3.13
v0.3.12
v0.3.12: tag float configs with AcceptableValueRange so r2modman rend…
v0.3.11
v0.3.11: bump deadzone defaults from 5% to 20% to suppress puck drift v0.3.10 SHIPPED A WORKING MOD — siappdll motion events flow into HeldObjectController and physRotation writes apply visibly to the held object. The remaining issue is jitter from the puck's idle drift (every SpaceMouse has ±2-15% sensor noise at rest depending on age), which our 5% default deadzone wasn't catching, so accumulated micro-rotations made the held object oscillate. Bumps both Translation and Rotation deadzone defaults to 20% (0.20) which reliably suppresses idle drift on consumer SpaceMouse Wireless units. Users with a fresher unit can dial it back to 0.10 or even 0.05 in BepInEx/config/com.zanewebb.spacemouse_repo.cfg without restarting the game (config changes apply live). Comments updated to point users at this knob if they still see jitter. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v0.3.10
v0.3.10: call SiGetEventWinInit before SiGetEvent (canonical pattern) v0.3.9 confirmed siappdll IS dispatching messages: all 20 logged were msg=0xC20B (a registered Si message) — but SiGetEvent returned SI_SKIP_EVENT for every one. SI_SKIP_EVENT means 'siappdll handled it internally, you should ignore.' We never got SI_IS_EVENT for motion. The SDK's canonical call pattern is: SiGetEventData ed; SiGetEventWinInit(&ed, msg, wParam, lParam); // initializes hidden state SiGetEvent(handle, 0, &ed, &spwEvent); We were skipping SiGetEventWinInit and constructing SiGetEventData via C# field-initializer. That fills the public msg/wParam/lParam fields but may leave native-only hidden state uninitialized that SiGetEvent checks before classifying the message as a real Si event. Adds the SiGetEventWinInit P/Invoke and calls it before SiGetEvent. If that still gives SI_SKIP_EVENT for everything, the next move is abandoning siappdll for 3DxNavLib (the modern API that doesn't go through window messages at all). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v0.3.9
v0.3.9: log first 20 raw Windows messages with SiGetEvent return code v0.3.8's heartbeat showed: - wndProcCalls jumped 16 → 184 → 322 (~30/sec) during puck wiggle - siEvents/siMotion stayed 0 the whole time - axes always (0,0,0) So *something* is being dispatched to our hidden window during input, but SiGetEvent doesn't recognize the messages as Si events. Either they're regular Windows messages firing on input (raw input, focus, etc.) or siappdll is sending them with a format that doesn't match SiGetEvent's expectations. Logs the raw msg/wParam/lParam plus SiGetEvent return for the first 20 calls, in InfoLog so it ends up in BepInEx's LogOutput.log. From that we'll know: - if msg values are in WM_USER (0x400) + 0x6000-ish range → these ARE 3DxWare events; the issue is in our SiGetEvent call/struct - if they're ordinary Windows messages (WM_INPUT 0xFF, WM_TIMER 0x113, WM_KEYDOWN, etc.) → siappdll isn't dispatching events; the driver is using the legacy mouse-emulation path for R.E.P.O. and we'd need to switch to 3DxNavLib (option C) instead No behavior change, pure diagnostic. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v0.3.8
v0.3.8: heartbeat reports SDK telemetry to diagnose silent SpaceMouse v0.3.7's side channel proved patches fire and grab is detected, but 'First rotation write' never logged — meaning the SpaceMouse delta is identity every frame. Either siappdll isn't dispatching motion events to our hidden window, or events arrive but parsing yields zero. Adds counters/values to SpaceMouseSdk that the per-5s heartbeat reads and logs: - WindowProcCalls — total times our background-thread WndProc was hit. Stays 0 if message routing is broken (driver sends events to a different window, or our hidden window isn't picking up the queue). - SiAnyEvents / SiMotionEvents — out of those WndProc calls, how many were Si events (vs other Windows messages) and how many were specifically motion events. - RawAxes — current normalized axis values [-1..1]. If non-zero while the user wiggles the puck, parsing works and the issue is in our identity-check or HeldObjectController math. Heartbeat line goes from a bare 'tick #N' to: [heartbeat] tick #N t=Xs | active=True wndProcCalls=NNN siEvents=NN siMotion=NN axes=T(0.00,0.00,0.00) R(0.00,0.00,0.00) This narrows the failure to one of three places without another ship. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v0.3.7
v0.3.7: read/write PhysGrabber fields via Traverse to bypass access c…
v0.3.6
v0.3.6: ship System.Numerics.Vectors.dll alongside the plugin v0.3.5's BepInEx LogOutput.log was 2.2 MB / 31k lines, almost entirely this exception repeating every frame: FileNotFoundException: Could not load file or assembly 'System.Numerics.Vectors, Version=4.1.3.0, ...' (wrapper dynamic-method) PhysGrabber.DMD<PhysGrabber::Update> Our Core project uses System.Numerics.Quaternion and Vector3, which live in System.Numerics.Vectors.dll. The Release build correctly emits it next to SpaceMouseRepo.dll, but the package script and CI workflow only copied the two SpaceMouseRepo DLLs into the staging directory — System.Numerics.Vectors.dll never made it into the zip. Unity's Mono runtime doesn't have this assembly in the GAC, so Harmony's JIT-compiled wrapper for PhysGrabber.Update threw on every frame, taking the game hostage. Fix: copy System.Numerics.Vectors.dll in both packaging paths. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v0.3.5
v0.3.5: move rotation injection from GetRotationInput to Update postfix v0.3.4 confirmed two things from the BepInEx log: - Lifecycle workaround (orphan + DontDestroyOnLoad + HideAndDontSave) unblocked patches: PhysGrabber.Update POSTFIX fired at line 28, Plugin.Update heartbeats fired every 5s for 2+ minutes - GetRotationInput POSTFIX never appeared, even during gameplay So PhysGrabber.GetRotationInput() simply isn't called by the vanilla code path that drives normal grab rotation — it's likely only invoked when a specific keyboard rotation modifier is held. The actual per- frame rotation must happen elsewhere (probably in Update or ObjectTurning). This release moves the rotation injection into the Update postfix that we know fires. On each call we: - gate on isLocal && grabbed - accumulate the SpaceMouse delta in HeldObjectController - compose ourDelta * vanilla.physRotation and write it back - mirror to nextPhysRotation in case downstream reads from that field Three new diag log lines (each fires once per session): - 'PhysGrabber.Update POSTFIX firing.' — confirms the new postfix - 'First time observing grabbed=true on local PhysGrabber.' — confirms we hit the holding branch - 'First rotation write: before=(...) delta=(...)' — confirms we computed and wrote a non-identity rotation Stub PhysGrabber.cs adds nextPhysRotation since we now write to it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v0.3.4
v0.3.4: orphan GameObject + DontDestroyOnLoad to survive scene changes The v0.3.3 side-channel diagnostic was conclusive: Plugin.Awake fires once and writes its 'Awake complete' line, then Update never runs again — across 5+ minutes of gameplay we got zero heartbeats. The plugin's MonoBehaviour is being torn down between Awake and the first in-game frame. The working DualGrab mod's decompiled Awake explicitly orphans its GameObject (parent = null) and sets HideFlags.HideAndDontSave. We were relying on BepInEx's default lifecycle, which is apparently not sufficient in R.E.P.O. — its scene transitions seem to clean up the BepInEx_Manager GameObject our component lived on. Replicating DualGrab's three-step trick (reparent to root, hide+don't-save flag, explicit DontDestroyOnLoad) should keep our component alive across the main-menu → level transition. Also bundles the new 256x256 logo (six-axis SpaceMouse puck design) into the package — first non-placeholder icon on Thunderstore. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>