commit so i can work elsewhere
This commit is contained in:
parent
58fa9ee185
commit
686d1d74fe
2 changed files with 208 additions and 0 deletions
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
|||
[package]
|
||||
name = "rawnetchat"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
time = { version = "0.3.37", features = ["formatting", "local-offset"] }
|
201
src/main.rs
Normal file
201
src/main.rs
Normal file
|
@ -0,0 +1,201 @@
|
|||
#![feature(random)]
|
||||
#![feature(inline_const_pat)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::net::{TcpListener, TcpStream};
|
||||
use std::random;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread::{self, sleep};
|
||||
|
||||
#[derive(Clone)]
|
||||
enum UserStatus {
|
||||
ONLINE,
|
||||
DISCONNECTED,
|
||||
IDLE,
|
||||
CUSTOM(String),
|
||||
}
|
||||
|
||||
impl UserStatus {
|
||||
fn get_status_string(&self) -> String {
|
||||
match self {
|
||||
UserStatus::ONLINE => "\x1b[0m\x1b[32mOnline\x1b[0m".to_string(),
|
||||
UserStatus::IDLE => "\x1b[0m\x1b[33mIdle\x1b[0m".to_string(),
|
||||
UserStatus::DISCONNECTED => "\x1b[0m\x1b[31mDisconnected\x1b[0m".to_string(),
|
||||
UserStatus::CUSTOM(val) => format!("\x1b[0m\x1b[34m{}\x1b[0m", val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct User {
|
||||
name: String,
|
||||
status: UserStatus,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Message {
|
||||
sender: User,
|
||||
contents: String,
|
||||
timestamp: time::OffsetDateTime,
|
||||
}
|
||||
|
||||
type Channel = Arc<Mutex<UnwrappedChannel>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct UnwrappedChannel {
|
||||
name: String,
|
||||
messages: Vec<Message>,
|
||||
online_users: Vec<User>,
|
||||
}
|
||||
|
||||
fn handle_client(mut stream: TcpStream, channel: Channel) {
|
||||
stream
|
||||
.write_all(b"\x1b[33mEnter your chosen nickname(up to 30 characters):\x1b[0m\x1b[4m\n")
|
||||
.unwrap();
|
||||
let mut buf = [0u8; 30];
|
||||
let mut i = 0usize;
|
||||
let mut buf1 = [0u8];
|
||||
while buf1[0] != b'\n' || i == 0 {
|
||||
stream.read_exact(&mut buf1).unwrap();
|
||||
if buf1[0] == b'\n' && i == 0 {
|
||||
stream.write_all(
|
||||
b"\x1b[0m\x1b[31;1mEnter at least one character.\n\x1b[0m\x1b[33mEnter your chosen nickname(up to 30 characters):\x1b[0m\x1b[4m\n"
|
||||
).unwrap();
|
||||
buf = [0u8; 30];
|
||||
buf1[0] = b'\0';
|
||||
continue;
|
||||
} else if buf1[0] == b'\n' && i >= 30 {
|
||||
stream.write_all(
|
||||
b"\x1b[0m\x1b[31;1mEnter less than 30 characters.\n\x1b[0m\x1b[33mEnter your chosen nickname(up to 30 characters):\x1b[0m\x1b[4m\n"
|
||||
).unwrap();
|
||||
buf = [0u8; 30];
|
||||
buf1[0] = b'\0';
|
||||
i = 0;
|
||||
continue;
|
||||
} else if buf1[0] == b'\n' {
|
||||
break;
|
||||
}
|
||||
if i < 30 {
|
||||
buf[i] = buf1[0];
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
stream
|
||||
.write_fmt(format_args!(
|
||||
"\x1b[0m\x1b[32;1mWelcome, {}! Joining server \"{}\".\x1b[0m\n",
|
||||
String::from_utf8_lossy(&(buf[..i])),
|
||||
channel.lock().unwrap().name
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let user = User {
|
||||
name: String::from_utf8_lossy(&(buf[..i])).to_string(),
|
||||
status: UserStatus::ONLINE,
|
||||
};
|
||||
|
||||
channel.lock().unwrap().online_users.push(user.clone());
|
||||
|
||||
{
|
||||
let channel = channel.clone();
|
||||
let mut stream = stream.try_clone().unwrap();
|
||||
|
||||
let time_format = time::format_description::well_known::Rfc2822;
|
||||
|
||||
thread::spawn(move || {
|
||||
let mut current_message_count = channel.lock().unwrap().messages.len();
|
||||
loop {
|
||||
let new_message_count = channel.lock().unwrap().messages.len();
|
||||
if current_message_count != new_message_count {
|
||||
let new_messages = &channel.lock().unwrap().messages[current_message_count..];
|
||||
for msg in new_messages {
|
||||
stream
|
||||
.write_fmt(format_args!(
|
||||
"\x1b[0m\x1b[34m[{}]\x1b[0m \x1b[1m{}\x1b[0m \x1b[31m⇛\x1b[0m \x1b[35m{}\x1b[0m\n",
|
||||
msg.timestamp.format(&time_format).unwrap(),
|
||||
msg.sender.name,
|
||||
msg.contents
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
current_message_count = new_message_count;
|
||||
}
|
||||
|
||||
// so that not all the threads will sync at once
|
||||
let randomval = random::random::<u64>() / (18446744073709551615u64 / 20);
|
||||
sleep(std::time::Duration::from_millis(240 + randomval));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let mut buf = Vec::<u8>::new();
|
||||
let mut buf1 = [0u8];
|
||||
|
||||
loop {
|
||||
stream.read_exact(&mut buf1).unwrap();
|
||||
|
||||
if buf1[0] == b'\x08' {
|
||||
buf.pop();
|
||||
}
|
||||
|
||||
if buf1[0] == b'\n' && buf.len() > 0 {
|
||||
let contents = String::from_utf8_lossy(&buf).to_string();
|
||||
if !contents.starts_with("/") {
|
||||
channel.lock().unwrap().messages.push(Message {
|
||||
sender: user.clone(),
|
||||
contents,
|
||||
timestamp: time::OffsetDateTime::now_local().unwrap(),
|
||||
});
|
||||
} else {
|
||||
if contents.starts_with("/help") || contents.starts_with("/?") {
|
||||
stream
|
||||
.write_all(
|
||||
b"\x1b[0m\x1b[32mValid commands:\n \
|
||||
/help, /?: Print this help.\n \
|
||||
/status online|idle|disconnected|(any value): Set your status.\n \
|
||||
/user nick: Get information about a user.\x1b[0m",
|
||||
)
|
||||
.unwrap();
|
||||
} else if contents.starts_with("/status") {
|
||||
let contents = contents.split(" ").collect::<Vec<&str>>();
|
||||
if contents.len() == 1 {
|
||||
stream
|
||||
.write_fmt(
|
||||
format_args!("\x1b[0m\x1b[32mYou are currently marked as {}\n\x1b[0m"),
|
||||
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
} else {
|
||||
stream
|
||||
.write_all(b"\x1b[0m\x1b[31;1mThat's an invalid command!\n\x1b[0m")
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
buf.clear();
|
||||
}
|
||||
if buf1[0] == b'\n' {
|
||||
continue;
|
||||
}
|
||||
buf.push(buf1[0]);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
let listener = TcpListener::bind("0.0.0.0:7867")?;
|
||||
let channel: Channel = Arc::new(Mutex::new(UnwrappedChannel {
|
||||
name: "Rawnetchat".to_string(),
|
||||
messages: vec![],
|
||||
online_users: vec![],
|
||||
}));
|
||||
|
||||
for stream in listener.incoming() {
|
||||
let channel: Channel = channel.clone();
|
||||
thread::spawn(|| {
|
||||
handle_client(stream.unwrap(), channel);
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Add table
Reference in a new issue