diff options
author | pommicket <pommicket@gmail.com> | 2024-08-07 23:23:45 -0400 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2024-08-07 23:23:45 -0400 |
commit | 29f0d5e52212bffc3e9508f32225436e47b86071 (patch) | |
tree | 3349065c4142b0c0cbf5eec61d621e627c86fc9f /server | |
parent | bddf8ce87d175a9e2688d4ddda4424711d65388d (diff) |
Terrible synchronization
Diffstat (limited to 'server')
-rw-r--r-- | server/src/main.rs | 82 |
1 files changed, 77 insertions, 5 deletions
diff --git a/server/src/main.rs b/server/src/main.rs index 81f2581..1fac1e5 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -19,6 +19,20 @@ struct Database { pieces: sled::Tree, } +fn get_puzzle_info(database: &Database, id: &[u8]) -> anyhow::Result<Vec<u8>> { + if id.len() != PUZZLE_ID_LEN { return Err(anyhow!("bad puzzle ID")); } + let mut data = vec![1, 0, 0, 0, 0, 0, 0, 0]; // opcode + padding + let puzzle = database.puzzles.get(id)?.ok_or_else(|| anyhow!("bad puzzle ID"))?; + data.extend_from_slice(&puzzle); + while data.len() % 8 != 0 { + // padding + data.push(0); + } + let pieces = database.pieces.get(id)?.ok_or_else(|| anyhow!("bad puzzle ID"))?; + data.extend_from_slice(&pieces); + Ok(data) +} + async fn handle_connection(database: &Database, conn: &mut tokio::net::TcpStream) -> anyhow::Result<()> { let mut ws = tokio_tungstenite::accept_async_with_config( conn, @@ -42,11 +56,25 @@ async fn handle_connection(database: &Database, conn: &mut tokio::net::TcpStream 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 url.len() > 255 { + return Err(anyhow!("image URL too long")); + } if (width as u16) * (height as u16) > 1000 { return Err(anyhow!("too many pieces")); } let mut puzzle_data = vec![width, height]; + // pick nib types + { + let mut rng = rand::thread_rng(); + for _ in 0..2u16 * (width as u16) * (height as u16) - (width as u16) - (height as u16) { + puzzle_data.push(rng.gen()); + puzzle_data.push(rng.gen()); + } + } + // URL puzzle_data.extend(url.as_bytes()); + puzzle_data.push(0); + // puzzle ID let mut id; loop { id = generate_puzzle_id(); @@ -57,18 +85,62 @@ async fn handle_connection(database: &Database, conn: &mut tokio::net::TcpStream } drop(puzzle_data); // should be empty now puzzle_id = Some(id); - let pieces_data: Vec<u8>; + let mut pieces_data: Vec<u8>; { let mut rng = rand::thread_rng(); - pieces_data = (0..(width as u16) * (height as u16) * 4).map(|_| rng.gen()).collect(); + pieces_data = Vec::new(); + pieces_data.resize((width as usize) * (height as usize) * 10, 0); + // positions + let mut it = pieces_data.iter_mut(); + for _ in 0..(width as u16) * (height as u16) * 2 { + let coord: f32 = rng.gen(); + let [a, b, c, d] = coord.to_le_bytes(); + *it.next().unwrap() = a; + *it.next().unwrap() = b; + *it.next().unwrap() = c; + *it.next().unwrap() = d; + } + // connectivity + for i in 0..(width as u16) * (height as u16) { + let [a, b] = i.to_le_bytes(); + *it.next().unwrap() = a; + *it.next().unwrap() = b; + } } database.pieces.insert(id, pieces_data)?; ws.send(Message::Text(format!("id: {}", std::str::from_utf8(&id)?))).await?; + let info = get_puzzle_info(&database, &id)?; + ws.send(Message::Binary(info)).await?; + } else if let Some(id) = text.strip_prefix("join ") { + let id = id.as_bytes().try_into()?; + puzzle_id = Some(id); + let info = get_puzzle_info(&database, &id)?; + ws.send(Message::Binary(info)).await?; + } else if let Some(data) = text.strip_prefix("move ") { + let mut parts = data.split(' '); + let puzzle_id = puzzle_id.ok_or_else(|| anyhow!("move without puzzle ID"))?; + let piece: usize = parts.next().ok_or_else(|| anyhow!("bad syntax"))?.parse()?; + let x: f32 = parts.next().ok_or_else(|| anyhow!("bad syntax"))?.parse()?; + let y: f32 = parts.next().ok_or_else(|| anyhow!("bad syntax"))?.parse()?; + loop { + let curr_pieces = database.pieces.get(&puzzle_id)? + .ok_or_else(|| anyhow!("bad puzzle ID"))?; + let mut new_pieces = curr_pieces.to_vec(); + new_pieces.get_mut(8 * piece..8 * piece + 4).ok_or_else(|| anyhow!("bad piece ID"))? + .copy_from_slice(&x.to_le_bytes()); + new_pieces.get_mut(8 * piece + 4..8 * piece + 8).ok_or_else(|| anyhow!("bad piece ID"))? + .copy_from_slice(&y.to_le_bytes()); + if database.pieces.compare_and_swap(&puzzle_id, Some(curr_pieces), Some(new_pieces))?.is_ok() { + break; + } + tokio::time::sleep(std::time::Duration::from_millis(10)).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?; + let pieces = database.pieces.get(&puzzle_id)?.ok_or_else(|| anyhow!("bad puzzle ID"))?; + let mut data = vec![2, 0, 0, 0, 0, 0, 0, 0]; // opcode / version number + padding + data.extend_from_slice(&pieces); + ws.send(Message::Binary(data)).await?; } } } |