summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorstale <redkugelblitzin@gmail.com>2022-07-02 05:49:10 -0300
committerstale <redkugelblitzin@gmail.com>2022-07-02 05:49:10 -0300
commit517df189e71626025d33a442cebff592df2d5358 (patch)
treebd24571f39d529fe0b9412346667ba2dd6af0a4e /src
parentcc2eb4ef053da7a53663bdecac912a1c49490301 (diff)
a bunch of stuff iunno, v 1.2
Diffstat (limited to 'src')
-rw-r--r--src/conn.rs38
-rw-r--r--src/main.rs34
-rw-r--r--src/minesweeper.rs27
-rw-r--r--src/types.rs5
4 files changed, 60 insertions, 44 deletions
diff --git a/src/conn.rs b/src/conn.rs
index 7adeeae..047d7db 100644
--- a/src/conn.rs
+++ b/src/conn.rs
@@ -4,19 +4,15 @@ use std::{
net::SocketAddr,
};
use tokio::sync::RwLock;
-use tokio::sync::mpsc as tokio_mpsc;
-use futures::{SinkExt, StreamExt, TryStreamExt, stream::SplitStream};
+use futures::{SinkExt, TryStreamExt, StreamExt, stream::SplitStream};
use warp::ws::{ WebSocket, Message };
use crate::livepos;
-const MAX_IN: usize = 2048;
-
-pub async fn lobby(socket: WebSocket, addr: SocketAddr, rinfo: (RoomId,Arc<RwLock<Room>>)) {
+pub async fn setup_conn(socket: WebSocket, addr: SocketAddr, rinfo: (RoomId,Arc<RwLock<Room>>), max_in: usize) {
let (room_id, room) = rinfo;
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
-
- // server <-> client comms
let (mut outgoing, incoming) = socket.split();
+ let conn = Conn { addr, tx };
println!("{room_id} I: Incoming TCP connection from: {}", addr);
@@ -24,10 +20,10 @@ pub async fn lobby(socket: WebSocket, addr: SocketAddr, rinfo: (RoomId,Arc<RwLoc
let rl = room.read().await;
let pcap = rl.conf.player_cap;
let pl = rl.players.read().await;
- pl.len() >= pcap
+ pl.len() >= pcap.get()
};
if full { return }
- let drive_game = handle_room((incoming,tx), addr, (room_id.clone(),room.clone()));
+ let drive_game = drive_conn((conn, incoming), (room_id.clone(),room.clone()), max_in);
let send_to_client = {
let room_id = room_id.clone();
async move {
@@ -61,10 +57,9 @@ pub async fn lobby(socket: WebSocket, addr: SocketAddr, rinfo: (RoomId,Arc<RwLoc
}
}
-type RoomStreams = (SplitStream<WebSocket>,tokio_mpsc::UnboundedSender<Message>);
-pub async fn handle_room(streams: RoomStreams, addr: SocketAddr, rinfo: (RoomId, Arc<RwLock<Room>>)) {
- let (mut incoming, tx) = streams;
+pub async fn drive_conn(conn: (Conn, SplitStream<WebSocket>), rinfo: (RoomId, Arc<RwLock<Room>>), max_in: usize) {
+ let (conn, mut incoming) = conn;
let (room_id, room) = rinfo;
let (players, cmd_tx, pos_tx, room_conf) = {
let room = room.read().await;
@@ -74,7 +69,7 @@ pub async fn handle_room(streams: RoomStreams, addr: SocketAddr, rinfo: (RoomId,
if let Some(cmd) = cmd {
// if it ain't text we can't handle it
let cmd = match cmd.to_str() {
- Ok(cmd) => { if cmd.len() > MAX_IN {
+ Ok(cmd) => { if cmd.len() > max_in {
println!("{room_id} E: string too big: {cmd}");
return
} else { cmd.to_owned() } },
@@ -90,7 +85,7 @@ pub async fn handle_room(streams: RoomStreams, addr: SocketAddr, rinfo: (RoomId,
if let Some(cmd_name) = fields.next() {
use crate::minesweeper::{Move,MoveType};
let mut players_lock = players.write().await;
- match players_lock.get_mut(&addr) {
+ match players_lock.get_mut(&conn.addr) {
Some(me) => match cmd_name {
"pos" => {
if let Some(pos) = parse_pos(fields) {
@@ -102,7 +97,7 @@ pub async fn handle_room(streams: RoomStreams, addr: SocketAddr, rinfo: (RoomId,
"reveal" => {
match parse_pos(fields) {
Some(pos) => {
- if let Err(e) = cmd_tx.send(MetaMove::Move(Move { t: MoveType::Reveal, pos }, addr)) {
+ if let Err(e) = cmd_tx.send(MetaMove::Move(Move { t: MoveType::Reveal, pos }, conn.addr)) {
println!("{room_id} E: couldn't process {me}'s reveal command: {e}");
};
},
@@ -114,7 +109,7 @@ pub async fn handle_room(streams: RoomStreams, addr: SocketAddr, rinfo: (RoomId,
"flag" => {
match parse_pos(fields) {
Some(pos) => {
- if let Err(e) = cmd_tx.send(MetaMove::Move(Move { t: MoveType::ToggleFlag, pos }, addr)) {
+ if let Err(e) = cmd_tx.send(MetaMove::Move(Move { t: MoveType::ToggleFlag, pos }, conn.addr)) {
println!("{room_id} E: couldn't process {me}'s flag command: {e}");
};
},
@@ -142,16 +137,15 @@ pub async fn handle_room(streams: RoomStreams, addr: SocketAddr, rinfo: (RoomId,
if n.is_empty() { def } else { n }
}
};
- println!("{room_id} I: registered \"{name}@{addr}\"");
+ println!("{room_id} I: registered \"{name}@{}\"", conn.addr);
drop(players_lock);
let uid = {
// new scope cuz paranoid bout deadlocks
- let conn = Conn { addr, tx: tx.clone() };
- room.write().await.players.write().await.insert_conn(conn, name.clone(), clr)
+ room.write().await.players.write().await.insert_conn(conn.clone(), name.clone(), clr)
};
let players_lock = players.read().await;
- let me = players_lock.get(&addr).unwrap();
- tx.send(Message::text(format!("regack {} {} {} {}",
+ let me = players_lock.get(&conn.addr).unwrap();
+ conn.tx.send(Message::text(format!("regack {} {} {} {}",
room_conf.name.replace(' ', "&nbsp;"), name.replace(' ', "&nbsp;"), uid, room_conf.board_conf))
).expect("couldn't send register ack");
@@ -176,7 +170,7 @@ pub async fn handle_room(streams: RoomStreams, addr: SocketAddr, rinfo: (RoomId,
}
}
} else {
- println!("{room_id} E: reached end of stream for {addr}");
+ println!("{room_id} E: reached end of stream for {}", conn.addr);
break;
}
}
diff --git a/src/main.rs b/src/main.rs
index eec9cf8..2321289 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -35,6 +35,7 @@ struct ConfLimits {
pub board_area: usize,
pub room_slots: usize,
pub form_size: u64,
+ pub inbound_packet_size: usize,
}
#[derive(Deserialize)]
struct Conf {
@@ -106,7 +107,6 @@ async fn tokio_main(conf: Conf) -> Result<(), Box<dyn Error>> {
post().and(path("r")).and(body::content_length_limit(conf.limits.form_size)).and(body::form())
.and_then(move |rinfo: HashMap<String, String>| {
- println!("{:?}", rinfo);
let rooms = rooms.clone();
let pubs = pubs.clone();
let conf = conf.clone();
@@ -131,6 +131,7 @@ async fn tokio_main(conf: Conf) -> Result<(), Box<dyn Error>> {
if access {
pubs.write().await.insert(uid.clone(), serde_json::to_string(&room.conf).unwrap());
}
+ println!("New room: {:?}", room.conf);
rooms.insert(uid.clone(), Arc::new(RwLock::new(room)));
Ok(
@@ -150,6 +151,8 @@ async fn tokio_main(conf: Conf) -> Result<(), Box<dyn Error>> {
let rooms_ws = rooms.clone();
let rooms_lobby = rooms.clone();
let prefix = get().and(path!("room" / String / ..));
+ let max_inbound_packet_size = conf.limits.inbound_packet_size;
+ let room_path = conf.paths.room_page.clone();
// Fixme: better errors
prefix.and(path!("ws"))
@@ -163,7 +166,7 @@ async fn tokio_main(conf: Conf) -> Result<(), Box<dyn Error>> {
Some(r) => {
println!("{id} I: conn from {saddr:?}");
Ok(websocket.on_upgrade(move |socket| {
- conn::lobby(socket, saddr.expect("socket without address"), (id,r))
+ conn::setup_conn(socket, saddr.expect("socket without address"), (id,r), max_inbound_packet_size)
}))
},
None => {
@@ -174,7 +177,7 @@ async fn tokio_main(conf: Conf) -> Result<(), Box<dyn Error>> {
}
})
.or(prefix.and(path::end())
- .and(fs::file(conf.paths.room_page.clone()))
+ .and(fs::file(room_path))
.then(move |id: String, f: fs::File| {
let rooms = rooms_lobby.clone();
async move {
@@ -291,19 +294,20 @@ async fn empty_rooms(rooms: &RoomMap) -> Vec<RoomId> {
}
fn room_from_form(uid: RoomId, rinfo: &HashMap<String,String>, conf: &Conf) -> Result<(types::Room, bool), Rejection> {
- if let (Some(w),Some(h),Some(num),Some(denom),access,asfm,limit) = (
- rinfo.get("rwidth").and_then(|wt| wt.parse::<NonZeroUsize>().ok()),
- rinfo.get("rheight").and_then(|ht| ht.parse::<NonZeroUsize>().ok()),
- rinfo.get("rration").and_then(|nt| nt.parse::<usize>().ok()),
- rinfo.get("rratiod").and_then(|dt| dt.parse::<NonZeroUsize>().ok()),
- rinfo.get("raccess"),
- rinfo.get("ralwayssafe1move"),
- rinfo.get("rlimit").and_then(|l| l.parse::<usize>().ok()),
+ if let (Some(w),Some(h),Some(num),Some(denom),public,asfm,rborders,Some(limit)) = (
+ rinfo.get("bwidth").and_then(|w| w.parse::<NonZeroUsize>().ok()),
+ rinfo.get("bheight").and_then(|h| h.parse::<NonZeroUsize>().ok()),
+ rinfo.get("mineratio-n").and_then(|n| n.parse::<usize>().ok()),
+ rinfo.get("mineratio-d").and_then(|d| d.parse::<NonZeroUsize>().ok()),
+ rinfo.get("public").map(|s| s == "on").unwrap_or(false),
+ rinfo.get("rborders").map(|s| s == "on").unwrap_or(false),
+ rinfo.get("allsafe1move").map(|s| s == "on").unwrap_or(false),
+ rinfo.get("limit").and_then(|l| l.parse::<NonZeroUsize>().ok()),
) {
if w.get()*h.get() > conf.limits.board_area {
return Err(warp::reject::custom(BoardTooBig))
}
- let board_conf = minesweeper::BoardConf { w, h, mine_ratio: (num,denom), always_safe_first_move: asfm.is_some() };
+ let board_conf = minesweeper::BoardConf { w, h, mine_ratio: (num,denom), always_safe_first_move: asfm, revealed_borders: rborders };
let name = {
let n = rinfo.get("rname").unwrap().to_owned();
if n.is_empty() { uid.to_string() } else { n }
@@ -319,8 +323,8 @@ fn room_from_form(uid: RoomId, rinfo: &HashMap<String,String>, conf: &Conf) -> R
let room_conf = RoomConf {
name,
- player_cap: match limit { Some(i) => i, None => usize::MAX },
- public: access.is_some(),
+ player_cap: limit,
+ public,
board_conf,
};
Ok((Room {
@@ -330,7 +334,7 @@ fn room_from_form(uid: RoomId, rinfo: &HashMap<String,String>, conf: &Conf) -> R
cmd_stream: cmd_tx,
livepos_driver: livepos_handle,
pos_stream: pos_tx,
- }, access.is_some()))
+ }, public))
} else { Err(warp::reject::custom(BadFormData)) }
}
diff --git a/src/minesweeper.rs b/src/minesweeper.rs
index 9e362dc..616a6a0 100644
--- a/src/minesweeper.rs
+++ b/src/minesweeper.rs
@@ -38,6 +38,7 @@ pub struct BoardConf {
/// mines/tiles, expressed as (numerator, denominator)
pub mine_ratio: (usize,NonZeroUsize),
pub always_safe_first_move: bool,
+ pub revealed_borders: bool,
}
impl std::fmt::Display for BoardConf {
@@ -100,6 +101,7 @@ impl Game {
if self.phase == Phase::FirstMoveFail {
let winnable = self.board.mine_count < (self.board.width.get() * self.board.height.get());
if winnable {
+ self.board.hidden_tiles += 1;
self.board.move_mine_elsewhere(m.pos);
self.phase = Phase::Run;
self = self.act(m);
@@ -113,10 +115,12 @@ impl Game {
}
}
impl Board {
- pub fn new(conf: BoardConf) -> Self {
+ pub fn new(mut conf: BoardConf) -> Self {
let (w,h) = (conf.w,conf.h);
let area = w.get()*h.get();
- let mine_count = ((conf.mine_ratio.0 * area) / conf.mine_ratio.1.get()).clamp(0, area);
+ if w.get() < 3 || h.get() < 3 { conf.revealed_borders = false; }
+ let mined_area = area - if conf.revealed_borders { 2*(w.get()-1) + 2*(h.get()-1) } else { 0 };
+ let mine_count = ((conf.mine_ratio.0 * mined_area) / conf.mine_ratio.1.get()).clamp(0, mined_area);
let b = Board {
data: [HIDDEN_BIT].repeat(area),
width: w,
@@ -124,14 +128,27 @@ impl Board {
hidden_tiles: area,
mine_count,
};
- b.spread_mines(mine_count)
+ if conf.revealed_borders {
+ let (w,h) = (w.get(),h.get());
+ let mut b = b.spread_mines(mine_count, true);
+ for x in 0..w {
+ b = b.reveal((x, 0)).0;
+ b = b.reveal((x, h-1)).0;
+ }
+ for y in 1..h-1 {
+ b = b.reveal(( 0, y)).0;
+ b = b.reveal((w-1, y)).0;
+ }
+ b
+ } else { b.spread_mines(mine_count, false) }
}
- pub fn spread_mines(mut self, mut count: usize) -> Self {
+ pub fn spread_mines(mut self, mut count: usize, without_edges: bool) -> Self {
let mut rng = thread_rng();
let w = self.width.get();
let h = self.height.get();
+ let (wr,hr) = if without_edges { ((1,w-1),(1,h-1)) } else { ((0,w),(0,h)) };
while count > 0 {
- let randpos: (usize, usize) = (rng.sample(Uniform::new(0,w)), rng.sample(Uniform::new(0,h)));
+ let randpos: (usize, usize) = (rng.sample(Uniform::new(wr.0, wr.1)), rng.sample(Uniform::new(hr.0, hr.1)));
let o = self.pos_to_off_unchecked(randpos);
if self.data[o] == MINED { continue }
else {
diff --git a/src/types.rs b/src/types.rs
index 0bd7881..002b5d7 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -7,6 +7,7 @@ use std::{
},
fmt::Display,
ops::{ Deref, DerefMut },
+ num::NonZeroUsize,
};
use warp::ws::Message;
use tokio::sync::RwLock;
@@ -17,7 +18,7 @@ use crate::livepos;
#[derive(Debug, Serialize, Clone)]
pub struct RoomConf {
pub name: String,
- pub player_cap: usize,
+ pub player_cap: NonZeroUsize,
pub public: bool,
pub board_conf: minesweeper::BoardConf,
}
@@ -38,7 +39,7 @@ pub enum MetaMove {
Reset,
}
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct Conn {
pub tx: tokio::sync::mpsc::UnboundedSender<Message>,
pub addr: SocketAddr,