From ea805970508007e54895ec87d2721cb83b81bfae Mon Sep 17 00:00:00 2001 From: clizia Date: Sun, 26 Jan 2025 02:15:37 +0100 Subject: [PATCH] bunch of things --- Cargo.toml | 12 ++++++++++ src/bin/client.rs | 15 +++++++++++++ src/bin/server.rs | 8 +++++++ src/card.rs | 2 +- src/client.rs | 28 +++++++++++++++++++++++ src/deck.rs | 4 ++-- src/lib.rs | 8 +++++++ src/lobby.rs | 2 +- src/main.rs | 12 ++++------ src/message.rs | 46 ++++++++++++++++++++++++++++++++++++++ src/message_read.rs | 44 ++++++++++++++++++++++++++++++++++++ src/player.rs | 2 +- src/server.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 224 insertions(+), 13 deletions(-) create mode 100644 src/bin/client.rs create mode 100644 src/bin/server.rs create mode 100644 src/client.rs create mode 100644 src/lib.rs create mode 100644 src/message.rs create mode 100644 src/message_read.rs create mode 100644 src/server.rs diff --git a/Cargo.toml b/Cargo.toml index b1a9c23..8688230 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,18 @@ name = "durak-frthistime" version = "0.1.0" edition = "2021" +[lib] +name = "lib" +path = "src/lib.rs" + +[[bin]] +name = "server" +path = "src/bin/server.rs" + +[[bin]] +name = "client" +path = "src/bin/client.rs" + [dependencies] rand = "0.9.0-beta.1" clap = "4.5.23" diff --git a/src/bin/client.rs b/src/bin/client.rs new file mode 100644 index 0000000..846905e --- /dev/null +++ b/src/bin/client.rs @@ -0,0 +1,15 @@ +use lib::{client::Client, message::Message}; + +#[tokio::main] +pub async fn main() -> anyhow::Result<()> { + let mut client = Client::connect("127.0.0.1", 8080).await?; + + for i in 1..11 { + let message = Message::new(format!("Hello toto x {}!!!", i)); + client.send_message(message.unwrap().clone()).await?; + } + + client.send_message(Message::new("EXIT").unwrap()).await?; + + Ok(()) +} diff --git a/src/bin/server.rs b/src/bin/server.rs new file mode 100644 index 0000000..011ef49 --- /dev/null +++ b/src/bin/server.rs @@ -0,0 +1,8 @@ +use lib::server::Server; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let mut server = Server::new("127.0.0.1", 8080); + server.run().await?; + Ok(()) +} diff --git a/src/card.rs b/src/card.rs index e16e85c..ef7f5e6 100644 --- a/src/card.rs +++ b/src/card.rs @@ -17,7 +17,7 @@ pub enum Suit { } impl Card { - pub fn new(suit: Suit, value: u8) -> Card { + pub fn new(suit: Suit, value: u8) -> Self { Card { suit, value, diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 0000000..ad45f9a --- /dev/null +++ b/src/client.rs @@ -0,0 +1,28 @@ +use tokio::{io::AsyncWriteExt, net::TcpStream}; + +use crate::message::Message; + +pub struct Client { + pub server_host: String, + pub server_port: u16, + pub stream: TcpStream, +} + +impl Client { + pub async fn connect(host: impl Into, port: u16) -> anyhow::Result { + let host = host.into(); + let address = format!("{}:{}", host, port); + let stream = TcpStream::connect(address).await?; + + Ok(Self { + server_host: host, + server_port: port, + stream, + }) + } + + pub async fn send_message(&mut self, message: Message) -> anyhow::Result<()> { + self.stream.write_all(&message.encode()).await?; + Ok(()) + } +} diff --git a/src/deck.rs b/src/deck.rs index 0376461..daba173 100644 --- a/src/deck.rs +++ b/src/deck.rs @@ -10,7 +10,7 @@ pub struct Deck { } impl Deck { - pub fn new() -> Deck { + pub fn new() -> Self { // possible values go from 0 to 8 because Durak is played // without cards with a value lower than 6 in real life // and ace is the highest card @@ -35,7 +35,7 @@ impl Deck { self.deck } - pub fn shuffle(mut self) -> Deck { + pub fn shuffle(mut self) -> Self { let mut rng = rng(); self.deck.shuffle(&mut rng); // shuffle modifies in place diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..88e2556 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,8 @@ +pub mod server; +pub mod client; +pub mod card; +pub mod deck; +pub mod lobby; +pub mod player; +pub mod message; +pub mod message_read; diff --git a/src/lobby.rs b/src/lobby.rs index 87e3659..39912a6 100644 --- a/src/lobby.rs +++ b/src/lobby.rs @@ -9,7 +9,7 @@ pub struct Lobby { } impl Lobby { - pub fn new() -> Lobby { + pub fn new() -> Self { let players: Vec = Vec::new(); let deck = Deck::new().shuffle(); diff --git a/src/main.rs b/src/main.rs index 4b07559..d4e82b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,18 +2,14 @@ use std::collections::HashMap; use std::io::{self, Error}; use std::net::SocketAddr; -use crate::lobby::Lobby; -use crate::player::Player; +use lib::lobby::Lobby; +use lib::player::Player; +use std::sync::{Arc, Mutex}; use anyhow::Result; use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader}; use tokio::net::{TcpListener, TcpStream}; -mod card; -mod deck; -mod lobby; -mod player; - async fn handle_connection(mut stream: TcpStream, addr: SocketAddr) -> (Option, Option) { let (mut read, mut write) = stream.split(); @@ -51,7 +47,7 @@ async fn main() -> io::Result<()> { let address = "127.0.0.1:8080".to_string(); let listener = TcpListener::bind(&address).await?; - let mut lobbies: HashMap = HashMap::new(); + let mut lobbies: Arc>> = Arc::new(Mutex::new(HashMap::new())); loop { let (mut stream, addr) = listener.accept().await?; diff --git a/src/message.rs b/src/message.rs new file mode 100644 index 0000000..adbc9e2 --- /dev/null +++ b/src/message.rs @@ -0,0 +1,46 @@ +// everything here needs to be adjusted for the deserialized json +// +#[derive(Debug, Clone)] +pub struct Message { + pub length: u16, + pub content: String, +} + +impl Message { + pub fn new(content: impl Into) -> anyhow::Result { + let content = content.into(); + let length = content.len() as u16; + + Ok(Self { + length, + content, + }) + } + + pub fn encode(&self) -> Vec { + let mut buffer = Vec::new(); + buffer.extend(&self.length.to_be_bytes()); + buffer.extend(self.content.as_bytes()); + + buffer + } + + pub fn decode(buffer: &[u8]) -> anyhow::Result { + // this `if` compares the buffer length to the minimum + // message size (which is 2 because it takes to bytes + // to write 0), it is now hardcoded but it MUST be moved + // into a constant in a special constant definition file + // byeeeeeeeeeeeeeeeeee luv ya + if buffer.len() < 2 { + return Err(anyhow::anyhow!("Invalid message length")); + } + + let length = u16::from_be_bytes([buffer[0], buffer[1]]); + let content = String::from_utf8(buffer[2..2 + length as usize].to_vec())?; + + Ok(Self { + length, + content, + }) + } +} diff --git a/src/message_read.rs b/src/message_read.rs new file mode 100644 index 0000000..4e13313 --- /dev/null +++ b/src/message_read.rs @@ -0,0 +1,44 @@ +// every hardcoded 2 will one day be a constant in a special +// constant file but rn I AM LAZY AND ITS LATE +// +use crate::message::Message; + +pub struct MessageReader { + pub buffer: Vec, +} + +impl MessageReader { + pub fn new() -> Self { + Self { buffer: Vec::new() } + } + + fn can_parse(&self) -> bool { + if self.buffer.len() < 2 { + return false; + } + + let length = u16::from_be_bytes([self.buffer[0], self.buffer[1]]); + self.buffer.len() >= 2 + length as usize + } + + fn parse_first(&mut self) -> anyhow::Result { + let length = u16::from_be_bytes([self.buffer[0], self.buffer[2]]); + let message_length = 2 + length as usize; + let message = self.buffer[..message_length].to_vec(); + self.buffer = self.buffer[message_length..].to_vec(); + + Message::decode(&message) + } + + pub fn read(&mut self, data: &[u8]) -> anyhow::Result> { + self.buffer.extend_from_slice(data); + let mut data = vec![]; + + while self.can_parse() { + let message = self.parse_first()?; + data.push(message); + } + + Ok(data) + } +} diff --git a/src/player.rs b/src/player.rs index 69752b5..f407840 100644 --- a/src/player.rs +++ b/src/player.rs @@ -19,7 +19,7 @@ pub struct Player { } impl Player { - pub fn new(addr: SocketAddr, name: &str) -> Player { + pub fn new(addr: SocketAddr, name: &str) -> Self { let hand_empty: Vec = Vec::new(); let to_digest: String = addr.to_string(); diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000..f87e466 --- /dev/null +++ b/src/server.rs @@ -0,0 +1,54 @@ +use tokio::{io::AsyncReadExt, net::TcpListener}; +use crate::message_read::MessageReader; + +pub struct Server { + pub host: String, + pub port: u16, +} + +impl Server { + pub fn new(host: impl Into, port: u16) -> Self { + Server { + host: host.into(), + port, + } + } + + pub async fn run(&mut self) -> anyhow::Result<()> { + let listener = TcpListener::bind(format!( + "{}:{}", + self.host, + self.port + )).await?; + + println!("Server is running on {}:{}", self.host, self.port); + + loop { + let (mut socket, addr) = listener.accept().await?; + println!("Connection received from {}", addr); + + tokio::task::spawn(async move { + let mut message_reader = MessageReader::new(); + 'handler: loop { + let mut buffer = vec![0; 256]; + let bytes_read = socket.read(&mut buffer).await?; + + let messages = message_reader.read(&buffer[..bytes_read])?; + + for message in messages { + if message.content == "EXIT" { + println!("Connection closed by the client"); + break 'handler; + } + + println!("Message:\n{:?}", message); + } + } + + Ok::<(), anyhow::Error>(()) + }); + } + + Ok(()) + } +}