summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorstale <redkugelblitzin@gmail.com>2022-05-03 05:39:13 -0300
committerstale <redkugelblitzin@gmail.com>2022-05-03 05:39:13 -0300
commit2f9687126ecb538f40b57bd6963129b406786175 (patch)
treeff79d253da529b1535540b84c1ccfc874e6b6cbc /src
parentc9272f5580b00180738eb6dde623ea0abb37f7af (diff)
lazy commit, buncha stuff
Diffstat (limited to 'src')
-rw-r--r--src/main.rs175
-rw-r--r--src/minesweeper.rs18
2 files changed, 127 insertions, 66 deletions
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(" ", "&nbsp").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"&nbsp"),
- _ 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