Skip to content

Allow players to change supported_commands mid-session - #177

Merged
maximmaxim345 merged 7 commits into
mainfrom
refactor/player-supported-commands
Sep 2, 2026
Merged

Allow players to change supported_commands mid-session#177
maximmaxim345 merged 7 commits into
mainfrom
refactor/player-supported-commands

Conversation

@maximmaxim345

@maximmaxim345 maximmaxim345 commented Aug 27, 2026

Copy link
Copy Markdown
Member

supported_commands was split across two messages, 'volume' and 'mute' in the client/hello support object and 'set_output_delay' in client/state, so a player could not change which commands it accepts without reconnecting and server/command had to name both locations as sources of truth. The whole list moves into the client/state player object.

The split also failed silently: an SDK reading only client/hello ignores set_output_delay, one reading only client/state ignores volume and mute, and neither errors. Consolidating matches the CONTRIBUTING rule that a field expected to change during a connection belongs in client/state.

This breaks player@v1 on the wire, and a server can no longer see command capabilities at server/activate time, which is exotic but worth stating.

#175 already made supported_commands non-optional and defined an empty list as accepting no commands, so the field line itself only gains 'volume' and 'mute', and the client/hello copy is deleted.

Rules the move requires

A server MUST NOT send a player command before the client has sent a client/state containing the player object, mirroring the existing rule for binary data. The move opens that window because server/activate precedes the first state message, and the player object is the unit that carries supported_commands.

supported_commands advertises settability, not reportability. A player MAY report volume or muted without accepting the matching command, an amplifier with a physical volume knob being the real case, and capability cannot be inferred from field presence because output_delay_ms is never optional either way. A server MUST NOT treat a reported volume or muted as settable while the matching command is absent, though it MAY still surface the value read-only.

Group volume averages over volume-capable players, so when the last capable player withdraws the reported value jumps to 100 and the control disappears with nobody having touched a volume. Both reading rules now say so and require a recompute when a player's support changes.

Group volume writes

The group volume algorithm already disagreed with itself before this change: it computed its delta against the average of all player volumes and sent a command to every player, while the reading rules excluded players without volume support. The move makes the mismatch reachable mid-session, so both paths now filter on capability, and setting group mute gets the same fix. This part is separable if you would rather it went on its own.

Editorial

The "Server behavior" bullets sat under the client/hello support-object heading while describing required_lead_time_ms and min_buffer_ms, which live in client/state, and two cross-references pointed at that mismatched anchor. The text merges into "Server Audio Send Constraints", which already carries the overlapping lead and send-ahead rules.

Closes #142

@arturpragacz arturpragacz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weren't we supposed to align all dynamically changing values and move them all to client/state? So including for example visualizer's and artwork's stream/request-format fields.

@maximmaxim345

Copy link
Copy Markdown
Member Author

Weren't we supposed to align all dynamically changing values and move them all to client/state? So including for example visualizer's and artwork's stream/request-format fields.

Thats for a different PR though since it touches multiple roles, I'll write that once #175 is merged.

The reading rules excluded players without volume support while the
write path averaged over all players and sent a command to each, so a
player that does not list 'volume' received a command `server/command`
forbids.
Comment thread roles/controller/v1.md Outdated
Comment thread roles/controller/v1.md Outdated
Comment thread roles/player/v1.md Outdated
@marcelveldt marcelveldt added this to the 1.0.0 milestone Sep 2, 2026
kahrendt pushed a commit that referenced this pull request Sep 2, 2026
`stream/request-format` was a request in name only: a decline is
invisible (the stream continues and the request MUST NOT be treated as
an error), so a client only ever learns the operative configuration from
`stream/start`, and with no stream active the server already had to
remember the requested format as de facto state. Per the CONTRIBUTING
rule that fields expected to change during a connection belong in
`client/state`, the message is dropped and its fields move there.

# Changes

`player` gains an optional `format` preference in its state object;
absent means the server selects per the `supported_formats` priority
order, a present preference must be one of the listed entries (which
also removes the previously undefined partial-request case), and the
server may still deviate, for example to match a track's native sample
rate or to apply an operator-configured format. `artwork` moves the
per-channel `source`/`format`/`width`/`height` into a state `channels`
array and `artwork@v1_support` is removed entirely: the array is
positional from channel 0, an uncovered channel is `source: 'none'` so
clients may truncate after their last active channel, an array longer
than 4 is a protocol error, and `format`/`width`/`height` are required
only when `source` is not `'none'`. `visualizer` moves `types`,
`rate_max` and `spectrum` into state; its hello object keeps only
`buffer_capacity`. `stream/start` now derives each role's configuration
from the hello constants plus the role's current state, answers a state
change on an active stream with a new `stream/start` when the
configuration changed, never starts a stream in response, and should
wait for the state update a role (re-)activation requires before
starting that role's stream.

# Breaking changes

The `stream/request-format` message is removed; clients that send it
break, and servers drop the handler. Artwork clients no longer send
`artwork@v1_support` and instead carry their channel configuration in
`client/state`; visualizer clients keep only `buffer_capacity` in their
support object and carry the rest in `client/state`. Players are
affected only if they used `stream/request-format`; the new `format`
state field is optional.

Conflicts with #177 in the player state object; whichever lands second
needs a rebase.

Closes #190

@kahrendt kahrendt left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@maximmaxim345
maximmaxim345 merged commit c4940f6 into main Sep 2, 2026
1 check passed
@maximmaxim345
maximmaxim345 deleted the refactor/player-supported-commands branch September 2, 2026 15:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

player@v1: consolidate supported_commands into client/state and give the timing/buffering server rules their own section

4 participants