use crate::{
model::{Destroy, OutgoingEvent},
node::{Node, NodeSenderError},
};
use dashmap::DashMap;
use std::{
fmt::Debug,
sync::{
atomic::{AtomicBool, AtomicI64, AtomicU16, AtomicU64, Ordering},
Arc,
},
};
use twilight_model::id::{
marker::{ChannelMarker, GuildMarker},
Id,
};
#[derive(Clone, Debug, Default)]
pub struct PlayerManager {
pub(crate) players: Arc<DashMap<Id<GuildMarker>, Arc<Player>>>,
}
impl PlayerManager {
pub(crate) fn new() -> Self {
Self::default()
}
pub fn get(&self, guild_id: &Id<GuildMarker>) -> Option<Arc<Player>> {
self.players.get(guild_id).map(|r| Arc::clone(r.value()))
}
pub fn get_or_insert(&self, guild_id: Id<GuildMarker>, node: Arc<Node>) -> Arc<Player> {
let player = self
.players
.entry(guild_id)
.or_insert_with(|| Arc::new(Player::new(guild_id, node)));
Arc::clone(&player)
}
pub fn destroy(&self, guild_id: Id<GuildMarker>) -> Result<(), NodeSenderError> {
if let Some(player) = self.get(&guild_id) {
player
.node()
.send(OutgoingEvent::from(Destroy::new(guild_id)))?;
self.players.remove(&guild_id);
}
Ok(())
}
}
#[derive(Debug)]
pub struct Player {
channel_id: AtomicU64,
guild_id: Id<GuildMarker>,
node: Arc<Node>,
paused: AtomicBool,
position: AtomicI64,
time: AtomicI64,
volume: AtomicU16,
}
impl Player {
pub(crate) const fn new(guild_id: Id<GuildMarker>, node: Arc<Node>) -> Self {
Self {
channel_id: AtomicU64::new(0),
guild_id,
node,
paused: AtomicBool::new(false),
position: AtomicI64::new(0),
time: AtomicI64::new(0),
volume: AtomicU16::new(100),
}
}
pub fn send(&self, event: impl Into<OutgoingEvent>) -> Result<(), NodeSenderError> {
self._send(event.into())
}
fn _send(&self, event: OutgoingEvent) -> Result<(), NodeSenderError> {
tracing::debug!("sending event on guild player {}: {event:?}", self.guild_id);
match &event {
OutgoingEvent::Pause(event) => self.paused.store(event.pause, Ordering::Release),
OutgoingEvent::Volume(event) => {
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
self.volume.store(event.volume as u16, Ordering::Release);
}
_ => {}
}
self.node.send(event)
}
pub const fn node(&self) -> &Arc<Node> {
&self.node
}
pub fn channel_id(&self) -> Option<Id<ChannelMarker>> {
let channel_id = self.channel_id.load(Ordering::Acquire);
if channel_id == 0 {
None
} else {
Some(Id::new(channel_id))
}
}
pub(crate) fn set_channel_id(&self, channel_id: Option<Id<ChannelMarker>>) {
self.channel_id
.store(channel_id.map_or(0_u64, Id::get), Ordering::Release);
}
pub const fn guild_id(&self) -> Id<GuildMarker> {
self.guild_id
}
pub fn paused(&self) -> bool {
self.paused.load(Ordering::Acquire)
}
pub fn position(&self) -> i64 {
self.position.load(Ordering::Relaxed)
}
pub(crate) fn set_position(&self, position: i64) {
self.position.store(position, Ordering::Release);
}
pub fn time(&mut self) -> i64 {
self.time.load(Ordering::Relaxed)
}
pub(crate) fn set_time(&self, time: i64) {
self.time.store(time, Ordering::Release);
}
pub fn volume(&self) -> u16 {
self.volume.load(Ordering::Relaxed)
}
}
#[cfg(test)]
mod tests {
use super::{Player, PlayerManager};
use static_assertions::assert_impl_all;
use std::fmt::Debug;
assert_impl_all!(PlayerManager: Debug, Default, Send, Sync);
assert_impl_all!(Player: Debug, Send, Sync);
}