OneLauncher-Cracked/packages/oneclient_app/src/view/app/settings/minecraft.rs
2026-07-31 04:50:49 +07:00

223 lines
7 KiB
Rust

use freya::prelude::*;
use oneclient_common::Patch;
use oneclient_core::settings::{ProfileUpdate, Resolution};
use super::settings_page;
use crate::components::{Icon, IconType, TextInput, toggle, validate_number};
use crate::hooks::{use_dispatch, use_settings_snapshot};
use crate::theme::colors;
use crate::view::app::settings::{section_header, settings_row};
#[derive(PartialEq)]
pub struct SettingsMinecraft;
impl Component for SettingsMinecraft {
fn render(&self) -> impl IntoElement {
let profile = use_settings_snapshot().settings.global_game_settings;
let dispatch = use_dispatch();
let fullscreen = use_state({
let v = profile.force_fullscreen.unwrap_or(false);
move || v
});
let width = use_state({
let v = profile
.resolution
.map(|r| r.width.to_string())
.unwrap_or_default();
move || v
});
let height = use_state({
let v = profile
.resolution
.map(|r| r.height.to_string())
.unwrap_or_default();
move || v
});
let memory = use_state({
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
});
let wrapper_command = use_state({
let v = profile.hook_wrapper.clone().unwrap_or_default();
move || v
});
let post_exit_command = use_state({
let v = profile.hook_post.clone().unwrap_or_default();
move || v
});
let mut first = use_state(|| true);
use_side_effect(move || {
let update = build_update(
*fullscreen.read(),
&width.read(),
&height.read(),
&memory.read(),
&jvm_args.read(),
&pre_launch_command.read(),
&wrapper_command.read(),
&post_exit_command.read(),
);
if *first.peek() {
first.set(false);
return;
}
dispatch.update_global_profile(update);
});
settings_page()
.child(section_header("GAME"))
.child(settings_row(
IconType::Maximize01,
"Force Fullscreen",
"Force Minecraft to start in fullscreen mode.",
toggle(fullscreen),
))
.child(settings_row(
IconType::LayoutTop,
"Resolution",
"The game window resolution in pixels.",
resolution_field(width, height),
))
.child(settings_row(
IconType::Database01,
"Memory",
"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,
"Pre-Launch Command",
"Command to run before launching the game.",
TextInput::new(pre_launch_command)
.placeholder("echo 'Game started'")
.width(Size::px(220.)),
))
.child(settings_row(
IconType::ParagraphWrap,
"Wrapper Command",
"Command to run when launching the game.",
TextInput::new(wrapper_command)
.placeholder("gamescope")
.width(Size::px(220.)),
))
.child(settings_row(
IconType::FileX02,
"Post-Exit Command",
"Command to run after exiting the game.",
TextInput::new(post_exit_command)
.placeholder("echo 'Game exited'")
.width(Size::px(220.)),
))
.into_element()
}
}
#[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,
) -> ProfileUpdate {
let resolution = match (width.trim(), height.trim()) {
("", "") => Patch::Clear,
(w, h) => match (w.parse::<u32>(), h.parse::<u32>()) {
(Ok(w), Ok(h)) => Patch::Set(Resolution::new(w, h)),
_ => Patch::Unchanged,
},
};
let mem_max = match memory.trim() {
"" => Patch::Clear,
m => m.parse::<u32>().map(Patch::Set).unwrap_or(Patch::Unchanged),
};
ProfileUpdate {
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),
..Default::default()
}
}
fn command_patch(value: &str) -> Patch<String> {
match value.trim() {
"" => Patch::Clear,
v => Patch::Set(v.to_string()),
}
}
fn resolution_field(width: State<String>, height: State<String>) -> impl IntoElement {
rect()
.horizontal()
.cross_align(Alignment::Center)
.spacing(8.)
.child(
TextInput::new(width)
.placeholder("854")
.on_validate(validate_number)
.text_align(TextAlign::Center)
.width(Size::px(70.))
.text_align(TextAlign::Center),
)
.child(
Icon::new(IconType::X)
.size(14.)
.color(colors::fg_secondary()),
)
.child(
TextInput::new(height)
.placeholder("480")
.on_validate(validate_number)
.text_align(TextAlign::Center)
.width(Size::px(70.))
.text_align(TextAlign::Center),
)
.into_element()
}
fn memory_field(memory: State<String>) -> impl IntoElement {
rect()
.horizontal()
.cross_align(Alignment::Center)
.spacing(8.)
.child(
TextInput::new(memory)
.width(Size::px(90.))
.placeholder("4096")
.on_validate(validate_number)
.trailing(
label()
.text("MB")
.font_size(12.)
.color(colors::fg_secondary()),
),
)
}