Parse vanilla to LogMeta

This commit is contained in:
2025-12-05 00:10:47 +01:00
parent 98d5e59413
commit 5f57bd8668
5 changed files with 146 additions and 19 deletions

View File

@@ -11,11 +11,13 @@ license = "MIT"
publish = false publish = false
[features] [features]
default = ["core", "events"] default = ["core", "events", "mc-vanilla"]
# Core runtime requirements for the currently implemented functionality. # Core runtime requirements for the currently implemented functionality.
core = ["dep:thiserror", "dep:tokio", "dep:tokio-stream", "dep:tokio-util"] core = ["dep:thiserror", "dep:tokio", "dep:tokio-stream", "dep:tokio-util"]
# Placeholder for upcoming event-driven functionality. # Placeholder for upcoming event-driven functionality.
events = [] events = []
mc-vanilla = []
# Add new feature groups here; attach their optional dependencies to the relevant feature list. # Add new feature groups here; attach their optional dependencies to the relevant feature list.
[dependencies] [dependencies]

View File

@@ -1,5 +1,5 @@
pub mod stream; pub mod stream;
pub mod version; pub mod version;
pub use stream::{StreamLine, StreamSource}; pub use stream::{LogMeta, StreamLine, StreamSource};
pub use version::{MinecraftType, MinecraftVersion, Snapshot, Version}; pub use version::{MinecraftType, MinecraftVersion, Snapshot, Version};

View File

@@ -1,5 +1,8 @@
use std::fmt::{self, Display}; use std::fmt::{self, Display};
#[cfg(feature = "mc-vanilla")]
use crate::error::ParserError;
/// Identifies which process stream produced a line of output. /// Identifies which process stream produced a line of output.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum StreamSource { pub enum StreamSource {
@@ -22,6 +25,101 @@ pub struct InstanceEvent {}
#[cfg(feature = "events")] #[cfg(feature = "events")]
pub enum Events {} pub enum Events {}
#[cfg(feature = "mc-vanilla")]
pub struct LogMeta {
time: String,
thread: String,
level: LogLevel,
msg: String,
}
#[cfg(feature = "mc-vanilla")]
pub enum LogLevel {
Info,
Warn,
Error,
Other,
}
#[cfg(feature = "mc-vanilla")]
impl LogMeta {
pub fn new<S: Into<String>>(line: S) -> Result<Option<Self>, ParserError> {
let line: String = line.into();
let line = line.trim();
if !line.starts_with('[') {
return Ok(None);
}
let time_end = match line.find(']') {
Some(i) => i,
None => return Ok(None),
};
let time = line[1..time_end].to_string();
let meta_start = match line[time_end + 1..].find('[') {
Some(j) => time_end + 1 + j,
None => return Ok(None),
};
let msg_sep = match line[meta_start..].find("]: ") {
Some(k) => meta_start + k,
None => return Ok(None),
};
let meta = &line[(meta_start + 1)..msg_sep]; // inside the brackets
let msg = line[(msg_sep + 3)..].to_string(); // after "]: "
let mut thread_level = meta.splitn(2, '/');
let thread = thread_level
.next()
.ok_or(ParserError::ParserError)?
.to_string();
let level_str = thread_level
.next()
.ok_or(ParserError::ParserError)?
.trim_end_matches(']'); // just in case
let level = match level_str {
"INFO" => LogLevel::Info,
"WARN" => LogLevel::Warn,
"ERROR" => LogLevel::Error,
_ => LogLevel::Other,
};
Ok(Some(LogMeta {
time,
thread,
level,
msg,
}))
}
}
#[cfg(feature = "mc-vanilla")]
impl Display for LogMeta {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let line = format!(
"Time: {}\nThread: {}\nLevel: {}\nMessage: {}",
self.time, self.thread, self.level, self.msg
);
write!(f, "{}", line)
}
}
#[cfg(feature = "mc-vanilla")]
impl Display for LogLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
LogLevel::Info => write!(f, "INFO"),
LogLevel::Warn => write!(f, "WARN"),
LogLevel::Error => write!(f, "ERROR"),
LogLevel::Other => write!(f, "OTHER"),
}
}
}
impl StreamLine { impl StreamLine {
pub fn new<S: Into<String>>(line: S, source: StreamSource) -> Self { pub fn new<S: Into<String>>(line: S, source: StreamSource) -> Self {
Self { Self {
@@ -43,6 +141,10 @@ impl StreamLine {
source: StreamSource::Stderr, source: StreamSource::Stderr,
} }
} }
pub fn msg(&self) -> String {
self.line.clone()
}
} }
impl Display for StreamLine { impl Display for StreamLine {

View File

@@ -92,3 +92,10 @@ pub enum ServerError {
#[error("Failed to write to stdin")] #[error("Failed to write to stdin")]
StdinWriteFailed, StdinWriteFailed,
} }
#[cfg(feature = "events")]
#[derive(Debug, Clone, Error)]
pub enum ParserError {
#[error("ParserError")]
ParserError,
}

View File

@@ -74,7 +74,7 @@ impl InstanceHandle {
root_dir: root, root_dir: root,
jar_path: path, jar_path: path,
mc_version: parsed_version, mc_version: parsed_version,
mc_type: mc_type, mc_type,
}; };
let status = InstanceStatus::Stopped; let status = InstanceStatus::Stopped;
@@ -248,32 +248,48 @@ impl InstanceHandle {
Ok(()) Ok(())
} }
#[cfg(feature = "events")] #[cfg(all(feature = "events", any(feature = "mc-vanilla")))]
fn setup_parser(&mut self) -> Result<(), ServerError> { fn setup_parser(&mut self) -> Result<(), ServerError> {
let stdout_stream = self let stdout_stream = self
.subscribe(StreamSource::Stdout) .subscribe(StreamSource::Stdout)
.map_err(|_| ServerError::NoStdoutPipe)?; .map_err(|_| ServerError::NoStdoutPipe)?;
let shutdown = self.shutdown.clone(); let shutdown = self.shutdown.clone();
let event_tx = self.events_tx.clone(); // TODO: Stream events!!!!
let _event_tx = self.events_tx.clone();
tokio::spawn(async move { #[cfg(feature = "mc-vanilla")]
let mut rx = stdout_stream; if self.data.mc_type == MinecraftType::Vanilla {
use crate::config::LogMeta;
tokio::spawn(async move {
let mut rx = stdout_stream;
loop { loop {
tokio::select! { tokio::select! {
_ = shutdown.cancelled() => { _ = shutdown.cancelled() => {
break; break;
} }
line = rx.next() => { line = rx.next() => {
match line { match line {
Some(Ok(_)) => { Some(Ok(val)) => {
}, let msg = val.msg();
_ => (), let meta = LogMeta::new(msg);
match meta {
Ok(val) => {
if val.is_some() {
println!("{}", val.unwrap());
}
}
Err(_) => (),
}
},
_ => (),
}
} }
} }
} }
} });
}); }
Ok(()) Ok(())
} }