This commit is contained in:
Tiago Batista Cardoso
2026-01-24 23:04:01 +01:00
parent 7a1155c0bd
commit 95c2dfe83c
4 changed files with 237 additions and 104 deletions

View File

@@ -5,8 +5,8 @@ use client_network::{
}; };
use crossbeam_channel::{Receiver, Sender}; use crossbeam_channel::{Receiver, Sender};
use egui::{ use egui::{
CentralPanel, CollapsingHeader, Color32, Context, CornerRadius, Frame, Response, ScrollArea, Align, CentralPanel, CollapsingHeader, Color32, Context, CornerRadius, Frame, Layout, Response,
SidePanel, Stroke, TopBottomPanel, Ui, ViewportCommand, ScrollArea, SidePanel, Stroke, TopBottomPanel, Ui, ViewportCommand,
}; };
use std::collections::HashSet; use std::collections::HashSet;
use std::{collections::HashMap, fmt::format, io::Seek}; use std::{collections::HashMap, fmt::format, io::Seek};
@@ -32,6 +32,7 @@ pub struct P2PClientApp {
// GUI State // GUI State
status_message: String, status_message: String,
known_peers: Vec<(String, bool)>, known_peers: Vec<(String, bool)>,
loading_peers: Vec<String>,
connect_address_input: String, connect_address_input: String,
connected_address: String, connected_address: String,
connect_name_input: String, connect_name_input: String,
@@ -47,8 +48,8 @@ pub struct P2PClientApp {
show_network_popup: bool, // gérer selon besoin show_network_popup: bool, // gérer selon besoin
error_message: Option<String>, // Some(message) -> afficher, None -> rien error_message: Option<(String, String)>, // Some(message) -> afficher, None -> rien
success_message: Option<String>, // Some(message) -> afficher, None -> rien success_message: Option<(String, String)>, // Some(message) -> afficher, None -> rien
active_server: String, active_server: String,
current_downloading_file_map: MerkleTree, current_downloading_file_map: MerkleTree,
@@ -73,6 +74,7 @@ impl P2PClientApp {
network_event_rx: event_rx, network_event_rx: event_rx,
status_message: "Client Initialized. Awaiting network status...".to_string(), status_message: "Client Initialized. Awaiting network status...".to_string(),
known_peers: Vec::new(), known_peers: Vec::new(),
loading_peers: Vec::new(),
connect_address_input: "https://jch.irif.fr:8443".to_string(), connect_address_input: "https://jch.irif.fr:8443".to_string(),
connected_address: "".to_string(), connected_address: "".to_string(),
loaded_fs, loaded_fs,
@@ -89,11 +91,11 @@ impl P2PClientApp {
remaining_chunks: HashSet::new(), remaining_chunks: HashSet::new(),
} }
} }
pub fn show_error(&mut self, msg: impl Into<String>) { pub fn show_error(&mut self, msg: impl Into<String>, peer_username: impl Into<String>) {
self.error_message = Some(msg.into()); self.error_message = Some((msg.into(), peer_username.into()));
} }
pub fn show_success(&mut self, msg: impl Into<String>) { pub fn show_success(&mut self, msg: impl Into<String>, peer_username: impl Into<String>) {
self.success_message = Some(msg.into()); self.success_message = Some((msg.into(), peer_username.into()));
} }
pub fn clear_error(&mut self) { pub fn clear_error(&mut self) {
self.error_message = None; self.error_message = None;
@@ -252,8 +254,9 @@ impl eframe::App for P2PClientApp {
self.known_peers.clear(); self.known_peers.clear();
self.server_status = ServerStatus::NotConnected; self.server_status = ServerStatus::NotConnected;
} }
NetworkEvent::Error(err) => { NetworkEvent::Error(err, peer_username) => {
self.show_error(err); self.loading_peers.retain(|s| s != peer_username.as_str());
self.show_error(err, peer_username);
} }
NetworkEvent::InitDownload(hash, ip) => { NetworkEvent::InitDownload(hash, ip) => {
if let Some(addr) = &self.active_peer { if let Some(addr) = &self.active_peer {
@@ -299,15 +302,16 @@ impl eframe::App for P2PClientApp {
println!("bigfile téléchargé"); println!("bigfile téléchargé");
} }
} }
NetworkEvent::Success(msg) => { NetworkEvent::Success(msg, peer_username) => {
self.show_success(msg); self.loading_peers.retain(|s| s != peer_username.as_str());
self.show_success(msg, peer_username);
} }
NetworkEvent::HandshakeFailed() => {} NetworkEvent::HandshakeFailed() => {}
NetworkEvent::ServerHandshakeFailed(err) => { NetworkEvent::ServerHandshakeFailed(err) => {
self.active_server = "".to_string(); self.active_server = "".to_string();
self.server_status = ServerStatus::NotConnected; self.server_status = ServerStatus::NotConnected;
let err_msg = format!("Failed to connect to the server: {}", err); let err_msg = format!("Failed to connect to the server: {}", err);
self.show_error(err_msg); self.show_error(err_msg, "");
let res = self.network_cmd_tx.send(NetworkCommand::ResetServerPeer()); let res = self.network_cmd_tx.send(NetworkCommand::ResetServerPeer());
} }
} }
@@ -435,84 +439,201 @@ impl eframe::App for P2PClientApp {
self.active_peer.as_ref().map_or(false, |id| id == &peer.0); // if peer.id == self.active_peer_id self.active_peer.as_ref().map_or(false, |id| id == &peer.0); // if peer.id == self.active_peer_id
let selectable: Response; let selectable: Response;
if &self.active_server == &peer.0 { // if &self.active_server == &peer.0 {
// Create a frame with green background and render the selectable inside it. // // Create a frame with green background and render the selectable inside it.
// Adjust rounding, padding and stroke as desired. // // Adjust rounding, padding and stroke as desired.
let frame = Frame { // let frame = Frame {
fill: Color32::DARK_BLUE, // fill: Color32::DARK_BLUE,
stroke: Stroke::default(), // stroke: Stroke::default(),
corner_radius: CornerRadius::from(0.5), // corner_radius: CornerRadius::from(0.5),
..Default::default() // ..Default::default()
}; // };
let internal = frame.show(ui, |ui| { // let internal = frame.show(ui, |ui| {
// // horizontal row: label on the left, spinner on the right
// ui.horizontal(|ui| {
// // let selectable label take remaining space
// ui.with_layout(Layout::left_to_right(Align::Center), |ui| {
// ui.add_space(0.0); // ensure layout established
// return ui.selectable_label(
// is_active,
// format!("{}", peer.0),
// );
// })
// .inner // return Response from the inner closure if your egui version does so
// })
// });
// selectable = internal.inner.inner;
// } else {
// selectable = ui.selectable_label(is_active, format!("{}", peer.0));
// }
// place spinner to the right of the label
ui.horizontal(|ui| {
// Use same width for the label widget as the selectable we already created:
// Recreate selectable inline so both label and spinner share the same row.
let resp = if &self.active_server == &peer.0 {
// draw with frame inline
let frame = Frame {
fill: Color32::DARK_BLUE,
stroke: Stroke::default(),
corner_radius: CornerRadius::from(0.5),
..Default::default()
};
frame
.show(ui, |ui| {
ui.selectable_label(is_active, format!("{}", peer.0))
})
.inner
} else {
ui.selectable_label(is_active, format!("{}", peer.0)) ui.selectable_label(is_active, format!("{}", peer.0))
};
ui.add_space(4.0); // small gap
if self.loading_peers.contains(&peer.0) {
// push spinner to right by expanding a spacer before it
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
ui.spinner();
});
}
// use resp (click handling etc.)
if resp.clicked() {
// switch to displaying this peer's tree
self.active_peer = Some(peer.0.clone());
// Request root content if not loaded
if !self
.loaded_fs
.contains_key(self.active_peer.as_ref().unwrap())
{
//todo!();
let _ = self.network_cmd_tx.send(NetworkCommand::Discover(
peer.0.clone(),
"root".to_string(),
self.connected_address.clone(),
));
}
}
resp.context_menu(|ui| {
// ... action
match self.server_status {
ServerStatus::Connected => {
if ui
.button("Utiliser le peer en tant que serveur")
.clicked()
{
self.active_server = peer.0.to_string();
let res = self.network_cmd_tx.send(
NetworkCommand::ServerHandshake(
peer.0.to_string(),
self.connected_address.clone(),
),
);
}
}
_ => {}
}
if ui.button("Send Ping").clicked() {
let res = self.network_cmd_tx.send(NetworkCommand::Ping(
peer.0.to_string(),
self.connected_address.clone(),
));
self.loading_peers.push(peer.0.to_owned());
}
if ui.button("Send Nat Traversal Request").clicked() {
match self.network_cmd_tx.send(
NetworkCommand::NatTraversal(
peer.0.to_string(),
self.connected_address.clone(),
),
) {
Ok(_) => {
print!(
"[+] successfully sent nat traversal request"
)
}
Err(_) => {
print!("[-] failed to send nat traversal request")
}
}
}
if ui.button("Infos").clicked() {
// action 3
ui.close();
}
// ... autres boutons
}); });
selectable = internal.inner;
} else {
selectable = ui.selectable_label(is_active, format!("{}", peer.0));
}
if selectable.clicked() {
// switch to displaying this peer's tree
self.active_peer = Some(peer.0.clone());
// Request root content if not loaded
if !self
.loaded_fs
.contains_key(self.active_peer.as_ref().unwrap())
{
//todo!();
let _ = self.network_cmd_tx.send(NetworkCommand::Discover(
peer.0.clone(),
"root".to_string(),
self.connected_address.clone(),
));
}
}
selectable.context_menu(|ui| {
// ... action
match self.server_status {
ServerStatus::Connected => {
if ui
.button("Utiliser le peer en tant que serveur")
.clicked()
{
self.active_server = peer.0.to_string();
let res = self.network_cmd_tx.send(
NetworkCommand::ServerHandshake(
peer.0.to_string(),
self.connected_address.clone(),
),
);
}
}
_ => {}
}
if ui.button("Send Ping").clicked() {
let res = self.network_cmd_tx.send(NetworkCommand::Ping(
peer.0.to_string(),
self.connected_address.clone(),
));
}
if ui.button("Send Nat Traversal Request").clicked() {
match self.network_cmd_tx.send(NetworkCommand::NatTraversal(
peer.0.to_string(),
self.connected_address.clone(),
)) {
Ok(_) => {
print!("[+] successfully sent nat traversal request")
}
Err(_) => {
print!("[-] failed to send nat traversal request")
}
}
}
if ui.button("Infos").clicked() {
// action 3
ui.close();
}
// ... autres boutons
}); });
// if self.loading_peers.contains(&peer.0) {
// ui.spinner();
// }
//if selectable.clicked() {
// switch to displaying this peer's tree
//self.active_peer = Some(peer.0.clone());
//// Request root content if not loaded
//if !self
// .loaded_fs
// .contains_key(self.active_peer.as_ref().unwrap())
//{
// //todo!();
// let _ = self.network_cmd_tx.send(NetworkCommand::Discover(
// peer.0.clone(),
// "root".to_string(),
// self.connected_address.clone(),
// ));
//}
//}
//selectable.context_menu(|ui| {
// // ... action
// match self.server_status {
// ServerStatus::Connected => {
// if ui
// .button("Utiliser le peer en tant que serveur")
// .clicked()
// {
// self.active_server = peer.0.to_string();
// let res = self.network_cmd_tx.send(
// NetworkCommand::ServerHandshake(
// peer.0.to_string(),
// self.connected_address.clone(),
// ),
// );
// }
// }
// _ => {}
// }
// if ui.button("Send Ping").clicked() {
// let res = self.network_cmd_tx.send(NetworkCommand::Ping(
// peer.0.to_string(),
// self.connected_address.clone(),
// ));
// self.loading_peers.push(peer.0.to_owned());
// }
// if ui.button("Send Nat Traversal Request").clicked() {
// match self.network_cmd_tx.send(NetworkCommand::NatTraversal(
// peer.0.to_string(),
// self.connected_address.clone(),
// )) {
// Ok(_) => {
// print!("[+] successfully sent nat traversal request")
// }
// Err(_) => {
// print!("[-] failed to send nat traversal request")
// }
// }
// }
// if ui.button("Infos").clicked() {
// // action 3
// ui.close();
// }
// // ... autres boutons
//});
} }
} }
}); });
@@ -554,7 +675,8 @@ impl eframe::App for P2PClientApp {
.resizable(false) .resizable(false)
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0]) .anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
.show(ctx, |ui| { .show(ctx, |ui| {
ui.label(&msg); ui.label(&msg.1);
ui.label(&msg.0);
if ui.button("OK").clicked() { if ui.button("OK").clicked() {
self.clear_error(); self.clear_error();
} }
@@ -568,7 +690,8 @@ impl eframe::App for P2PClientApp {
.resizable(false) .resizable(false)
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0]) .anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
.show(ctx, |ui| { .show(ctx, |ui| {
ui.label(&msg); ui.label(&msg.1);
ui.label(&msg.0);
if ui.button("OK").clicked() { if ui.button("OK").clicked() {
self.clear_success(); self.clear_success();
} }

View File

@@ -194,8 +194,8 @@ pub enum NetworkEvent {
Connected(String), Connected(String),
ConnectedHandshake(), ConnectedHandshake(),
Disconnected(), Disconnected(),
Error(String), Error(String, String),
Success(String), Success(String, String),
PeerConnected(String), PeerConnected(String),
PeerListUpdated(Vec<(String, bool)>), PeerListUpdated(Vec<(String, bool)>),
FileTreeReceived([u8; 32], MerkleNode, String), // peer_id, content FileTreeReceived([u8; 32], MerkleNode, String), // peer_id, content
@@ -458,7 +458,8 @@ pub fn start_p2p_executor(
Err(e) => { Err(e) => {
let mut err_msg = String::from("failed to initialize socket: "); let mut err_msg = String::from("failed to initialize socket: ");
err_msg += &e.to_string(); err_msg += &e.to_string();
let res = event_tx.send(NetworkEvent::Error(err_msg)); let res =
event_tx.send(NetworkEvent::Error(err_msg, name.to_owned()));
let res = event_tx.send(NetworkEvent::Disconnected()); let res = event_tx.send(NetworkEvent::Disconnected());
None None
} }
@@ -468,7 +469,8 @@ pub fn start_p2p_executor(
if let Err(e) = register_with_the_server(&sd.cryptopair(), &ip).await { if let Err(e) = register_with_the_server(&sd.cryptopair(), &ip).await {
let mut err_msg = String::from("request failed: "); let mut err_msg = String::from("request failed: ");
err_msg += &e.to_string(); err_msg += &e.to_string();
let res = event_tx.send(NetworkEvent::Error(err_msg)); let res =
event_tx.send(NetworkEvent::Error(err_msg, name.to_owned()));
let res = event_tx.send(NetworkEvent::Disconnected()); let res = event_tx.send(NetworkEvent::Disconnected());
} else { } else {
let res = event_tx.send(NetworkEvent::Connected(ip)); let res = event_tx.send(NetworkEvent::Connected(ip));
@@ -492,6 +494,7 @@ pub fn start_p2p_executor(
if ip == "" { if ip == "" {
let res = event_tx.send(NetworkEvent::Error( let res = event_tx.send(NetworkEvent::Error(
"Not registered to any server".to_string(), "Not registered to any server".to_string(),
"".to_owned(),
)); ));
} else { } else {
println!("cc"); println!("cc");
@@ -530,7 +533,7 @@ pub fn start_p2p_executor(
let pingrequest = let pingrequest =
construct_message(PING, Vec::new(), id, sd.cryptopair_ref()); construct_message(PING, Vec::new(), id, sd.cryptopair_ref());
let peer_address = let peer_address =
get_socket_address(str, ip, shared_data.as_ref()).await; get_socket_address(str.to_owned(), ip, shared_data.as_ref()).await;
match peer_address { match peer_address {
Ok(addr) => { Ok(addr) => {
//if let Some(ping) = pingrequest { //if let Some(ping) = pingrequest {
@@ -546,10 +549,13 @@ pub fn start_p2p_executor(
// sd.messages_list(), // sd.messages_list(),
// ); // );
//} //}
match event_tx.send(NetworkEvent::Success(format!( match event_tx.send(NetworkEvent::Success(
"Successfully sent ping message to {}.", format!(
addr.to_string() "Successfully sent ping message to {}.",
))) { addr.to_string(),
),
str.to_owned(),
)) {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
eprintln!("NetworkEvent error : {}", e); eprintln!("NetworkEvent error : {}", e);
@@ -557,7 +563,9 @@ pub fn start_p2p_executor(
}; };
} }
Err(err_msg) => { Err(err_msg) => {
match event_tx.send(NetworkEvent::Error(err_msg.to_string())) { match event_tx
.send(NetworkEvent::Error(err_msg.to_string(), str))
{
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
eprintln!("NetworkEvent error : {}", e); eprintln!("NetworkEvent error : {}", e);
@@ -624,7 +632,9 @@ pub fn start_p2p_executor(
); );
} }
Err(err_msg) => { Err(err_msg) => {
match event_tx.send(NetworkEvent::Error(err_msg.to_string())) { match event_tx
.send(NetworkEvent::Error(err_msg.to_string(), username))
{
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
eprintln!("NetworkEvent error : {}", e); eprintln!("NetworkEvent error : {}", e);

View File

@@ -317,10 +317,10 @@ pub fn parse_message(
String::from_utf8(received_message[LENGTH..(msg_length + LENGTH + 4)].to_vec()) String::from_utf8(received_message[LENGTH..(msg_length + LENGTH + 4)].to_vec())
{ {
let err_msg = format!("Error received from peer {} : {}", ip, err_received); let err_msg = format!("Error received from peer {} : {}", ip, err_received);
let _ = cmd_tx_clone.send(NetworkEvent::Error(err_msg)); let _ = cmd_tx_clone.send(NetworkEvent::Error(err_msg, "".to_owned()));
} else { } else {
let err_msg = format!("Error received from peer {} : N/A", ip,); let err_msg = format!("Error received from peer {} : N/A", ip,);
let _ = cmd_tx_clone.send(NetworkEvent::Error(err_msg)); let _ = cmd_tx_clone.send(NetworkEvent::Error(err_msg, "".to_owned()));
} }
} }

View File

@@ -92,7 +92,7 @@ pub async fn perform_handshake(
} }
None => { None => {
let err_msg = format!("failed to retreive socket address:").to_string(); let err_msg = format!("failed to retreive socket address:").to_string();
let res = event_tx.send(NetworkEvent::Error(err_msg)); let res = event_tx.send(NetworkEvent::Error(err_msg, "".to_owned()));
} }
} }