use std::fmt::Display;
use std::sync::LazyLock;
use regex::Regex;
use serde::{Deserialize, Serialize};
use crate::client::rules::Rule;
use crate::java::JAVA_SEPARATOR;
use crate::minecraft::arguments::DataMap;
use crate::minecraft::configuration::MinecraftConfiguration as Configuration;
use super::CollectArguments;
static ARGUMENT_FIND_PATTERN: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\$\{([^}]+)\}").unwrap());
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Arguments {
jvm: Vec<Argument>,
game: Vec<Argument>
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum ArgumentValue {
String(String),
Array(Vec<String>)
}
impl Display for ArgumentValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str = match self {
ArgumentValue::String(s) => s.to_owned(), ArgumentValue::Array(a) => a.join(JAVA_SEPARATOR),
};
write!(f, "{}", str)
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Argument {
value: ArgumentValue,
rules: Option<Vec<Rule>>
}
impl Display for Argument {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(rules) = &self.rules {
for rule in rules {
if !rule.is_followed() {
return write!(f, "{}", String::new())
}
}
}
write!(f, "{}", self.value)
}
}
impl CollectArguments for Vec<Argument> {
fn collect(&self) -> Vec<String> {
let mut result = Vec::new();
for arg in self {
for part in arg.to_string().split(';') {
let trimmed = part.trim();
if !trimmed.is_empty() {
result.push(trimmed.to_string());
}
}
}
result
}
}
fn jvm_setup(config: &Configuration, jvm: &mut Vec<Argument>) {
if let Some(authlib) = &config.session.authlib_server {
let version = &authlib.version;
jvm.insert(0, Argument { value: ArgumentValue::String(format!("-javaagent:${{libraries_dir}}/moe/yushi/authlibinjector/{}/authlibinjector-{}.jar=${{authlib_server}}", version, version)), rules: None });
}
}
fn game_setup(config: &Configuration, game: &mut Vec<Argument>) {
if config.window.width.is_some() {
game.push(Argument { value: ArgumentValue::String("--width; ${width}".to_string()), rules: None });
}
if config.window.height.is_some() {
game.push(Argument { value: ArgumentValue::String("--height; ${height}".to_string()), rules: None });
}
if config.window.fullscreen.is_some() {
game.push(Argument { value: ArgumentValue::String("--fullscreen".to_string()), rules: None });
}
}
impl Arguments {
pub fn collect(&mut self, config: &Configuration, data_map: DataMap<'_>) -> std::io::Result<Vec<String>> {
jvm_setup(config, &mut self.jvm);
game_setup(config, &mut self.game);
let mut result = self.jvm.collect();
result.extend(self.game.collect());
let pattern = &ARGUMENT_FIND_PATTERN; let mut final_result = Vec::new();
for arg in result.iter() {
let mut new_arg = arg.clone();
for cap in pattern.captures_iter(arg) {
let key = &cap[1];
if let Some(value) = data_map.get(key) {
new_arg = new_arg.replace(&cap[0], value);
}
}
final_result.push(new_arg);
}
Ok(final_result)
}
}