1use dartboard_core::{Canvas, CanvasOp, ClientOpId, UserId};
9
10pub use dartboard_local::{
11 CanvasStore, ColorSelectionMode, ConnectOutcome, Hello, InMemStore, LocalClient, MAX_PLAYERS,
12};
13
14mod ws;
15
16#[derive(Clone)]
17pub struct ServerHandle {
18 local: dartboard_local::ServerHandle,
19}
20
21impl ServerHandle {
22 pub fn spawn_local<S: CanvasStore + 'static>(store: S) -> Self {
23 Self::spawn_local_with_color_selection_mode(store, ColorSelectionMode::default())
24 }
25
26 pub fn spawn_local_with_color_selection_mode<S: CanvasStore + 'static>(
27 store: S,
28 color_selection_mode: ColorSelectionMode,
29 ) -> Self {
30 Self {
31 local: dartboard_local::ServerHandle::spawn_local_with_color_selection_mode(
32 store,
33 color_selection_mode,
34 ),
35 }
36 }
37
38 pub fn try_connect_local(&self, hello: Hello) -> ConnectOutcome {
39 self.local.try_connect_local(hello)
40 }
41
42 pub fn connect_local(&self, hello: Hello) -> LocalClient {
43 self.local.connect_local(hello)
44 }
45
46 pub fn peer_count(&self) -> usize {
47 self.local.peer_count()
48 }
49
50 pub fn canvas_snapshot(&self) -> Canvas {
51 self.local.canvas_snapshot()
52 }
53
54 pub fn bind_ws(&self, addr: std::net::SocketAddr) -> std::io::Result<()> {
62 let (ready_tx, ready_rx) = std::sync::mpsc::channel();
63 let server = self.clone();
64 std::thread::spawn(move || {
65 let runtime = match tokio::runtime::Builder::new_multi_thread()
66 .enable_all()
67 .build()
68 {
69 Ok(rt) => rt,
70 Err(e) => {
71 let _ = ready_tx.send(Err(e));
72 return;
73 }
74 };
75 runtime.block_on(async move {
76 match tokio::net::TcpListener::bind(addr).await {
77 Ok(listener) => {
78 let _ = ready_tx.send(Ok(()));
79 loop {
80 let Ok((stream, _)) = listener.accept().await else {
81 break;
82 };
83 let server = server.clone();
84 tokio::spawn(async move {
85 if let Err(e) = ws::accept_and_run(server, stream).await {
86 eprintln!("ws connection ended: {}", e);
87 }
88 });
89 }
90 }
91 Err(e) => {
92 let _ = ready_tx.send(Err(e));
93 }
94 }
95 });
96 });
97
98 ready_rx
99 .recv()
100 .unwrap_or_else(|_| Err(std::io::Error::other("ws thread disappeared")))
101 }
102
103 pub(crate) fn register_transport(
104 &self,
105 hello: Hello,
106 sender: Box<dyn dartboard_local::ServerSink>,
107 ) -> Result<UserId, String> {
108 self.local.register_transport(hello, sender)
109 }
110
111 pub(crate) fn submit_op_for(&self, user_id: UserId, client_op_id: ClientOpId, op: CanvasOp) {
112 self.local.submit_op_for(user_id, client_op_id, op);
113 }
114 pub(crate) fn disconnect_user(&self, user_id: UserId) {
115 self.local.disconnect_user(user_id);
116 }
117}