Basics working.

This commit is contained in:
Arthur Beck 2025-03-04 15:31:24 +00:00
parent 686d1d74fe
commit 65086a3b3c

View file

@ -1,13 +1,13 @@
#![feature(random)] #![feature(random)]
#![feature(inline_const_pat)] #![feature(inline_const_pat)]
use std::io::prelude::*; use std::io::{ErrorKind, prelude::*};
use std::net::{TcpListener, TcpStream}; use std::net::{TcpListener, TcpStream};
use std::random; use std::random;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread::{self, sleep}; use std::thread::{self, JoinHandle, sleep};
#[derive(Clone)] #[derive(Clone, PartialEq, Eq)]
enum UserStatus { enum UserStatus {
ONLINE, ONLINE,
DISCONNECTED, DISCONNECTED,
@ -18,10 +18,10 @@ enum UserStatus {
impl UserStatus { impl UserStatus {
fn get_status_string(&self) -> String { fn get_status_string(&self) -> String {
match self { match self {
UserStatus::ONLINE => "\x1b[0m\x1b[32mOnline\x1b[0m".to_string(), UserStatus::ONLINE => "\x1b[0m\x1b[32monline\x1b[0m".to_string(),
UserStatus::IDLE => "\x1b[0m\x1b[33mIdle\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::DISCONNECTED => "\x1b[0m\x1b[31mdisconnected\x1b[0m".to_string(),
UserStatus::CUSTOM(val) => format!("\x1b[0m\x1b[34m{}\x1b[0m", val) UserStatus::CUSTOM(val) => format!("\x1b[0m\x1b[34m{}\x1b[0m", val),
} }
} }
} }
@ -55,7 +55,7 @@ fn handle_client(mut stream: TcpStream, channel: Channel) {
let mut buf = [0u8; 30]; let mut buf = [0u8; 30];
let mut i = 0usize; let mut i = 0usize;
let mut buf1 = [0u8]; let mut buf1 = [0u8];
while buf1[0] != b'\n' || i == 0 { 'bufreadloop: while buf1[0] != b'\n' || i == 0 {
stream.read_exact(&mut buf1).unwrap(); stream.read_exact(&mut buf1).unwrap();
if buf1[0] == b'\n' && i == 0 { if buf1[0] == b'\n' && i == 0 {
stream.write_all( stream.write_all(
@ -73,6 +73,19 @@ fn handle_client(mut stream: TcpStream, channel: Channel) {
i = 0; i = 0;
continue; continue;
} else if buf1[0] == b'\n' { } else if buf1[0] == b'\n' {
for user in &channel.lock().unwrap().online_users {
if user.name == String::from_utf8_lossy(&(buf[..i])).to_string()
&& user.status != UserStatus::DISCONNECTED
{
stream.write_all(
b"\x1b[0m\x1b[31;1mSorry, but that nickname is taken.\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 'bufreadloop;
}
}
break; break;
} }
if i < 30 { if i < 30 {
@ -89,22 +102,34 @@ fn handle_client(mut stream: TcpStream, channel: Channel) {
)) ))
.unwrap(); .unwrap();
let user = User { let mut user = User {
name: String::from_utf8_lossy(&(buf[..i])).to_string(), name: String::from_utf8_lossy(&(buf[..i])).to_string(),
status: UserStatus::ONLINE, status: UserStatus::ONLINE,
}; };
let netbot = User {
name: "Netbot".to_owned(),
status: UserStatus::ONLINE,
};
channel.lock().unwrap().online_users.push(user.clone()); channel.lock().unwrap().online_users.push(user.clone());
let (recieve_terminate_send, recieve_terminate_recv) = std::sync::mpsc::channel::<()>();
let recieve_thread: JoinHandle<_>;
{ {
let channel = channel.clone(); let channel = channel.clone();
let mut stream = stream.try_clone().unwrap(); let mut stream = stream.try_clone().unwrap();
let time_format = time::format_description::well_known::Rfc2822; let time_format = time::format_description::well_known::Rfc2822;
thread::spawn(move || { recieve_thread = thread::spawn(move || {
let mut current_message_count = channel.lock().unwrap().messages.len(); let mut current_message_count = channel.lock().unwrap().messages.len();
loop { loop {
if let Ok(_) = recieve_terminate_recv.try_recv() {
stream.shutdown(std::net::Shutdown::Both).unwrap();
panic!("recieve thread exiting");
}
let new_message_count = channel.lock().unwrap().messages.len(); let new_message_count = channel.lock().unwrap().messages.len();
if current_message_count != new_message_count { if current_message_count != new_message_count {
let new_messages = &channel.lock().unwrap().messages[current_message_count..]; let new_messages = &channel.lock().unwrap().messages[current_message_count..];
@ -133,7 +158,27 @@ fn handle_client(mut stream: TcpStream, channel: Channel) {
let mut buf1 = [0u8]; let mut buf1 = [0u8];
loop { loop {
stream.read_exact(&mut buf1).unwrap(); if let Err(err) = stream.read_exact(&mut buf1) {
if err.kind() == ErrorKind::UnexpectedEof {
user.status = UserStatus::DISCONNECTED;
for usr in &mut channel.lock().unwrap().online_users {
if usr.name == user.name {
usr.status = user.status.clone();
}
}
channel.lock().unwrap().messages.push(Message {
sender: netbot.clone(),
contents: format!("User {} has left.", user.name),
timestamp: time::OffsetDateTime::now_local().unwrap(),
});
recieve_terminate_send.send(()).unwrap();
let _ = recieve_thread.join();
stream.shutdown(std::net::Shutdown::Both).unwrap();
return;
}
}
if buf1[0] == b'\x08' { if buf1[0] == b'\x08' {
buf.pop(); buf.pop();
@ -148,25 +193,107 @@ fn handle_client(mut stream: TcpStream, channel: Channel) {
timestamp: time::OffsetDateTime::now_local().unwrap(), timestamp: time::OffsetDateTime::now_local().unwrap(),
}); });
} else { } else {
if contents.starts_with("/help") || contents.starts_with("/?") { if contents.split(" ").collect::<Vec<&str>>()[0] == "/help" || contents.split(" ").collect::<Vec<&str>>()[0] == "/?" {
stream stream
.write_all( .write_all(
b"\x1b[0m\x1b[32mValid commands:\n \ b"\x1b[0m\x1b[32mValid commands:\n \
/help, /?: Print this help.\n \ /help, /?: Print this help.\n \
/status online|idle|disconnected|(any value): Set your status.\n \ /status [online|idle|disconnected|(any value)]: Set your status.\n \
/user nick: Get information about a user.\x1b[0m", /user [nick]: Get information about a user.\n \
/server [new name]: Get or set the server name.\n \
/users: List users.\x1b[0m\n",
) )
.unwrap(); .unwrap();
} else if contents.starts_with("/status") { } else if contents.split(" ").collect::<Vec<&str>>()[0] == "/status" || contents == "/user" {
let contents = contents.split(" ").collect::<Vec<&str>>(); let contents = contents.split(" ").collect::<Vec<&str>>();
if contents.len() == 1 { if contents.len() == 1 {
stream stream
.write_fmt( .write_fmt(format_args!(
format_args!("\x1b[0m\x1b[32mYou are currently marked as {}\n\x1b[0m"), "\x1b[0m\x1b[32mYou are currently marked as {}\n\x1b[0m",
user.clone().status.get_status_string()
) ))
.unwrap();
} else {
match contents[1] {
"online" => {
user.status = UserStatus::ONLINE;
}
"idle" => {
user.status = UserStatus::IDLE;
}
"disconnected" => {
user.status = UserStatus::DISCONNECTED;
}
other => {
user.status = UserStatus::CUSTOM(other.to_owned());
}
}
for usr in &mut channel.lock().unwrap().online_users {
if usr.name == user.name {
usr.status = user.status.clone();
}
}
stream
.write_fmt(format_args!(
"\x1b[0m\x1b[32mYou are now marked as {}\n\x1b[0m",
user.status.get_status_string()
))
.unwrap(); .unwrap();
} }
} else if contents.split(" ").collect::<Vec<&str>>()[0] == "/server" {
let contents = contents.split(" ").collect::<Vec<&str>>();
if contents.len() == 1 {
stream
.write_fmt(format_args!(
"\x1b[0m\x1b[32mCurrent server name: {}.\n\x1b[0m",
channel.lock().unwrap().name
))
.unwrap();
} else {
channel.lock().unwrap().name = contents[1].to_owned();
stream
.write_fmt(format_args!(
"\x1b[0m\x1b[32mNew server name: {}.\n\x1b[0m",
channel.lock().unwrap().name
))
.unwrap();
}
} else if contents.split(" ").collect::<Vec<&str>>()[0] == "/user" {
let contents = contents.split(" ").collect::<Vec<&str>>();
let mut found_user = false;
for usr in &mut channel.lock().unwrap().online_users {
if usr.name == contents[1] {
stream
.write_fmt(format_args!(
"\x1b[0m\x1b[32mUser {} is currently {}.\n\x1b[0m",
usr.name,
usr.status.get_status_string()
))
.unwrap();
found_user = true;
break;
}
}
if !found_user {
stream
.write_fmt(format_args!(
"\x1b[0m\x1b[32mUnknown user \"{}\".\n\x1b[0m",
contents[1]
))
.unwrap();
}
} else if contents.starts_with("/users") {
stream.write_all(b"\x1b[0m\x1b[32mUsers:\x1b[0m").unwrap();
for usr in &channel.lock().unwrap().online_users {
stream
.write_fmt(format_args!(
"\n{} (currently {})",
usr.name,
usr.status.get_status_string()
))
.unwrap();
}
stream.write_all(b"\n\x1b[0m").unwrap();
} else { } else {
stream stream
.write_all(b"\x1b[0m\x1b[31;1mThat's an invalid command!\n\x1b[0m") .write_all(b"\x1b[0m\x1b[31;1mThat's an invalid command!\n\x1b[0m")