Skip to main content

gled_core/engine/
session.rs

1//! Connections of user interfaces to the engine. Any number can be open at
2//! once: the desktop app in-process, and browsers or remote desktops over the
3//! network. Each has its own numbered actions and held keys; all get the same
4//! state.
5
6use std::{
7    collections::BTreeSet,
8    sync::{
9        Arc,
10        atomic::{AtomicU64, Ordering::Relaxed},
11    },
12};
13
14use kanal::{Receiver, Sender, bounded, unbounded};
15use tracing::warn;
16
17use super::{ClientInput, EngineEvent, EngineState, InputLog, LearnLog, MidiLog, SoundSnapshot};
18use crate::{action::UiAction, input::key_code::KeyCode, pipeline::extract_output::PreviewOutput};
19use gled_model::collection::Collections as AssetCollections;
20
21/// Identifies a connection while it is open.
22pub type ClientId = u64;
23
24/// What the engine's input channel carries.
25#[derive(Debug)]
26pub(super) enum EngineInput {
27    Connect {
28        id: ClientId,
29        state: Sender<ClientState>,
30        events: Sender<EngineEvent>,
31    },
32    Client {
33        id: ClientId,
34        input: ClientInput,
35    },
36    Disconnect(ClientId),
37}
38
39/// The engine's state as one client sees it: the shared state, and how far
40/// the engine has got with this client's actions.
41#[derive(Debug, Clone)]
42pub struct ClientState {
43    pub(crate) shared: Arc<EngineState>,
44    /// The number of the newest action of this client the engine applied.
45    pub(crate) applied_sequence: u64,
46    /// The engine's assets. A new value whenever they changed.
47    pub(crate) assets: Arc<AssetCollections>,
48    /// The newest rendered output for the preview, a new value per frame.
49    pub(crate) preview: Option<PreviewOutput>,
50    /// The sound levels, a new value 30 times a second.
51    pub(crate) sound: Arc<SoundSnapshot>,
52    /// The input events of the last ticks.
53    pub(crate) input_log: Arc<InputLog>,
54    /// MIDI traffic of the last ticks, while an interface watches it.
55    pub(crate) midi_log: Arc<MidiLog>,
56    /// Messages captured for MIDI learn.
57    pub(crate) learn_log: Arc<LearnLog>,
58}
59
60/// The engine's record of an open connection.
61#[derive(Debug)]
62pub(super) struct Session {
63    pub(super) state: Sender<ClientState>,
64    pub(super) events: Sender<EngineEvent>,
65    pub(super) applied_sequence: u64,
66    pub(super) keys: BTreeSet<KeyCode>,
67}
68
69/// Opens connections to the engine. Cheap to clone; the engine runs as long
70/// as a connector or a connection exists.
71#[derive(Debug, Clone)]
72pub struct EngineConnector {
73    pub(super) inputs: Sender<EngineInput>,
74    pub(super) next_id: Arc<AtomicU64>,
75}
76
77impl EngineConnector {
78    /// Opens a new connection.
79    pub fn connect(&self) -> ClientConnection {
80        let id = self.next_id.fetch_add(1, Relaxed);
81        // A client that falls behind only needs the newest state.
82        let (state_sender, state) = bounded(4);
83        let (event_sender, events) = unbounded();
84        if let Err(err) = self.inputs.send(EngineInput::Connect {
85            id,
86            state: state_sender,
87            events: event_sender,
88        }) {
89            warn!("The engine has stopped: {err}");
90        }
91        ClientConnection {
92            id,
93            inputs: self.inputs.clone(),
94            sequence: Arc::default(),
95            state,
96            events,
97        }
98    }
99}
100
101/// One client's end of the engine. Closes when dropped.
102#[derive(Debug)]
103pub struct ClientConnection {
104    id: ClientId,
105    inputs: Sender<EngineInput>,
106    /// Number of the last action sent.
107    sequence: Arc<AtomicU64>,
108    state: Receiver<ClientState>,
109    events: Receiver<EngineEvent>,
110}
111
112impl ClientConnection {
113    pub fn id(&self) -> ClientId {
114        self.id
115    }
116
117    /// Sends an action with the client's own number, for clients that number
118    /// their actions themselves (remote ones).
119    pub fn send_numbered_action(&self, sequence: u64, action: UiAction) {
120        self.sequence.fetch_max(sequence, Relaxed);
121        self.send(ClientInput::Action { sequence, action });
122    }
123
124    /// Sends other input.
125    pub fn send(&self, input: ClientInput) {
126        if let Err(err) = self.inputs.send(EngineInput::Client { id: self.id, input }) {
127            warn!("The engine has stopped: {err}");
128        }
129    }
130
131    /// The newest state since the last call, if any.
132    pub fn latest_state(&self) -> Option<ClientState> {
133        let mut latest = None;
134        while let Ok(Some(state)) = self.state.try_recv() {
135            latest = Some(state);
136        }
137        latest
138    }
139
140    /// The receiver of states, for clients that wait on several sources.
141    pub fn state_receiver(&self) -> &Receiver<ClientState> {
142        &self.state
143    }
144
145    /// The receiver of events, for clients that wait on several sources.
146    pub fn event_receiver(&self) -> &Receiver<EngineEvent> {
147        &self.events
148    }
149
150    /// The next event, if any.
151    pub fn next_event(&self) -> Option<EngineEvent> {
152        self.events.try_recv().ok().flatten()
153    }
154}
155
156impl Drop for ClientConnection {
157    fn drop(&mut self) {
158        // Fails only when the engine is gone, which needs no goodbye.
159        let _ = self.inputs.send(EngineInput::Disconnect(self.id));
160    }
161}