diff --git a/Cargo.lock b/Cargo.lock index 9f9dc64..ca0e287 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,34 +50,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" -[[package]] -name = "cc" -version = "1.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" - [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - [[package]] name = "cpufeatures" version = "0.2.5" @@ -107,36 +85,12 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "fastrand" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" -dependencies = [ - "instant", -] - [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.1.0" @@ -146,6 +100,95 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" + +[[package]] +name = "futures-executor" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" + +[[package]] +name = "futures-macro" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" + +[[package]] +name = "futures-task" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" + +[[package]] +name = "futures-util" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.6" @@ -167,14 +210,25 @@ dependencies = [ "wasi", ] +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hexland-server" version = "0.1.0" dependencies = [ + "futures", "rand", "regex", "sha2", - "tungstenite", + "tokio", + "tokio-tungstenite", ] [[package]] @@ -204,33 +258,28 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "itoa" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.133" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.17" @@ -247,72 +296,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] -name = "native-tls" -version = "0.2.10" +name = "mio" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" dependencies = [ - "lazy_static", "libc", "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", + "wasi", + "windows-sys", ] [[package]] -name = "once_cell" -version = "1.14.0" +name = "num_cpus" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] [[package]] -name = "openssl" -version = "0.10.41" +name = "parking_lot" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ - "bitflags", "cfg-if", - "foreign-types", "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", + "redox_syscall", + "smallvec", + "windows-sys", ] [[package]] @@ -322,10 +347,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] -name = "pkg-config" -version = "0.3.25" +name = "pin-project-lite" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "ppv-lite86" @@ -408,46 +439,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "scopeguard" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "schannel" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" -dependencies = [ - "lazy_static", - "windows-sys", -] - -[[package]] -name = "security-framework" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" -dependencies = [ - "core-foundation-sys", - "libc", -] +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "sha-1" @@ -471,6 +466,40 @@ dependencies = [ "digest", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "syn" version = "1.0.100" @@ -482,20 +511,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "tempfile" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" -dependencies = [ - "cfg-if", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", -] - [[package]] name = "thiserror" version = "1.0.35" @@ -531,6 +546,49 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +[[package]] +name = "tokio" +version = "1.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + [[package]] name = "tungstenite" version = "0.17.3" @@ -543,7 +601,6 @@ dependencies = [ "http", "httparse", "log", - "native-tls", "rand", "sha-1", "thiserror", @@ -595,12 +652,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index af955a6..0e2a5ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tungstenite = { version = "0.17.3", features = ["native-tls"] } sha2 = "0.10.6" rand = "0.8.5" -regex = "1.6.0" \ No newline at end of file +regex = "1.6.0" +tokio = { version = "1.21.2", features = ["full"] } +tokio-tungstenite = "0.17.2" +futures = "0.3.24" \ No newline at end of file diff --git a/doc/flow.txt b/doc/flow.txt new file mode 100644 index 0000000..347c4e0 --- /dev/null +++ b/doc/flow.txt @@ -0,0 +1,52 @@ +Sign on process: + +New client +client server + HELO +SIGNUP + PLAYER_CODE + +Existing client +client server + HELO +LOGIN + LOGIN_OK + +Expired login +client server + HELO +LOGIN + PLAYER_CODE + +Anything else +client server + HELO +HOST + ERROR + +This is the only scenario where an error is followed by +an immediate close frame. + +Room join process: + +Create new room +client server + HOSTJOIN +HOST + ROOM_CODE + +Join an existing room +client server + HOSTJOIN +JOIN + JOIN_OK + +Join an expired room +client server + HOSTJOIN +JOIN + BAD_ROOM +At this point the client is responsible for procuring +a new room code + + diff --git a/src/main.rs b/src/main.rs index 6447154..f892a57 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,53 +1,72 @@ -use std::net::TcpListener; -use std::thread; -use std::sync::{Arc, Mutex}; -use std::collections::HashMap; +use std::{ + sync::{Arc, Mutex}, + io::Error as IoError, + collections::HashMap, +}; + +use tokio::net::{TcpListener}; mod message; -use message::Message; +use message::{Message, MessageWebSocket}; mod code_generator; use code_generator::CodeGenerator; -mod websocket; -use websocket::WebsocketWrapper; -fn main() { - let code_generator = Arc::new(Mutex::new(CodeGenerator::default())); - let server = TcpListener::bind("127.0.0.1:8080").unwrap(); - - let rooms = Arc::new(Mutex::new(HashMap::new())); +#[tokio::main] +async fn main() -> Result<(), IoError> { + let global_state = Arc::new(GlobalState::default()); - for stream in server.incoming() { - let code_generator = Arc::clone(&code_generator); - let rooms = Arc::clone(&rooms); - thread::spawn (move || { - let ws = tungstenite::accept(stream.unwrap()).unwrap(); - let mut ws = WebsocketWrapper::new(ws); - - ws.send(msg!("HOSTJOIN")); - let msg = ws.recv(); - match msg { - None => (), - Some(msg) => match msg.command.as_str() { - "HOST" => { - let code = code_generator.lock().unwrap().generate(); - let mut room = Room::default(); - let player = Player {}; - room.players.push(player); - rooms.lock().unwrap().insert(code.clone(), room); - ws.send(msg!("CODE", code)); - } - _ => unimplemented!(), - } - } + let socket = TcpListener::bind("127.0.0.1:8080").await; + let listener = socket.expect("Could not bind to localhost:8080"); + println!("Server running"); + while let Ok((stream, addr)) = listener.accept().await { + let global_state = Arc::clone(&global_state); + let mut local_state = LocalState::default(); + tokio::spawn(async move { + // Upgrade to a WS connection + let mut ws = MessageWebSocket(tokio_tungstenite::accept_async(stream) + .await + .expect("Could not establish connection")); + + println!("Connected to {}", addr); + + while let Ok(msg) = ws.next().await { + dispatch(msg, &global_state, &mut local_state, &mut ws).await; + } }); } + Ok(()) +} + +async fn dispatch( + msg: Message, + global_state: &GlobalState, + local_state: &mut LocalState, + ws: &mut MessageWebSocket +) { + match msg.command.as_str() { + "HOST" => { + let room_code = global_state.code_generator.lock().unwrap().generate(); + let game_controller = GameController::default(); + global_state.rooms.lock().unwrap().insert(room_code, game_controller); + } + "JOIN" => { + let room_code = &msg.args[0]; + let room = global_state.rooms.lock().unwrap().get(room_code); + } + _ => unimplemented!(), + } } #[derive(Default)] -struct Room { - players: Vec, +struct GlobalState { + code_generator: Arc>, + rooms: Arc>>, } -struct Player { +#[derive(Default)] +struct GameController {} // TODO + +#[derive(Default)] +struct LocalState { } diff --git a/src/message.rs b/src/message.rs index c95a994..150265c 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,3 +1,12 @@ +use std::convert::{From, TryFrom}; + +use tokio::net::TcpStream; +use tokio_tungstenite::{ + WebSocketStream, + tungstenite::Message as WsMessage, +}; +use futures::{StreamExt, SinkExt}; + #[derive(PartialEq, Debug)] pub struct Message { pub command: String, @@ -10,6 +19,8 @@ pub type Result = std::result::Result; #[derive(PartialEq, Debug)] pub enum Error { BadParse, + NonText, + Unknown, } impl Message { @@ -33,12 +44,49 @@ impl Message { } } +impl TryFrom for Message { + type Error = Error; + + fn try_from(ws_message: WsMessage) -> Result { + let text = match ws_message { + WsMessage::Text(text) => text, + _ => return Err(Error::NonText) + }; + Message::parse(text) + } +} + +impl From for WsMessage { + fn from(message: Message) -> Self { + WsMessage::Text(message.to_string()) + } +} + +pub struct MessageWebSocket(pub WebSocketStream); + +impl MessageWebSocket { + pub async fn next(&mut self) -> Result { + if let Some(Ok(msg)) = self.0.next().await { + msg.try_into() + } else { + Err(Error::Unknown) + } + } + pub async fn send(&mut self, msg: Message) -> Result<()> { + if self.0.send(msg.into()).await.is_err() { + Err(Error::Unknown) + } else { + Ok(()) + } + } +} + #[macro_export] macro_rules! msg { ( $command:expr) => { { let command = $command.to_string(); - let args = Vec::new(); + let args = vec![]; Message { command, args } } }; @@ -62,7 +110,6 @@ impl std::fmt::Display for Message { } } - #[cfg(test)] mod test { use super::*; diff --git a/src/websocket.rs b/src/websocket.rs deleted file mode 100644 index dce8417..0000000 --- a/src/websocket.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::net::TcpStream; - -use tungstenite::protocol::{ WebSocket, Message as WsMessage }; - -use crate::message::Message; - -pub struct WebsocketWrapper { - websocket: WebSocket, -} - -impl WebsocketWrapper { - pub fn new(websocket: WebSocket) -> Self { - WebsocketWrapper { websocket } - } - pub fn send(&mut self, msg: Message) { - self.websocket.write_message(WsMessage::Text(msg.to_string())).unwrap(); - } - pub fn recv(&mut self) -> Option { - match self.websocket.read_message() { - Err(_) | Ok(WsMessage::Close(_)) => None, - Ok(WsMessage::Text(text)) => match Message::parse(text) { - Ok(msg) => Some(msg), - Err(_) => { - self.websocket.write_message(WsMessage::Text("ERROR: bad_format".to_string())).unwrap(); - self.recv() - } - } - _ => { - self.websocket.write_message(WsMessage::Text("ERROR: bad_command".to_string())).unwrap(); - self.recv() - } - } - } -} - -