mirror of
https://github.com/Dummi26/mcdcbot.git
synced 2025-03-10 13:43:53 +01:00
215 lines
9.6 KiB
Rust
Executable File
215 lines
9.6 KiB
Rust
Executable File
use std::{
|
|
io::{BufRead, BufReader, Write},
|
|
process::{self, Stdio},
|
|
};
|
|
|
|
use crate::{
|
|
chat::ChatMessage,
|
|
events::{self, MinecraftServerEventType},
|
|
MinecraftServerSettings, MinecraftServerType,
|
|
};
|
|
|
|
pub enum ParseOutput {
|
|
Nothing,
|
|
Error(ParseError),
|
|
Event(MinecraftServerEventType),
|
|
}
|
|
|
|
pub enum ParseError {
|
|
/// any other errors (for custom line parser implementations)
|
|
Custom(String),
|
|
}
|
|
|
|
pub fn parse_line(line: &str, settings: &MinecraftServerSettings) -> ParseOutput {
|
|
if line.trim().is_empty() {
|
|
return ParseOutput::Nothing;
|
|
}
|
|
match &settings.server_type {
|
|
MinecraftServerType::Custom {
|
|
line_parser,
|
|
line_parser_proc,
|
|
..
|
|
} => {
|
|
let mut proc = line_parser_proc.lock().unwrap();
|
|
let proc = &mut *proc;
|
|
let make_new_proc = if let Some((proc, _, _)) = proc {
|
|
if let Ok(Some(_)) = proc.try_wait() {
|
|
// has exited
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
} else {
|
|
true
|
|
};
|
|
if make_new_proc {
|
|
if let Ok(mut new_proc) = process::Command::new(line_parser)
|
|
.stdin(Stdio::piped())
|
|
.stdout(Stdio::piped())
|
|
.spawn()
|
|
{
|
|
if let (Some(stdin), Some(stdout)) =
|
|
(new_proc.stdin.take(), new_proc.stdout.take())
|
|
{
|
|
*proc = Some((new_proc, stdin, BufReader::new(stdout)));
|
|
} else {
|
|
eprintln!("[WARN/CUSTOM-LINE-PARSER] No stdin/stdout handles!");
|
|
_ = new_proc.kill();
|
|
}
|
|
} else {
|
|
eprintln!("[WARN/CUSTOM-LINE-PARSER] Can't spawn command '{line_parser}'!");
|
|
}
|
|
}
|
|
if let Some((_proc, stdin, stdout)) = proc {
|
|
if let Err(e) = writeln!(stdin, "{line}") {
|
|
eprintln!("[WARN/CUSTOM-LINE-PARSER] Can't write to stdin: {e:?}");
|
|
return ParseOutput::Nothing;
|
|
};
|
|
let mut buf = String::new();
|
|
if let Err(e) = stdout.read_line(&mut buf) {
|
|
eprintln!("[WARN/CUSTOM-LINE-PARSER] Can't read_line: {e:?}");
|
|
return ParseOutput::Nothing;
|
|
};
|
|
if buf.ends_with('\n') || buf.ends_with('\r') {
|
|
buf.pop();
|
|
}
|
|
if buf.len() > 0 {
|
|
match buf.as_bytes()[0] {
|
|
b'c' => {
|
|
ParseOutput::Event(MinecraftServerEventType::ChatMessage(ChatMessage {
|
|
author: buf[1..].to_owned(),
|
|
message: {
|
|
let mut o = String::new();
|
|
if let Err(e) = stdout.read_line(&mut o) {
|
|
eprintln!(
|
|
"[WARN/CUSTOM-LINE-PARSER] Can't read_line: {e:?}"
|
|
);
|
|
return ParseOutput::Nothing;
|
|
}
|
|
o
|
|
},
|
|
}))
|
|
}
|
|
b'j' => ParseOutput::Event(MinecraftServerEventType::JoinLeave(
|
|
events::JoinLeaveEvent {
|
|
username: buf[1..].to_owned(),
|
|
joined: true,
|
|
},
|
|
)),
|
|
b'l' => ParseOutput::Event(MinecraftServerEventType::JoinLeave(
|
|
events::JoinLeaveEvent {
|
|
username: buf[1..].to_owned(),
|
|
joined: false,
|
|
},
|
|
)),
|
|
b'e' => ParseOutput::Error({
|
|
if buf.len() > 1 {
|
|
match buf.as_bytes()[1] {
|
|
b'c' => ParseError::Custom(buf[2..].to_string()),
|
|
_ => ParseError::Custom(String::new()),
|
|
}
|
|
} else {
|
|
ParseError::Custom(String::new())
|
|
}
|
|
}),
|
|
_ => ParseOutput::Nothing,
|
|
}
|
|
} else {
|
|
ParseOutput::Nothing
|
|
}
|
|
} else {
|
|
eprintln!("[WARN/CUSTOM-LINE-PARSER] No process!");
|
|
ParseOutput::Nothing
|
|
}
|
|
}
|
|
MinecraftServerType::VanillaMojang => {
|
|
if let Some((_time, rest)) = line[1..].split_once("] [Server thread/INFO]: ") {
|
|
let rest = rest.trim();
|
|
if rest.starts_with("<") {
|
|
if let Some((user, msg)) = rest[1..].split_once("> ") {
|
|
return ParseOutput::Event(MinecraftServerEventType::ChatMessage(
|
|
ChatMessage {
|
|
author: user.to_owned(),
|
|
message: msg.to_owned(),
|
|
},
|
|
));
|
|
}
|
|
} else if rest.ends_with(" joined the game") {
|
|
return ParseOutput::Event(MinecraftServerEventType::JoinLeave(
|
|
events::JoinLeaveEvent {
|
|
username: rest[0..rest.len() - " joined the game".len()].to_owned(),
|
|
joined: true,
|
|
},
|
|
));
|
|
} else if rest.ends_with(" left the game") {
|
|
return ParseOutput::Event(MinecraftServerEventType::JoinLeave(
|
|
events::JoinLeaveEvent {
|
|
username: rest[0..rest.len() - " left the game".len()].to_owned(),
|
|
joined: false,
|
|
},
|
|
));
|
|
}
|
|
}
|
|
ParseOutput::Nothing
|
|
// Vanilla servers not yet supported...
|
|
}
|
|
MinecraftServerType::VanillaPaperMC => {
|
|
match line.chars().next() {
|
|
Some('[') => {
|
|
if let Some((_time, rest)) = line[1..].split_once(' ') {
|
|
if let Some((severity, rest)) = rest.split_once(']') {
|
|
if rest.starts_with(": ") {
|
|
let rest = &rest[2..];
|
|
// eprintln!("Time: '{time}', Severity: '{severity}', Rest: '{rest}'.");
|
|
match severity {
|
|
"INFO" => {
|
|
if let Some('<') = rest.chars().next() {
|
|
if let Some((username, message)) =
|
|
rest[1..].split_once('>')
|
|
{
|
|
return ParseOutput::Event(
|
|
MinecraftServerEventType::ChatMessage(
|
|
ChatMessage {
|
|
author: username.to_string(),
|
|
message: message[1..].to_string(),
|
|
},
|
|
),
|
|
);
|
|
}
|
|
} // join/leave
|
|
if rest.trim_end().ends_with(" joined the game") {
|
|
let username = &rest[..rest.len() - 16];
|
|
return ParseOutput::Event(
|
|
MinecraftServerEventType::JoinLeave(
|
|
events::JoinLeaveEvent {
|
|
username: username.to_string(),
|
|
joined: true,
|
|
},
|
|
),
|
|
);
|
|
}
|
|
if rest.trim_end().ends_with(" left the game") {
|
|
let username = &rest[..rest.len() - 14];
|
|
return ParseOutput::Event(
|
|
MinecraftServerEventType::JoinLeave(
|
|
events::JoinLeaveEvent {
|
|
username: username.to_string(),
|
|
joined: false,
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|
|
_ => (),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_ => (),
|
|
}
|
|
ParseOutput::Nothing
|
|
}
|
|
}
|
|
}
|