use dartboard_core::{Canvas, CanvasOp, ClientOpId, UserId};
pub use dartboard_local::{
CanvasStore, ColorSelectionMode, ConnectOutcome, Hello, InMemStore, LocalClient, MAX_PLAYERS,
};
mod ws;
#[derive(Clone)]
pub struct ServerHandle {
local: dartboard_local::ServerHandle,
}
impl ServerHandle {
pub fn spawn_local<S: CanvasStore + 'static>(store: S) -> Self {
Self::spawn_local_with_color_selection_mode(store, ColorSelectionMode::default())
}
pub fn spawn_local_with_color_selection_mode<S: CanvasStore + 'static>(
store: S,
color_selection_mode: ColorSelectionMode,
) -> Self {
Self {
local: dartboard_local::ServerHandle::spawn_local_with_color_selection_mode(
store,
color_selection_mode,
),
}
}
pub fn try_connect_local(&self, hello: Hello) -> ConnectOutcome {
self.local.try_connect_local(hello)
}
pub fn connect_local(&self, hello: Hello) -> LocalClient {
self.local.connect_local(hello)
}
pub fn peer_count(&self) -> usize {
self.local.peer_count()
}
pub fn canvas_snapshot(&self) -> Canvas {
self.local.canvas_snapshot()
}
pub fn bind_ws(&self, addr: std::net::SocketAddr) -> std::io::Result<()> {
let (ready_tx, ready_rx) = std::sync::mpsc::channel();
let server = self.clone();
std::thread::spawn(move || {
let runtime = match tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
{
Ok(rt) => rt,
Err(e) => {
let _ = ready_tx.send(Err(e));
return;
}
};
runtime.block_on(async move {
match tokio::net::TcpListener::bind(addr).await {
Ok(listener) => {
let _ = ready_tx.send(Ok(()));
loop {
let Ok((stream, _)) = listener.accept().await else {
break;
};
let server = server.clone();
tokio::spawn(async move {
if let Err(e) = ws::accept_and_run(server, stream).await {
eprintln!("ws connection ended: {}", e);
}
});
}
}
Err(e) => {
let _ = ready_tx.send(Err(e));
}
}
});
});
ready_rx
.recv()
.unwrap_or_else(|_| Err(std::io::Error::other("ws thread disappeared")))
}
pub(crate) fn register_transport(
&self,
hello: Hello,
sender: Box<dyn dartboard_local::ServerSink>,
) -> Result<UserId, String> {
self.local.register_transport(hello, sender)
}
pub(crate) fn submit_op_for(&self, user_id: UserId, client_op_id: ClientOpId, op: CanvasOp) {
self.local.submit_op_for(user_id, client_op_id, op);
}
pub(crate) fn disconnect_user(&self, user_id: UserId) {
self.local.disconnect_user(user_id);
}
}