Fell out when attempting
https://github.com/ppy/osu-server-spectator/pull/346.
Functionally, if a true non-`HubException` is produced via an invocation
of a spectator server hub method, this doesn't really do much - the
error will still log as 'unobserved' due to the default handler, it will
still show up on sentry, etc. The only difference is that it'll get
handled via the continuation installed in `FireAndForget()` rather than
the `TaskScheduler.UnobservedTaskException` event.
The only real case where this is relevant is when the server throws
`HubException`s, which will now instead bubble up to a more
human-readable form. Which is relevant to the aforementioned PR because
that one makes any hub method potentially throw a `HubException` if the
client version is too old.
Obviously this does nothing for the existing old clients.
It proved to be too difficult to deal with the flow that clears user
states on stopping the watching of global presence updates. It's not
helped in the least that friends are updated via the API, so there's a
third flow to consider (and the timings therein - both server-spectator
and friends are updated concurrently).
Simplest is to separate the friends flow, though this does mean some
logic and state duplication.
First of all, this sort of cleanup isn't really the client's
responsibility, and secondly, at the point the client received this
request to disconnect, *none of its requests will be honored anymore*
(currently the only scenario of this if another client has connected
- the server-side concurrency filter will reject this request).
When disconnection is requested, the only valid thing to do with respect
to talking to the server is to stop doing it.
This will be moved server-side in a follow-up change, although I'm not
even strictly sure that's required - I'd like to think signalr would
know to clean up a disconnecting client from all groups they were in.