Thanks for your interest! This project reverse-engineers the local SteelSeries Sonar API, so contributing has two sides: regular C# work, and careful verification against the real API. This guide covers both.
Start by reading docs/ARCHITECTURE.md - most design questions are answered there.
- .NET SDK 10 (the library multi-targets
net8.0;net10.0) - Windows with SteelSeries GG installed and Sonar enabled - only needed for live testing: the unit test suite runs anywhere, without GG
- Solution layout:
SteelSeriesAPI- the library (the only shipped project)SteelSeriesAPI.Tests- xUnit tests, no network, run everywhereSteelSeriesAPI.Sample- manual test bench: dumps the full state, then prints every event liveSteelSeriesAPI.Explorer- API exploration & verification tool (see below)
dotnet build
dotnet test # must stay green on net8.0 and net10.0
dotnet run --project SteelSeriesAPI.Sample # requires GG running
dotnet run --project SteelSeriesAPI.Explorer # requires GG runningThe Explorer is how this library was built and how it stays alive across GG updates.
| Command | Purpose |
|---|---|
get <route> / put <route> |
Play with any route by hand |
probe <r1> <r2>... |
Test route candidates (distinguishes 404 from wrong-mode 500) |
ws [path] |
Listen to a WebSocket and print every message |
dump |
Snapshot every known GET route into dumps/<date>/ |
check |
Compare response structures against committed references (reference-shapes/) |
check update |
Re-capture the references (do this only on a version you trust) |
verify |
Live write round-trips: set, read back, restore, for every write route |
check and verify handle the mixer mode themselves and restore your state.
GG updates are what break this kind of library. When one lands:
dotnet run --project SteelSeriesAPI.Explorer -- checkSTRUCTURE CHANGED? Diff the.shape.jsonvs.shape.json.actualfiles: that's exactly what SteelSeries changed.
dotnet run --project SteelSeriesAPI.Explorer -- verify- a
FAILmeans a write contract broke (renamed route, new vocabulary...).
- a
dump, and diff against the previous dated folder for the human-readable view.- If something broke: fix
SonarRoutes/parsers, update the test fixtures with freshly captured payloads (keep the capture date comment), runcheck update, and commit the new references with the fix. - Nothing broke? Enjoy, you just spent 30 seconds.
Every manager follows the same shape - copy an existing one (e.g. ChatMixManager
for a simple one, RedirectionsManager for a rich one):
- Explore first. Find the routes (Explorer
probe, Wireshark on loopback, or grepping GG'sapp.asar), capture real payloads, verify writes by hand. SonarRoutes: add the routes, commented and dated ("Verified live on ...").Models/: immutable records, only the fields the library exposes.Managers/: public interface +internal sealedclass takingISonarTransport. Parsing goes in a pureinternal staticmethod. Validate before sending.SonarClient: expose the interface, instantiate in the constructor.- Tests: fixtures = your captured payloads (dated), plus the standard cases: real payload parses, unknown entries are skipped without crashing, write routes build exactly the verified strings, validation throws before any HTTP call.
- Sample: add the state dump and event subscriptions if relevant.
- If the domain needs change detection, follow the hybrid pattern in
SonarEventListener.Redirections.cs(it reusesDebouncedRefresher).
- Unit tests run against
FakeTransportwith real captured payloads as fixtures. They protect this library's code; they cannot detect SteelSeries-side changes - that's the Explorer'scheck/verifyjob. - Keep the suite green on both target frameworks; CI runs it on every push/PR.
- New fixtures: paste the real payload (trim huge blobs like EQ data), date it.
- C# 12, nullable enabled, XML docs on every public member (English).
- Comments explain why, and carry dates when they encode an empirical finding.
- No new public API without XML docs; no route strings outside
SonarRoutes.