summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2024-08-07 23:23:45 -0400
committerpommicket <pommicket@gmail.com>2024-08-07 23:23:45 -0400
commit29f0d5e52212bffc3e9508f32225436e47b86071 (patch)
tree3349065c4142b0c0cbf5eec61d621e627c86fc9f /server
parentbddf8ce87d175a9e2688d4ddda4424711d65388d (diff)
Terrible synchronization
Diffstat (limited to 'server')
-rw-r--r--server/src/main.rs82
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?;
}
}
}