mumble_server_runtime_shard/
mumble_server_runtime_shard.rs

1//! A shard runtime: render once per shard, share the **changes**, not the views.
2//!
3//! # The model in one page
4//!
5//! The pipeline this crate replaces rendered *the whole world as seen by one
6//! connection*, once per connection. Everything else followed mechanically: N
7//! connections each holding an O(N) view means materializing every view costs
8//! Θ(N²), and no scheduler or cache fixes that, because it is in the type.
9//!
10//! Here, a shard renders **once**. Each fact is held once, whoever ends up
11//! seeing it. What connections receive is the resulting delta, filtered:
12//!
13//! ```text
14//!   business state changes
15//!        |  handle.wake()
16//!        v
17//!   render -> plan -> journal -> per-connection: filter, splice, collapse
18//! ```
19//!
20//! A three-operation delta filtered N times costs O(N·|D|), not O(N·W). That
21//! property holds even when every view is different, which is what makes it
22//! robust rather than merely fast.
23//!
24//! # Three independent mechanisms
25//!
26//! Wanting to unify them is the mistake that breeds special cases.
27//!
28//! | mechanism | shape | what it solves | cost |
29//! |---|---|---|---|
30//! | [shared view + scopes](scope) | a scope tree, one scope per element, a `ScopeSet` per observer | parties, teams, spectators, staff - the 95% | O(W) once + O(\|D\|) per connection |
31//! | [private overlay](view::Overlay) | a few elements visible to **one** connection | vanish, private channel, per-observer placement | O(\|overlay\|) |
32//! | [audio relation](routing) | a **directed** relation per receiver | all of the audio | O(N + edges) |
33//!
34//! The rule that says which to use: **a scope describes a group, an overlay
35//! describes an individual exception.** A scope with one observer is an overlay
36//! in disguise. A role - player, host, spectator, staff - is a group by nature
37//! even when it has one member.
38//!
39//! # The only coupling, and it is not ours
40//!
41//! > **A receiver must see the sender.**
42//!
43//! The Mumble client discards audio whose sender session it does not know, so
44//! this is a protocol constraint rather than a design choice, and it is checked
45//! on the render's output. A corollary worth stating: there is no separate
46//! "right to speak". Speaking somewhere implies being visible there.
47//!
48//! REF: docs/design/guide-implementation.md
49//! REF: references/mumble/src/mumble/ServerHandler.cpp : `handleVoicePacket`
50#![forbid(unsafe_code)]
51
52pub mod build;
53pub mod compose;
54pub mod emit;
55pub mod ids;
56pub mod journal;
57pub mod plan;
58pub mod queue;
59pub mod reply;
60pub mod routing;
61pub mod scope;
62pub mod shard;
63pub mod view;
64
65pub use build::{
66    BuildError, ChannelRef, MAX_ACTIONS, Narrow, PrivateBuilder, Rendered, ShardBuilder, UserRef,
67};
68pub use compose::{collapse, filter, splice};
69pub use emit::{
70    TextTarget, action_key, action_name, actions, denied_permission, emit, perm, permission_query,
71    permissions_of, relayed, spoken, user_stats,
72};
73pub use ids::{
74    ActionKey, ChannelId, ChannelKey, ConnectionId, Exhausted, IdAllocator, Occupant, SessionId,
75    ShardId, SharedIds, SyntheticId,
76};
77pub use journal::{Journal, TooFarBehind};
78pub use plan::{
79    ChannelPatch, ElementId, OverlayOps, PlanOp, PlannedOp, UserPatch, plan, plan_elements,
80};
81pub use queue::{MAX_DEPTH_FOR_VOICE, OutboundQueue, Refused, VoiceAdmission};
82pub use reply::{Audience, Effect, Effects, Reply, Spoken, Word};
83pub use routing::{AudioRelation, AudioRouting, DomainId, Silence, compile};
84pub use scope::{MAX_DEPTH, MAX_OBSERVED, Scope, ScopeSet, TooManyScopes};
85pub use shard::{
86    ActionTarget, AttachedConnection, Handover, MIN_INTERVAL, ReconcileReport, Shard, ShardCommand,
87    ShardHandle, ShardLogic, VoiceEvent, run, spawn_parts,
88};
89pub use view::{Action, Actions, Channel, On, Overlay, ShardView, User, UserFlags};