use std::collections::HashMap;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use thiserror::Error;
use tokio::net::UdpSocket;
use tokio::sync::{mpsc, oneshot, RwLock};
use super::session::{ServerSession, ServerSessionId};
use crate::core::SyncState;
#[derive(Debug, Error)]
pub enum ServerError {
#[error("bind failed: {0}")]
BindFailed(String),
#[error("session error: {0}")]
SessionError(String),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("server shut down")]
Shutdown,
#[error("invalid handshake: {0}")]
InvalidHandshake(String),
}
#[derive(Debug, Clone)]
pub struct ServerConfig {
pub bind_addr: SocketAddr,
pub private_key: [u8; 32],
pub max_sessions: usize,
pub session_timeout: Duration,
pub enable_compression: bool,
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
bind_addr: "0.0.0.0:19999".parse().unwrap(),
private_key: [0u8; 32],
max_sessions: 1000,
session_timeout: Duration::from_secs(300),
enable_compression: true,
}
}
}
#[derive(Debug)]
pub struct NomadServerBuilder {
config: ServerConfig,
}
impl NomadServerBuilder {
pub fn new() -> Self {
Self {
config: ServerConfig::default(),
}
}
pub fn bind_addr(mut self, addr: SocketAddr) -> Self {
self.config.bind_addr = addr;
self
}
pub fn private_key(mut self, key: [u8; 32]) -> Self {
self.config.private_key = key;
self
}
pub fn max_sessions(mut self, max: usize) -> Self {
self.config.max_sessions = max;
self
}
pub fn session_timeout(mut self, timeout: Duration) -> Self {
self.config.session_timeout = timeout;
self
}
pub fn compression(mut self, enabled: bool) -> Self {
self.config.enable_compression = enabled;
self
}
pub fn build(self) -> ServerConfig {
self.config
}
}
impl Default for NomadServerBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub enum ServerEvent<S: SyncState> {
ClientConnected {
session_id: ServerSessionId,
client_public_key: [u8; 32],
},
StateUpdated {
session_id: ServerSessionId,
state: S,
},
ClientDisconnected {
session_id: ServerSessionId,
},
}
pub struct SessionSender<S: SyncState> {
session_id: ServerSessionId,
tx: mpsc::Sender<(ServerSessionId, S)>,
}
impl<S: SyncState> SessionSender<S> {
pub async fn send(&self, state: S) -> Result<(), ServerError> {
self.tx
.send((self.session_id, state))
.await
.map_err(|_| ServerError::Shutdown)
}
pub fn session_id(&self) -> ServerSessionId {
self.session_id
}
}
impl<S: SyncState> Clone for SessionSender<S> {
fn clone(&self) -> Self {
Self {
session_id: self.session_id,
tx: self.tx.clone(),
}
}
}
pub struct NomadServer<S: SyncState> {
config: ServerConfig,
sessions: Arc<RwLock<HashMap<ServerSessionId, ServerSession<S>>>>,
state_tx: mpsc::Sender<(ServerSessionId, S)>,
shutdown_tx: Option<oneshot::Sender<()>>,
local_addr: SocketAddr,
}
impl<S: SyncState> NomadServer<S> {
pub async fn bind<F>(
config: ServerConfig,
_state_factory: F,
) -> Result<(Self, mpsc::Receiver<ServerEvent<S>>), ServerError>
where
F: Fn() -> S + Send + Sync + 'static,
{
let socket = UdpSocket::bind(config.bind_addr)
.await
.map_err(|e| ServerError::BindFailed(e.to_string()))?;
let local_addr = socket.local_addr()?;
let (state_tx, _state_rx) = mpsc::channel::<(ServerSessionId, S)>(256);
let (event_tx, event_rx) = mpsc::channel::<ServerEvent<S>>(256);
let (shutdown_tx, _shutdown_rx) = oneshot::channel();
let sessions: Arc<RwLock<HashMap<ServerSessionId, ServerSession<S>>>> =
Arc::new(RwLock::new(HashMap::new()));
let _sessions_clone = sessions.clone();
let _config_clone = config.clone();
let _event_tx = event_tx;
tokio::spawn(async move {
let mut buf = [0u8; 65535];
while let Ok((_len, _addr)) = socket.recv_from(&mut buf).await {
}
});
let server = Self {
config,
sessions,
state_tx,
shutdown_tx: Some(shutdown_tx),
local_addr,
};
Ok((server, event_rx))
}
pub fn local_addr(&self) -> SocketAddr {
self.local_addr
}
pub async fn session_count(&self) -> usize {
self.sessions.read().await.len()
}
pub async fn send_to(&self, session_id: ServerSessionId, state: S) -> Result<(), ServerError> {
self.state_tx
.send((session_id, state))
.await
.map_err(|_| ServerError::Shutdown)
}
pub async fn broadcast(&self, state: S) -> Result<(), ServerError> {
let sessions = self.sessions.read().await;
for session_id in sessions.keys() {
self.state_tx
.send((*session_id, state.clone()))
.await
.map_err(|_| ServerError::Shutdown)?;
}
Ok(())
}
pub fn session_sender(&self, session_id: ServerSessionId) -> SessionSender<S> {
SessionSender {
session_id,
tx: self.state_tx.clone(),
}
}
pub async fn disconnect(&self, session_id: ServerSessionId) -> Result<(), ServerError> {
let mut sessions = self.sessions.write().await;
if sessions.remove(&session_id).is_some() {
Ok(())
} else {
Err(ServerError::SessionError(format!(
"session not found: {:?}",
session_id
)))
}
}
pub async fn shutdown(mut self) -> Result<(), ServerError> {
if let Some(tx) = self.shutdown_tx.take() {
let _ = tx.send(());
}
self.sessions.write().await.clear();
Ok(())
}
pub fn config(&self) -> &ServerConfig {
&self.config
}
}
impl<S: SyncState> Drop for NomadServer<S> {
fn drop(&mut self) {
if let Some(tx) = self.shutdown_tx.take() {
let _ = tx.send(());
}
}
}
#[cfg(test)]
mod tests {
}