1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use broadcasts::Broadcast;
use commands::handle_commands;
use state::ServerState;
use std::{env, sync::Arc};
use tokio::{
io::{self, AsyncReadExt, AsyncWriteExt},
net::TcpListener,
spawn,
};
pub mod broadcasts;
pub mod commands;
pub mod messages;
pub mod routines;
pub mod state;
#[tokio::main]
async fn main() {
let args: Vec<String> = env::args().collect();
if args.iter().any(|s| s == "-v" || s == "--version") {
let ver = env!("CARGO_PKG_VERSION");
let sha = env!("GIT_SHORTHASH");
let por = env!("GIT_PORCELAIN");
let rel = match cfg!(debug_assertions) {
true => "dbg",
false => "rel",
};
println!("picochat v{ver} ({sha} {rel} {por})");
return;
}
let listener = TcpListener::bind("0.0.0.0:7426").await.unwrap();
let state = Arc::new(ServerState::new());
{
let state = state.clone();
spawn(async move {
let mut rx = state.broadcasts.subscribe();
loop {
let broadcast = rx.recv().await.unwrap();
print!("{broadcast}");
}
});
}
loop {
let (mut socket, _) = listener.accept().await.unwrap();
let state = state.clone();
spawn(async move {
match routines::prejoin(&mut socket, &state).await {
Ok(_) => (),
Err(_) => return,
};
let mut user = match routines::get_nickname(&mut socket, &state).await {
Ok(Some(u)) => u,
Ok(None) | Err(_) => return,
};
match routines::postjoin(&mut socket).await {
Ok(_) => (),
Err(_) => return,
};
let (mut rsocket, mut wsocket) = io::split(socket);
let mut rx = state.broadcasts.subscribe();
let mut buffer = [0u8; 256];
loop {
buffer.fill(0);
tokio::select! {
res = rx.recv() => {
let res = res.unwrap();
match res.send_to_all() {
true => {
let res = res.to_string();
match wsocket.write_all(res.as_bytes()).await {
Ok(_) => (),
Err(_) => break,
}
}
false => {
if let Broadcast::UserPoke{poker, poked} = &res {
if &user.name == &poker.name {
let res = res.actor_string();
match wsocket.write_all(res.as_bytes()).await {
Ok(_) => (),
Err(_) => break,
}
}
if &user.name == &poked.name {
let res = res.target_string();
match wsocket.write_all(res.as_bytes()).await {
Ok(_) => (),
Err(_) => break,
}
}
}
}
}
}
res = rsocket.read(&mut buffer) => {
match res {
Ok(0) | Err(_) => break,
Ok(_) => {
let msg: String = String::from_utf8_lossy(&buffer).chars()
.filter(|c| {
c.is_alphabetic() || c.is_digit(10) || c.is_ascii_punctuation() || *c == ' '
}).collect();
match msg.starts_with('/') {
true => {
match handle_commands(msg, &mut wsocket, &mut user, &state).await {
Ok(_) => (),
Err(_) => return,
}
},
false => {
state.broadcasts.send(Broadcast::UserMessage {
user: user.clone(),
message: msg,
}).unwrap();
}
}
},
}
}
}
}
state.remove_user(&user).await;
state.broadcasts.send(Broadcast::UserLeft(user)).unwrap();
});
}
}