feat(ui): JVM arguments setting

This commit is contained in:
LynithDev 2026-07-28 13:28:16 +02:00
parent 5f5fa4141a
commit 6344d2e985
3 changed files with 97 additions and 4 deletions

View file

@ -79,6 +79,7 @@ impl Component for ClusterSettings {
}
.into_element(),
)
.child(text_row(cluster_id, TextField::JvmArgs, &profile, &global))
.child(section_header("GAME"))
.child(
ToggleRow {
@ -420,6 +421,7 @@ impl Component for ResolutionRow {
#[derive(Clone, Copy, PartialEq)]
enum TextField {
JvmArgs,
Pre,
Wrapper,
Post,
@ -428,6 +430,12 @@ enum TextField {
impl TextField {
fn meta(self) -> (IconType, &'static str, &'static str, &'static str) {
match self {
Self::JvmArgs => (
IconType::Terminal,
"JVM Arguments",
"Extra arguments passed to Java. Separate them with spaces; quote values containing spaces.",
"-XX:+UseG1GC",
),
Self::Pre => (
IconType::FilePlus02,
"Pre-Launch Command",
@ -451,6 +459,7 @@ impl TextField {
fn value(self, profile: &GameSettingsProfile) -> Option<String> {
match self {
Self::JvmArgs => profile.launch_args.clone(),
Self::Pre => profile.hook_pre.clone(),
Self::Wrapper => profile.hook_wrapper.clone(),
Self::Post => profile.hook_post.clone(),
@ -460,6 +469,7 @@ impl TextField {
fn patch(self, value: Patch<String>) -> ProfileUpdate {
let mut u = ProfileUpdate::default();
match self {
Self::JvmArgs => u.launch_args = value,
Self::Pre => u.hook_pre = value,
Self::Wrapper => u.hook_wrapper = value,
Self::Post => u.hook_post = value,

View file

@ -38,6 +38,10 @@ impl Component for SettingsMinecraft {
let v = profile.mem_max.map(|m| m.to_string()).unwrap_or_default();
move || v
});
let jvm_args = use_state({
let v = profile.launch_args.clone().unwrap_or_default();
move || v
});
let pre_launch_command = use_state({
let v = profile.hook_pre.clone().unwrap_or_default();
move || v
@ -58,6 +62,7 @@ impl Component for SettingsMinecraft {
&width.read(),
&height.read(),
&memory.read(),
&jvm_args.read(),
&pre_launch_command.read(),
&wrapper_command.read(),
&post_exit_command.read(),
@ -89,6 +94,14 @@ impl Component for SettingsMinecraft {
"The amount of memory in megabytes allocated for the game.",
memory_field(memory),
))
.child(settings_row(
IconType::Terminal,
"JVM Arguments",
"Extra arguments passed to Java. Separate them with spaces; quote values containing spaces.",
TextInput::new(jvm_args)
.placeholder("-XX:+UseG1GC")
.width(Size::px(220.)),
))
.child(section_header("PROCESS"))
.child(settings_row(
IconType::FilePlus02,
@ -118,11 +131,13 @@ impl Component for SettingsMinecraft {
}
}
#[allow(clippy::too_many_arguments)]
fn build_update(
fullscreen: bool,
width: &str,
height: &str,
memory: &str,
jvm_args: &str,
pre: &str,
wrapper: &str,
post: &str,
@ -144,6 +159,7 @@ fn build_update(
force_fullscreen: Patch::Set(fullscreen),
resolution,
mem_max,
launch_args: command_patch(jvm_args),
hook_pre: command_patch(pre),
hook_wrapper: command_patch(wrapper),
hook_post: command_patch(post),

View file

@ -52,14 +52,49 @@ pub fn java_arguments(
}
parsed.push(format!("-Xmx{mem_max}M"));
if !custom_args.is_empty() {
parsed.push(custom_args);
}
parsed.extend(split_custom_args(&custom_args));
Ok(parsed)
}
/// Splits a user-entered JVM argument string into individual arguments.
///
/// Whitespace-separated, with double quotes for values that contain spaces
/// (`-Dfoo="a b"`), which is what people expect from every other launcher.
/// Pushing the whole string as one argv entry, as this used to, means the JVM
/// sees a single unrecognised option as soon as there is more than one.
fn split_custom_args(raw: &str) -> Vec<String> {
let mut args = Vec::new();
let mut current = String::new();
let mut quoted = false;
let mut started = false;
for ch in raw.chars() {
match ch {
'"' => {
quoted = !quoted;
started = true;
}
c if c.is_whitespace() && !quoted => {
if started {
args.push(std::mem::take(&mut current));
started = false;
}
}
c => {
current.push(c);
started = true;
}
}
}
if started {
args.push(current);
}
args
}
#[allow(clippy::too_many_arguments)]
pub fn minecraft_arguments(
version_updated: bool,
@ -415,3 +450,35 @@ where
Ok(())
}
#[cfg(test)]
mod tests {
use super::split_custom_args;
#[test]
fn blank_input_contributes_nothing() {
assert!(split_custom_args("").is_empty());
assert!(split_custom_args(" \t ").is_empty());
}
#[test]
fn each_flag_becomes_its_own_argument() {
assert_eq!(
split_custom_args("-XX:+UseG1GC -Xms2G"),
vec!["-XX:+UseG1GC", "-Xms2G"]
);
}
#[test]
fn quotes_hold_a_value_with_spaces_together() {
assert_eq!(
split_custom_args(r#"-Dname="My Server" -Xss1M"#),
vec!["-Dname=My Server", "-Xss1M"]
);
}
#[test]
fn an_empty_quoted_value_survives() {
assert_eq!(split_custom_args(r#"-Dempty="""#), vec!["-Dempty="]);
}
}