summaryrefslogtreecommitdiff
path: root/server/src
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2024-08-07 15:24:06 -0400
committerpommicket <pommicket@gmail.com>2024-08-07 15:24:06 -0400
commitbddf8ce87d175a9e2688d4ddda4424711d65388d (patch)
tree805cd0213457aa6b7a6132a45649638624ba6d2f /server/src
parent3f231dabb4d674113852a68e245ce8786dec2cb6 (diff)
a database!
Diffstat (limited to 'server/src')
-rw-r--r--server/src/main.rs69
1 files changed, 65 insertions, 4 deletions
diff --git a/server/src/main.rs b/server/src/main.rs
index d879ae6..81f2581 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -2,8 +2,24 @@ use futures_util::{SinkExt, StreamExt};
use std::net::SocketAddr;
use tokio::io::AsyncWriteExt;
use tungstenite::protocol::Message;
+use rand::Rng;
+use std::sync::LazyLock;
+use anyhow::anyhow;
-async fn handle_connection(conn: &mut tokio::net::TcpStream) -> anyhow::Result<()> {
+const PUZZLE_ID_CHARSET: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+const PUZZLE_ID_LEN: usize = 6;
+
+fn generate_puzzle_id() -> [u8; PUZZLE_ID_LEN] {
+ let mut rng = rand::thread_rng();
+ [(); 6].map(|()| PUZZLE_ID_CHARSET[rng.gen_range(0..PUZZLE_ID_CHARSET.len())])
+}
+
+struct Database {
+ puzzles: sled::Tree,
+ pieces: sled::Tree,
+}
+
+async fn handle_connection(database: &Database, conn: &mut tokio::net::TcpStream) -> anyhow::Result<()> {
let mut ws = tokio_tungstenite::accept_async_with_config(
conn,
Some(tungstenite::protocol::WebSocketConfig {
@@ -13,13 +29,48 @@ async fn handle_connection(conn: &mut tokio::net::TcpStream) -> anyhow::Result<(
}),
)
.await?;
+ let mut puzzle_id = None;
while let Some(message) = ws.next().await {
let message = message?;
if matches!(message, Message::Close(_)) {
break;
}
- println!("{:?}", message);
- ws.send(message).await?;
+ if let Message::Text(text) = &message {
+ let text = text.trim();
+ if let Some(dimensions) = text.strip_prefix("new ") {
+ let mut parts = dimensions.split(' ');
+ let width: u8 = parts.next().ok_or_else(|| anyhow!("no width"))?.parse()?;
+ let height: u8 = parts.next().ok_or_else(|| anyhow!("no height"))?.parse()?;
+ let url: &str = parts.next().ok_or_else(|| anyhow!("no url"))?;
+ if (width as u16) * (height as u16) > 1000 {
+ return Err(anyhow!("too many pieces"));
+ }
+ let mut puzzle_data = vec![width, height];
+ puzzle_data.extend(url.as_bytes());
+ let mut id;
+ loop {
+ id = generate_puzzle_id();
+ let data = std::mem::take(&mut puzzle_data);
+ if database.puzzles.compare_and_swap(id, None::<&'static [u8; 0]>, Some(&data[..]))?.is_ok() {
+ break;
+ }
+ }
+ drop(puzzle_data); // should be empty now
+ puzzle_id = Some(id);
+ let pieces_data: Vec<u8>;
+ {
+ let mut rng = rand::thread_rng();
+ pieces_data = (0..(width as u16) * (height as u16) * 4).map(|_| rng.gen()).collect();
+ }
+ database.pieces.insert(id, pieces_data)?;
+ ws.send(Message::Text(format!("id: {}", std::str::from_utf8(&id)?))).await?;
+ } else if text == "poll" {
+ let puzzle_id = puzzle_id.ok_or_else(|| anyhow!("poll without puzzle ID"))?;
+ let pieces = database.pieces.get(&puzzle_id)?.ok_or_else(|| anyhow!("bad puzzle ID: {puzzle_id:?}"))?;
+ let pieces = pieces.to_vec();
+ ws.send(Message::Binary(pieces)).await?;
+ }
+ }
}
Ok(())
}
@@ -41,6 +92,16 @@ async fn main() {
tokio::time::sleep(std::time::Duration::from_secs(3600)).await;
}
});
+ static DATABASE_VALUE: LazyLock<Database> = LazyLock::new(|| {
+ let db = sled::open("database.sled").expect("error opening database");
+ let puzzles = db.open_tree("PUZZLES").expect("error opening puzzles tree");
+ let pieces = db.open_tree("PIECES").expect("error opening pieces tree");
+ Database {
+ puzzles,
+ pieces
+ }
+ });
+ let database: &Database = &DATABASE_VALUE;
loop {
let (mut stream, addr) = match listener.accept().await {
Ok(result) => result,
@@ -50,7 +111,7 @@ async fn main() {
}
};
tokio::task::spawn(async move {
- match handle_connection(&mut stream).await {
+ match handle_connection(database, &mut stream).await {
Ok(()) => {}
Err(e) => {
eprintln!("Error handling connection to {addr}: {e}");