use std::{
collections::BTreeSet,
sync::{
Arc,
atomic::{AtomicU64, Ordering::Relaxed},
},
};
use kanal::{Receiver, Sender, bounded, unbounded};
use tracing::warn;
use super::{ClientInput, EngineEvent, EngineState, InputLog, LearnLog, MidiLog, SoundSnapshot};
use crate::{action::UiAction, input::key_code::KeyCode, pipeline::extract_output::PreviewOutput};
use gled_model::collection::Collections as AssetCollections;
pub type ClientId = u64;
#[derive(Debug)]
pub(super) enum EngineInput {
Connect {
id: ClientId,
state: Sender<ClientState>,
events: Sender<EngineEvent>,
},
Client {
id: ClientId,
input: ClientInput,
},
Disconnect(ClientId),
}
#[derive(Debug, Clone)]
pub struct ClientState {
pub(crate) shared: Arc<EngineState>,
pub(crate) applied_sequence: u64,
pub(crate) assets: Arc<AssetCollections>,
pub(crate) preview: Option<PreviewOutput>,
pub(crate) sound: Arc<SoundSnapshot>,
pub(crate) input_log: Arc<InputLog>,
pub(crate) midi_log: Arc<MidiLog>,
pub(crate) learn_log: Arc<LearnLog>,
}
#[derive(Debug)]
pub(super) struct Session {
pub(super) state: Sender<ClientState>,
pub(super) events: Sender<EngineEvent>,
pub(super) applied_sequence: u64,
pub(super) keys: BTreeSet<KeyCode>,
}
#[derive(Debug, Clone)]
pub struct EngineConnector {
pub(super) inputs: Sender<EngineInput>,
pub(super) next_id: Arc<AtomicU64>,
}
impl EngineConnector {
pub fn connect(&self) -> ClientConnection {
let id = self.next_id.fetch_add(1, Relaxed);
let (state_sender, state) = bounded(4);
let (event_sender, events) = unbounded();
if let Err(err) = self.inputs.send(EngineInput::Connect {
id,
state: state_sender,
events: event_sender,
}) {
warn!("The engine has stopped: {err}");
}
ClientConnection {
id,
inputs: self.inputs.clone(),
sequence: Arc::default(),
state,
events,
}
}
}
#[derive(Debug)]
pub struct ClientConnection {
id: ClientId,
inputs: Sender<EngineInput>,
sequence: Arc<AtomicU64>,
state: Receiver<ClientState>,
events: Receiver<EngineEvent>,
}
impl ClientConnection {
pub fn id(&self) -> ClientId {
self.id
}
pub fn send_numbered_action(&self, sequence: u64, action: UiAction) {
self.sequence.fetch_max(sequence, Relaxed);
self.send(ClientInput::Action { sequence, action });
}
pub fn send(&self, input: ClientInput) {
if let Err(err) = self.inputs.send(EngineInput::Client { id: self.id, input }) {
warn!("The engine has stopped: {err}");
}
}
pub fn latest_state(&self) -> Option<ClientState> {
let mut latest = None;
while let Ok(Some(state)) = self.state.try_recv() {
latest = Some(state);
}
latest
}
pub fn state_receiver(&self) -> &Receiver<ClientState> {
&self.state
}
pub fn event_receiver(&self) -> &Receiver<EngineEvent> {
&self.events
}
pub fn next_event(&self) -> Option<EngineEvent> {
self.events.try_recv().ok().flatten()
}
}
impl Drop for ClientConnection {
fn drop(&mut self) {
let _ = self.inputs.send(EngineInput::Disconnect(self.id));
}
}