diff options
author | stale <redkugelblitzin@gmail.com> | 2022-05-03 05:39:13 -0300 |
---|---|---|
committer | stale <redkugelblitzin@gmail.com> | 2022-05-03 05:39:13 -0300 |
commit | 2f9687126ecb538f40b57bd6963129b406786175 (patch) | |
tree | ff79d253da529b1535540b84c1ccfc874e6b6cbc | |
parent | c9272f5580b00180738eb6dde623ea0abb37f7af (diff) |
lazy commit, buncha stuff
-rw-r--r-- | Cargo.lock | 64 | ||||
-rwxr-xr-x | deploy.sh | 4 | ||||
-rw-r--r-- | page.html | 55 | ||||
-rw-r--r-- | src/main.rs | 175 | ||||
-rw-r--r-- | src/minesweeper.rs | 18 |
5 files changed, 197 insertions, 119 deletions
@@ -210,9 +210,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" +checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb" dependencies = [ "bytes", "fnv", @@ -301,9 +301,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.124" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50" +checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" [[package]] name = "lock_api" @@ -317,9 +317,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] @@ -332,9 +332,9 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mio" @@ -396,9 +396,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ "cfg-if", "libc", @@ -538,9 +538,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.91" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52" dependencies = [ "proc-macro2", "quote", @@ -549,18 +549,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ "proc-macro2", "quote", @@ -584,9 +584,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.17.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" +checksum = "dce653fb475565de9f6fb0614b28bca8df2c430c0cf84bcd9c843f15de5414cc" dependencies = [ "bytes", "libc", @@ -725,9 +725,9 @@ dependencies = [ [[package]] name = "unicode-xid" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" [[package]] name = "url" @@ -812,9 +812,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ "windows_aarch64_msvc", "windows_i686_gnu", @@ -825,30 +825,30 @@ dependencies = [ [[package]] name = "windows_aarch64_msvc" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] name = "windows_i686_gnu" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] name = "windows_i686_msvc" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] name = "windows_x86_64_gnu" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] name = "windows_x86_64_msvc" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" @@ -2,5 +2,5 @@ cargo build --release --target x86_64-unknown-linux-musl && \ strip target/x86_64-unknown-linux-musl/release/websweeper && \ - rsync --progress page.html root@masba.net:/srv/www/tooling/ - rsync --progress target/x86_64-unknown-linux-musl/release/websweeper root@masba.net:/srv/www/tooling/ + rsync --progress page.html root@masba.net:/srv/tooling/ + rsync --progress target/x86_64-unknown-linux-musl/release/websweeper root@masba.net:/srv/tooling/ @@ -43,10 +43,9 @@ <p id="miscinfo">Loading...</p> </div> </body> - <script src="https://masba.net/ansispan.js"></script> <script> window.id = NaN; - let s = new WebSocket("ws://127.0.0.1:31236"); + let s = new WebSocket(`ws://${window.location.hostname}:31236`); let info_elem = document.getElementById("miscinfo"); let board_elem = document.getElementById("board"); let name = "deadbeef"; @@ -58,7 +57,6 @@ let name_in = document.getElementById("name-in"); name = name_in.value; s.send(`register ${name}`); - info_elem.innerHTML = "Running"; } s.onopen = function(e) { info_elem.innerHTML = @@ -70,6 +68,8 @@ let d = e.data; if (typeof d == "object") { d.arrayBuffer().then(acceptBoard); + info_elem.onclick = undefined; + info_elem.innerHTML = "Running"; } else if (typeof e.data == "string") { let fields = d.split(" "); if (d.startsWith("pos")) { @@ -90,13 +90,17 @@ } else if (d.startsWith("win")) { info_elem.innerHTML = "<p>You win! Reset?</p>"; - info_elem.onclick = e => { s.send("reset"); info_elem.onclick = undefined; - info_elem.innerHTML = "Running"; }; + info_elem.onclick = e => { s.send("reset") }; } else if (d.startsWith("lose")) { - info_elem.innerHTML = "<p>You lose... Reset?</p>"; - info_elem.onclick = e => { s.send("reset"); info_elem.onclick = undefined; - info_elem.innerHTML = "Running"; }; + let badone = fields[1]; + info_elem.innerHTML = `<p>You lost, ${badone} was blown up. Reset?</p>`; + info_elem.onclick = e => { s.send("reset") }; + } + else if (d.startsWith("logoff")) { + let oid = fields[1]; + cursors.get(oid).elem.remove(); + cursors.delete(oid); } } } @@ -105,18 +109,31 @@ function acceptBoard(data) { let vals = new Uint8Array(data); - if (vals[0] == 27) { - // starts with escape char, is a board dump - board = Array.from(vals.subarray(1,vals.length).values()).reduce((s,c) => s + - String.fromCodePoint(c), ""); - board_elem.innerHTML = "<span>" + ansispan(board) + "</span>"; - } - else if (vals[0] == 'p'.charCodeAt(0)) { - // starts with a p, is a cursor position update - // unimplemented! + board = Array.from(vals).reduce((s,c) => s + String.fromCodePoint(c), ""); + let last = board[0]; + let last_idx = 0; + let split_board = []; + for (let i = 1; i < board.length+1; i++) { + let cur = board[i]; + let gamechars = /^[CFO# 1-8]+$/; + if ((cur != last && gamechars.test(cur)) || cur == undefined) { + let txt = board.substr(last_idx, i-last_idx); + if (txt[0] == 'O') { + txt = `<span style="color:red;">${txt}</span>`; + } else if (txt[0] == 'C') { + txt = `<span style="color:green;">${txt}</span>`; + } else if (txt[0] == 'F') { + txt = `<span style="color:yellow;">${txt}</span>`; + } else { + txt = `<span style="color:white;">${txt}</span>`; } - - } + split_board.push(txt); + last_idx = i; + } + last = board[i]; + } + board_elem.innerHTML = "<span>" + split_board.join("") + "</span>"; + } function createCursor(id, name) { // shit doesn't line up diff --git a/src/main.rs b/src/main.rs index 8f556c3..d42ad9f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,7 +32,7 @@ type PeerInfo = (PeerMap, Arc::<AtomicUsize>); #[derive(Debug)] enum MetaMove { - Move(Move), + Move(Move,SocketAddr), Dump, Reset, } @@ -77,26 +77,33 @@ async fn main() { // If a move is made, broadcast new board, else just send current board async fn gameloop(mut move_rx: mpsc::UnboundedReceiver<MetaMove>, peers: PeerMap) { let mut game = Game::new(Board::new(75,35), (75*35)/8); + let mut latest_player_name = None; while let Some(req) = move_rx.recv().await { - let mut done = game.phase == Phase::Die || game.phase == Phase::Win; - match req { - MetaMove::Move(m) => if !done { game = game.act(m)}, - MetaMove::Dump => (), - MetaMove::Reset => { game = Game::new(Board::new(75,35), (75*35)/8); done = false;}, - } - if !done { - let mut reply = vec![]; - match game.phase { - Phase::Win => { reply.push(Message::text("win")); game.board = game.board.grade(); }, - Phase::Die => { reply.push(Message::text("lose")); game.board = game.board.grade(); }, - _ => (), - } - reply.push(Message::binary(game.board.render())); - { - let peers = peers.read().await; - for (_, (tx, _, _, _)) in peers.iter() { - for r in reply.iter() { - tx.unbounded_send(r.clone()).unwrap(); + let done = game.phase == Phase::Die || game.phase == Phase::Win; + match req { + MetaMove::Move(m, o) => if !done { + game = game.act(m); + if game.phase == Phase::Win || game.phase == Phase::Die { + game.board = game.board.grade(); + } + latest_player_name = peers.read().await.get(&o).map(|(_,_,n,_)| n.clone()); + }, + MetaMove::Dump => (), + MetaMove::Reset => { game = Game::new(Board::new(75,35), (75*35)/8); }, + } + let mut reply = vec![Message::binary(game.board.render())]; + let lpname = latest_player_name.as_ref().map(|s| s.as_str()).unwrap_or("unknown player"); + match game.phase { + Phase::Win => { reply.push(Message::text(format!("win {lpname}"))); }, + Phase::Die => { reply.push(Message::text(format!("lose {lpname}"))); }, + _ => (), + } + { + let peers = peers.read().await; + for (addr, (tx, _, _, _)) in peers.iter() { + for r in reply.iter() { + if let Err(e) = tx.unbounded_send(r.clone()) { + println!("couldn't send game update {r} to {addr}: {e}"); } } } @@ -125,49 +132,111 @@ async fn peer_connection(peer_info: PeerInfo, cmd_tx: MovReqTx, raw_stream: TcpS let peer_map = peer_info.0; let peer_seqid = peer_info.1.fetch_add(1, atomic::Ordering::AcqRel); + let mut peer_name = "unknown".to_string(); - // Insert the write part of this peer to the peer map. let (tx, rx) = unbounded(); let (outgoing, mut incoming) = ws_stream.split(); let process_incoming = async { - while let Some(cmd) = incoming.try_next().await.unwrap() { - let cmd = cmd.to_text().unwrap(); - - let mut fields = cmd.split(" ").skip(1); - if cmd.starts_with("pos") { - let pos = (fields.next().unwrap().parse::<usize>().unwrap(), fields.next().unwrap().parse::<usize>().unwrap()); - let (name, id) = { + while let Ok(cmd) = incoming.try_next().await { + if let Some(cmd) = cmd { + if cmd.is_close() { + println!("closing \"{peer_name}\"@{addr}"); let mut peers = peer_map.write().await; - let mut entry = peers.get_mut(&addr).unwrap(); - entry.3 = pos.clone(); - (entry.2.clone(), entry.1) - }; - { - let peers = peer_map.read().await; - for peer_tx in peers.iter().filter(|(s, _)| **s != addr).map(|(_,(peer_tx,_,_,_))| peer_tx) { - peer_tx.unbounded_send(Message::text(format!("pos {} {} {} {}", id, name, pos.0, pos.1))).unwrap(); + if let Some(_) = peers.get(&addr) { + peers.remove(&addr); + } + for (paddr, (tx, _, pname, _)) in peers.iter() { + if let Err(e) = tx.unbounded_send(Message::text("logoff {peer_seqid} {peer_name}")) { + println!("couldn't deliver logoff info to \"{pname}\"@{paddr}: {e}"); + } } + break; } - } else if cmd.starts_with("reveal") { - println!("got {} from {}", cmd, addr); - let pos = (fields.next().unwrap().parse::<usize>().unwrap(), fields.next().unwrap().parse::<usize>().unwrap()); - cmd_tx.send(MetaMove::Move(Move { t: MoveType::Reveal, pos })).unwrap(); - } else if cmd.starts_with("flag") { - println!("got {} from {}", cmd, addr); - let pos = (fields.next().unwrap().parse::<usize>().unwrap(), fields.next().unwrap().parse::<usize>().unwrap()); - cmd_tx.send(MetaMove::Move(Move { t: MoveType::ToggleFlag, pos })).unwrap(); - } else if cmd.starts_with("reset") { - println!("got {} from {}", cmd, addr); - cmd_tx.send(MetaMove::Reset).unwrap(); - } else if cmd.starts_with("register") { - let name = fields.next().unwrap(); - { // new scope cuz paranoid bout deadlocks - peer_map.write().await.insert(addr, (tx.clone(), peer_seqid, name.to_string(), (0,0))); + // if it ain't text we can't handle it + if !cmd.is_text() { continue; } + let cmd = cmd.to_text().unwrap(); + + let mut fields = cmd.split(" "); + let parse_pos = |mut fields: std::str::Split<&str>| -> Option<(usize, usize)> { + let x = fields.next().and_then(|xstr| xstr.parse::<usize>().ok()); + let y = fields.next().and_then(|ystr| ystr.parse::<usize>().ok()); + x.zip(y) + }; + if let Some(cmd_name) = fields.next() { + match cmd_name { + "pos" => { + match parse_pos(fields) { + Some(pos) => { + let (name, id) = { + let mut peers = peer_map.write().await; + let mut entry = peers.get_mut(&addr).unwrap(); + entry.3 = pos.clone(); + (entry.2.clone(), entry.1) + }; + let sanitized_name = name.replace(" ", " ").to_string(); + { + let peers = peer_map.read().await; + for peer_tx in peers.iter().filter(|(s, _)| **s != addr).map(|(_,(peer_tx,_,_,_))| peer_tx) { + let r = peer_tx.unbounded_send(Message::text(format!("pos {id} {sanitized_name} {} {}", pos.0, pos.1))); + if let Err(e) = r { + println!("error sending pos update: {e}"); + } + } + } + }, + None => { + println!("bad position update from \"{peer_name}@{addr}\""); + }, + } + }, + "reveal" => { + match parse_pos(fields) { + Some(pos) => { + println!("{cmd} from \"{peer_name}\"@{addr}"); + cmd_tx.send(MetaMove::Move(Move { t: MoveType::Reveal, pos }, addr)).unwrap(); + }, + None => { + println!("bad reveal from \"{peer_name}\"@{addr}"); + } + } + }, + "flag" => { + match parse_pos(fields) { + Some(pos) => { + println!("{cmd} from \"{peer_name}\"@{addr}"); + cmd_tx.send(MetaMove::Move(Move { t: MoveType::ToggleFlag, pos }, addr)).unwrap(); + }, + None => { + println!("bad flag from \"{peer_name}\"@{addr}"); + } + } + }, + "reset" => { + println!("{cmd} from \"{peer_name}\"@{addr}"); + if let Err(e) = cmd_tx.send(MetaMove::Reset) { + println!("couldn't send game dump to \"{peer_name}\"@{addr}: {e}"); + } + }, + "register" => { + let name = fields.collect::<Vec<&str>>().join(&" "); + if name.is_empty() { + peer_name = "anon".to_string(); + } else { + peer_name = name; + } + { // new scope cuz paranoid bout deadlocks + peer_map.write().await.insert(addr, (tx.clone(), peer_seqid, peer_name.clone(), (0,0))); + } + tx.unbounded_send(Message::text(format!("id {}", peer_seqid))).unwrap(); + if let Err(e) = cmd_tx.send(MetaMove::Dump) { + println!("couldn't send game dump to \"{peer_name}\"@{addr}: {e}"); + } + }, + e => println!("unknown command {e:?} from {peer_name}@{addr}, \"{cmd}\""), + } } - tx.unbounded_send(Message::text(format!("id {}", peer_seqid))).unwrap(); - cmd_tx.send(MetaMove::Dump).unwrap(); } } }; diff --git a/src/minesweeper.rs b/src/minesweeper.rs index a56758b..8a0ee8d 100644 --- a/src/minesweeper.rs +++ b/src/minesweeper.rs @@ -172,28 +172,20 @@ impl Board { } pub fn render(&self) -> Vec<u8> { - const CYAN: &[u8] = &[27,b'[',b'3',b'6',b'm']; - const YELLOW: &[u8] = &[27,b'[',b'3',b'3',b'm']; - const GREEN: &[u8] = &[27,b'[',b'3',b'2',b'm']; - const RED: &[u8] = &[27,b'[',b'3',b'1',b'm']; - const FG: &[u8] = &[27,b'[',b'3',b'9',b'm']; - let mut ret = vec![27]; - let mut cur_clr = FG; for y in 0..self.height { for x in 0..self.width { let c = &self.data[self.pos_to_off((x,y))]; match *c { 0 => ret.extend_from_slice(b" "), - _ if *c <= 8 => { if cur_clr != FG { ret.extend_from_slice(FG); cur_clr = FG; } ret.push(b'0' + c); }, - _ if (*c & CORRECT_BIT) > 0 => { if cur_clr != GREEN { ret.extend_from_slice(GREEN); cur_clr = GREEN; } ret.push(b'F') }, - _ if (*c & FLAGGED_BIT) > 0 => { if cur_clr != YELLOW { ret.extend_from_slice(YELLOW); cur_clr = YELLOW; } ret.push(b'F'); }, - _ if (*c & HIDDEN_BIT) > 0 => { if cur_clr != CYAN { ret.extend_from_slice(CYAN); cur_clr = CYAN; } ret.push(35); }, - _ if *c == MINE_VAL => { if cur_clr != RED { ret.extend_from_slice(RED); cur_clr = RED; } ret.push(b'O'); }, + _ if *c <= 8 => ret.push(b'0' + c), + _ if (*c & CORRECT_BIT) > 0 => ret.push(b'C'), + _ if (*c & FLAGGED_BIT) > 0 => ret.push(b'F'), + _ if (*c & HIDDEN_BIT) > 0 => ret.push(b'#'), + _ if *c == MINE_VAL => ret.push(b'O'), _ => ret.push(b'?'), } } - ret.extend_from_slice(FG); cur_clr = FG; ret.extend_from_slice(b"<br>"); } ret |