From 1d72d7500a9bbdf75bfcbaa7d5acb9f6da59a2f5 Mon Sep 17 00:00:00 2001 From: wikano Date: Fri, 2 Jan 2026 17:23:33 +0100 Subject: [PATCH] graphical stuff --- client-gui/src/gui_app.rs | 138 +++++++++++++++++-- client-gui/src/main.rs | 7 +- client-network/src/lib.rs | 151 +++++++++++++++------ client-network/src/server_communication.rs | 17 +++ 4 files changed, 254 insertions(+), 59 deletions(-) create mode 100644 client-network/src/server_communication.rs diff --git a/client-gui/src/gui_app.rs b/client-gui/src/gui_app.rs index e6525fd..f8a052f 100644 --- a/client-gui/src/gui_app.rs +++ b/client-gui/src/gui_app.rs @@ -13,10 +13,14 @@ enum ServerStatus { Loading, NotConnected, Connected, + ConnectedHandshake, } // --- Main Application Struct --- pub struct P2PClientApp { + remaining: std::time::Duration, // temps restant + last_update: std::time::Instant, // pour calculer delta + timer_started: bool, // Communication channels network_cmd_tx: Sender, network_event_rx: Receiver, @@ -34,6 +38,8 @@ pub struct P2PClientApp { active_peer: Option, server_status: ServerStatus, + + show_network_popup: bool, // gérer selon besoin } impl P2PClientApp { @@ -45,6 +51,9 @@ impl P2PClientApp { loaded_fs.insert("bob".to_string(), tree); Self { + remaining: std::time::Duration::from_secs(0), + timer_started: false, + last_update: std::time::Instant::now(), network_cmd_tx: cmd_tx, network_event_rx: event_rx, status_message: "Client Initialized. Awaiting network status...".to_string(), @@ -53,7 +62,8 @@ impl P2PClientApp { connect_server_name_input: "jch.irif.fr".to_string(), loaded_fs, active_peer: None, - server_status: ServerStatus::Loading, + server_status: ServerStatus::NotConnected, + show_network_popup: false, } } } @@ -62,6 +72,23 @@ impl P2PClientApp { impl eframe::App for P2PClientApp { fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) { + if matches!(self.server_status, ServerStatus::Connected) && !self.timer_started { + self.remaining = std::time::Duration::from_secs(30 * 60); + self.last_update = std::time::Instant::now(); + self.timer_started = true; + } + + // in update (every frame) + let now = std::time::Instant::now(); + let delta = now.saturating_duration_since(self.last_update); + self.last_update = now; + + if matches!(self.server_status, ServerStatus::Connected) + && self.remaining > std::time::Duration::ZERO + { + self.remaining = self.remaining.saturating_sub(delta); + } + // 1. Process incoming Network Events // We poll the channel and update the GUI state for every event received. while let Ok(event) = self.network_event_rx.try_recv() { @@ -75,7 +102,7 @@ impl eframe::App for P2PClientApp { } } NetworkEvent::PeerListUpdated(peers) => { - todo!(); + //todo!(); self.known_peers = peers; } @@ -103,6 +130,9 @@ impl eframe::App for P2PClientApp { NetworkEvent::Connected() => { self.server_status = ServerStatus::Connected; } + NetworkEvent::ConnectedHandshake() => { + self.server_status = ServerStatus::ConnectedHandshake; + } NetworkEvent::Disconnected() => todo!(), NetworkEvent::Error() => todo!(), NetworkEvent::DataReceived(_, merkle_node) => todo!(), @@ -124,21 +154,78 @@ impl eframe::App for P2PClientApp { }); ui.menu_button("Network", |ui| { - ui.horizontal(|ui| { - ui.label("Server IP:"); - ui.text_edit_singleline(&mut self.connect_address_input); - ui.text_edit_singleline(&mut self.connect_server_name_input); + ui.vertical(|ui| { + ui.horizontal(|ui| { + ui.label("Server IP:"); + ui.text_edit_singleline(&mut self.connect_address_input); + }); if ui.button("Connect").clicked() { let addr = self.connect_address_input.clone(); - let serv_name = self.connect_server_name_input.clone(); let _ = self .network_cmd_tx - .send(NetworkCommand::ConnectToServer(addr, serv_name)); + .send(NetworkCommand::ConnectToServerPut(addr)); self.server_status = ServerStatus::Loading; ui.close(); } + + /* ui.horizontal(|ui| { + ui.label("Server peer name:"); + ui.text_edit_singleline(&mut self.connect_server_name_input); + if ui.button("Connect").clicked() { + let addr = self.connect_address_input.clone(); + let serv_name = self.connect_server_name_input.clone(); + let _ = self + .network_cmd_tx + .send(NetworkCommand::ConnectToServer(addr, serv_name)); + self.server_status = ServerStatus::Loading; + ui.close(); + } + });*/ }); }); + + // état + + /*if ui.button("Network").clicked() { + self.show_network_popup = true; + }*/ + + /*if self.show_network_popup { + egui::Window::new("Network") + .collapsible(false) + .resizable(false) + .show(ctx, |ui| { + ui.horizontal_wrapped(|ui| { + ui.with_layout( + egui::Layout::right_to_left(egui::Align::TOP), + |ui| { + if ui.button("✕").clicked() { + self.show_network_popup = false; + } + }, + ); + }); + ui.horizontal(|ui| { + ui.label("Server IP:"); + ui.text_edit_singleline(&mut self.connect_address_input); + }); + ui.horizontal(|ui| { + ui.label("Server peer name:"); + ui.text_edit_singleline(&mut self.connect_server_name_input); + if ui.button("Connect").clicked() { + // envoyer commande... + let addr = self.connect_address_input.clone(); + let serv_name = self.connect_server_name_input.clone(); + let _ = self + .network_cmd_tx + .send(NetworkCommand::ConnectToServer(addr, serv_name)); + self.server_status = ServerStatus::Loading; + + self.show_network_popup = false; + } + }); + }); + }*/ }); }); @@ -149,14 +236,21 @@ impl eframe::App for P2PClientApp { ui.spinner(); } ServerStatus::Connected => { - ui.label("📡"); + ui.label("Registered but no server peer chosen..."); } ServerStatus::NotConnected => { ui.label("No connection.."); } + ServerStatus::ConnectedHandshake => { + ui.label("📡"); + } } ui.add_space(ui.available_width() - 30.0); - ui.label("30:00"); + // formater mm:ss + let secs = self.remaining.as_secs(); + let minutes = secs / 60; + let seconds = secs % 60; + ui.label(format!("{:02}:{:02}", minutes, seconds)); }); }); @@ -189,11 +283,8 @@ impl eframe::App for P2PClientApp { for peer in &self.known_peers { let is_active = self.active_peer.as_ref().map_or(false, |id| id == peer); // if peer.id == self.active_peer_id - - if ui - .selectable_label(is_active, format!("{}", peer)) - .clicked() - { + let selectable = ui.selectable_label(is_active, format!("{}", peer)); + if selectable.clicked() { // switch to displaying this peer's tree self.active_peer = Some(peer.clone()); // Request root content if not loaded @@ -208,6 +299,23 @@ impl eframe::App for P2PClientApp { // )); } } + selectable.context_menu(|ui| { + // ... action + match self.server_status { + ServerStatus::Connected => {} + _ => { + if ui + .button("Utiliser le peer en tant que serveur") + .clicked() + {} + } + } + if ui.button("Infos").clicked() { + // action 3 + ui.close(); + } + // ... autres boutons + }); } } }); diff --git a/client-gui/src/main.rs b/client-gui/src/main.rs index ba7a31c..ed372a1 100644 --- a/client-gui/src/main.rs +++ b/client-gui/src/main.rs @@ -1,5 +1,5 @@ use crate::gui_app::P2PClientApp; -use client_network::{NetworkCommand, NetworkEvent, start_p2p_executor}; +use client_network::{NetworkCommand, NetworkEvent, P2PSharedData, start_p2p_executor}; mod gui_app; @@ -11,7 +11,10 @@ async fn main() -> eframe::Result<()> { // 2. Start the P2P Network Executor in a separate Tokio task // The executor runs in the background of our main async runtime. - let _network_handle = start_p2p_executor(network_cmd_rx, network_event_tx); + + let shared_data: Option = None; + + let _network_handle = start_p2p_executor(network_cmd_rx, network_event_tx, shared_data); // 3. Configure and Run the Eframe/Egui GUI let options = eframe::NativeOptions { diff --git a/client-network/src/lib.rs b/client-network/src/lib.rs index 03b2db3..ec5a2c7 100644 --- a/client-network/src/lib.rs +++ b/client-network/src/lib.rs @@ -4,6 +4,7 @@ mod message_handling; mod messages_channels; mod messages_structure; mod registration; +mod server_communication; use crate::{ cryptographic_signature::CryptographicSignature, @@ -12,6 +13,7 @@ use crate::{ registration::{ get_socket_address, parse_addresses, register_ip_addresses, register_with_the_server, }, + server_communication::get_peer_list, }; use std::sync::{Arc, Mutex}; use std::{ @@ -19,10 +21,40 @@ use std::{ str::FromStr, }; +pub struct P2PSharedData { + shared_socket: Arc, + shared_cryptopair: Arc, + shared_messageslist: Arc>>, + shared_senders: Arc, +} + +impl P2PSharedData { + pub fn new(username: String) -> P2PSharedData { + let messages_list = HashMap::::new(); + let username = String::from(username); + let crypto_pair = CryptographicSignature::new(username); + let socket = UdpSocket::bind("0.0.0.0:0").expect("bind failed"); + + let shared_socket = Arc::new(socket); + let shared_cryptopair = Arc::new(crypto_pair); + let shared_messageslist = Arc::new(Mutex::new(messages_list)); + + let senders = MultipleSenders::new(1, &shared_socket); + let shared_senders = Arc::new(senders); + P2PSharedData { + shared_socket: shared_socket, + shared_cryptopair: shared_cryptopair, + shared_messageslist: shared_messageslist, + shared_senders: shared_senders, + } + } +} + /// Messages sent to the Network thread by the GUI. pub enum NetworkCommand { - ConnectToServer(String, String), // ServerIP - FetchPeerList(String), // ServerIP + ConnectToServerPut(String), // ServerIP + ServerHandshake(String), // ServerName + FetchPeerList(String), // ServerIP RegisterAsPeer(String), Ping(), ConnectPeer(String), // IP:PORT @@ -35,6 +67,7 @@ pub enum NetworkCommand { /// Messages sent to the GUI by the Network thread. pub enum NetworkEvent { Connected(), + ConnectedHandshake(), Disconnected(), Error(), PeerConnected(String), @@ -69,6 +102,7 @@ pub fn calculate_chunk_id(data: &[u8]) -> String { pub fn start_p2p_executor( cmd_rx: Receiver, event_tx: Sender, + shared_data: Option, ) -> tokio::task::JoinHandle<()> { // Use tokio to spawn the asynchronous networking logic tokio::task::spawn(async move { @@ -81,6 +115,49 @@ pub fn start_p2p_executor( // Check for commands from the GUI if let Ok(cmd) = cmd_rx.try_recv() { match cmd { + NetworkCommand::ServerHandshake(username) => { + let server_addr_query = get_socket_address(username); + + match server_addr_query.await { + Ok(sockaddr_bytes) => { + match String::from_utf8(sockaddr_bytes.to_vec()) { + Ok(s) => { + let addresses = parse_addresses(&s); + if let Some(first) = addresses.first() { + // first: &SocketAddr + /*start_receving_thread( + &shared_socket, + &shared_messageslist, + &shared_cryptopair, + *first, // copie le SocketAddr (implémente Copy pour SocketAddr) + &shared_senders, + ); + register_ip_addresses( + &shared_cryptopair, + first.to_string(), + &shared_senders, + &shared_messageslist, + 545, + );*/ + + let res = event_tx.send(NetworkEvent::Connected()); + } else { + let res = event_tx.send(NetworkEvent::Error()); + eprintln!("no valid socket addresses found in: {}", s); + } + } + Err(e) => { + let res = event_tx.send(NetworkEvent::Error()); + eprintln!("invalid UTF-8 in socket address bytes: {}", e); + } + } + } + Err(e) => { + eprintln!("failed to retrieve socket address: {}", e); + let res = event_tx.send(NetworkEvent::Error()); + } + } + } NetworkCommand::ConnectPeer(addr) => { println!("[Network] ConnectPeer() called"); println!("[Network] Attempting to connect to: {}", addr); @@ -97,14 +174,14 @@ pub fn start_p2p_executor( NetworkCommand::RequestChunk(_, _) => { println!("[Network] RequestChunk() called"); } - NetworkCommand::ConnectToServer(ip, server_name) => { + NetworkCommand::ConnectToServerPut(ip) => { println!("[Network] ConnectToServer() called"); // Actual server connection let messages_list = HashMap::::new(); - let username = String::from("az"); + let username = String::from("Gamemeixtreize"); let crypto_pair = CryptographicSignature::new(username); @@ -118,57 +195,47 @@ pub fn start_p2p_executor( let shared_senders = Arc::new(senders); if let Err(e) = register_with_the_server(&shared_cryptopair, &ip).await { + let res = event_tx.send(NetworkEvent::Error()); eprintln!("request failed: {}", e); } + let res = event_tx.send(NetworkEvent::Connected()); + println!("ip: {}", ip); - let server_addr_query = get_socket_address(server_name); + //tokio::time::sleep(std::time::Duration::from_millis(5000)).await; - match server_addr_query.await { - Ok(sockaddr_bytes) => { - match String::from_utf8(sockaddr_bytes.to_vec()) { - Ok(s) => { - let addresses = parse_addresses(&s); - if let Some(first) = addresses.first() { - // first: &SocketAddr - start_receving_thread( - &shared_socket, - &shared_messageslist, - &shared_cryptopair, - *first, // copie le SocketAddr (implémente Copy pour SocketAddr) - &shared_senders, - ); - register_ip_addresses( - &shared_cryptopair, - first.to_string(), - &shared_senders, - &shared_messageslist, - 545, - ); - } else { - eprintln!("no valid socket addresses found in: {}", s); - } - } - Err(e) => { - eprintln!("invalid UTF-8 in socket address bytes: {}", e); - } - } - } - Err(e) => eprintln!("failed to retrieve socket address: {}", e), - } - - tokio::time::sleep(std::time::Duration::from_millis(5000)).await; - - let res = event_tx.send(NetworkEvent::Connected()); + /*let res = event_tx.send(NetworkEvent::Connected()); if let Some(error) = res.err() { println!( "[Network] Couldn't send crossbeam message to GUI: {}", error.to_string() ); - } + }*/ } NetworkCommand::FetchPeerList(ip) => { + println!("cc"); + match get_peer_list(String::from("https://jch.irif.fr:8443")).await { + Ok(body) => match String::from_utf8(body.to_vec()) { + Ok(peers_list) => { + let mut peers: Vec = Vec::new(); + let mut current = String::new(); + for i in peers_list.chars() { + if i == '\n' { + peers.push(current.clone()); + current.clear(); + } else { + current.push(i); + } + } + let res = event_tx.send(NetworkEvent::PeerListUpdated(peers)); + } + Err(e) => { + eprintln!("invalid UTF-8 in socket address bytes: {}", e); + } + }, + Err(e) => println!("error"), + } println!("[Network] FetchPeerList() called"); } NetworkCommand::RegisterAsPeer(_) => { diff --git a/client-network/src/server_communication.rs b/client-network/src/server_communication.rs new file mode 100644 index 0000000..ae6cb10 --- /dev/null +++ b/client-network/src/server_communication.rs @@ -0,0 +1,17 @@ +use bytes::Bytes; + +pub async fn get_peer_list(server_address: String) -> Result { + let client = reqwest::Client::new(); + let uri = format!("{}/peers/", server_address); + let res = client.get(uri).send().await?; + if res.status().is_success() { + println!("Successfully retreived the addresses."); + } else { + eprintln!( + "Failed to get the peers addresses from the server. Status: {}", + res.status() + ); + } + let body: Bytes = res.bytes().await?; + Ok(body) +}