This commit is contained in:
parent
8740c41a63
commit
fd3863c0d4
90
Cargo.lock
generated
90
Cargo.lock
generated
@ -1,7 +1,95 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "alt_enter_fix"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.140"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
@ -7,3 +7,6 @@ edition = "2021"
|
||||
|
||||
[target.x86_64-pc-windows-gnu]
|
||||
linker = "x86_64-w64-mingw32-gcc"
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0.140"
|
||||
|
202
src/main.rs
202
src/main.rs
@ -1,60 +1,150 @@
|
||||
use std::{env, fs, io::{self, stdin, Read, Write}, path::Path};
|
||||
fn main() {
|
||||
let mut rblxpath = String::new();
|
||||
if let Some(lad) = env::var("LOCALAPPDATA").ok() {
|
||||
let temprbx_lad = format!("{}\\Roblox", lad);
|
||||
if Path::new(&temprbx_lad).is_dir() {
|
||||
rblxpath = temprbx_lad;
|
||||
}
|
||||
} else if let Some(pfx86) = env::var("ProgramFiles(x86)").ok() {
|
||||
let temprbx_pfx86 = format!("{}\\Roblox", pfx86);
|
||||
if Path::new(&temprbx_pfx86).is_dir() {
|
||||
rblxpath = temprbx_pfx86;
|
||||
}
|
||||
}
|
||||
if rblxpath != "" {
|
||||
println!("Found path: {}", rblxpath);
|
||||
} else {
|
||||
use serde_json::{json, Value};
|
||||
use std::{env, fs, io::{self, stdin, Read, Write}, path::{Path, PathBuf}};
|
||||
|
||||
const UNLIMITED_FPS_CAP_NUM: i32 = i32::max_value();
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
let roblox_path = find_roblox_path().ok_or_else(|| {
|
||||
println!("Roblox not found (Die)");
|
||||
return;
|
||||
}
|
||||
io::Error::new(io::ErrorKind::NotFound, "Roblox installation not found")
|
||||
})?;
|
||||
|
||||
println!("Found path: {}", roblox_path.display());
|
||||
|
||||
println!("What FPS would you like to cap the fps to? (0 for no cap at all)");
|
||||
let _ = io::stdout().flush();
|
||||
let mut input = String::new();
|
||||
stdin().read_line(&mut input).expect("invalid input, try again");
|
||||
if let Some('\n')=input.chars().next_back() {
|
||||
input.pop();
|
||||
}
|
||||
if let Some('\r')=input.chars().next_back() {
|
||||
input.pop();
|
||||
}
|
||||
let fps = input.parse::<i32>().unwrap();
|
||||
let desired_fps = match fps {
|
||||
0 => 5588562,
|
||||
_ => fps
|
||||
};
|
||||
println!("fps cap will be set to {desired_fps}");
|
||||
let just_make_this_work = format!("{{\"FFlagHandleAltEnterFullscreenManually\":\"False\",\"DFIntTaskSchedulerTargetFps\":{desired_fps},\"FFlagEnableInGameMenuChromeABTest3\":\"False\",\"FFlagGameBasicSettingsFramerateCap5\":\"False\",\"FFlagTaskSchedulerLimitTargetFpsTo2402\":\"False\"}}");
|
||||
let fflag_contents = just_make_this_work.as_str();
|
||||
let versions = format!("{}\\Versions\\", rblxpath);
|
||||
for folder in fs::read_dir(versions).unwrap() {
|
||||
let cur_folder = folder.unwrap().path().display().to_string();
|
||||
if cur_folder.to_lowercase().contains("version") {
|
||||
let executable = format!("{}\\RobloxPlayerBeta.exe", cur_folder);
|
||||
if Path::new(&executable).is_file() {
|
||||
let cs_folder = format!("{}\\ClientSettings", cur_folder);
|
||||
if !Path::new(&cs_folder).is_dir() {
|
||||
fs::create_dir(&cs_folder).ok();
|
||||
println!("Created ClientSettings folder in {}", cur_folder);
|
||||
} else {
|
||||
println!("ClientSettings folder already exists in {}", cur_folder);
|
||||
}
|
||||
let cas_json = format!("{}\\ClientAppSettings.json", cs_folder);
|
||||
fs::write(cas_json, fflag_contents).ok();
|
||||
println!("Wrote ClientAppSettings.json to {}", cur_folder)
|
||||
}
|
||||
}
|
||||
}
|
||||
io::stdout().flush()?;
|
||||
|
||||
let fps = read_fps_input()?;
|
||||
let desired_fps = if fps == 0 { UNLIMITED_FPS_CAP_NUM } else { fps };
|
||||
|
||||
println!("FPS cap will be set to {desired_fps}");
|
||||
|
||||
let required_settings = json!({
|
||||
// Restore original 'alt-enter' behaviour
|
||||
"FFlagHandleAltEnterFullscreenManually": "False",
|
||||
|
||||
// Disable Roblox's built-in 240 fps hard-coded cap and replace with custom cap
|
||||
"DFIntTaskSchedulerTargetFps": desired_fps,
|
||||
"FFlagGameBasicSettingsFramerateCap5": "False",
|
||||
"FFlagTaskSchedulerLimitTargetFpsTo2402": "False",
|
||||
|
||||
// Ensure Direct3D11 is the rendering API for optimal performance
|
||||
"FFlagDebugGraphicsDisableDirect3D11": "False",
|
||||
"FFlagDebugGraphicsPreferD3D11": "True",
|
||||
|
||||
// Disable telemetry related FFlags
|
||||
"FFlagDebugDisableTelemetryEphemeralCounter": "True",
|
||||
"FFlagDebugDisableTelemetryEphemeralStat": "True",
|
||||
"FFlagDebugDisableTelemetryEventIngest": "True",
|
||||
"FFlagDebugDisableTelemetryPoint": "True",
|
||||
"FFlagDebugDisableTelemetryV2Counter": "True",
|
||||
"FFlagDebugDisableTelemetryV2Event": "True",
|
||||
"FFlagDebugDisableTelemetryV2Stat": "True"
|
||||
});
|
||||
|
||||
update_client_settings(&roblox_path, required_settings)?;
|
||||
|
||||
println!("Press enter to exit");
|
||||
let _ = io::stdin().read(&mut [0u8]).unwrap();
|
||||
let _ = io::stdin().read(&mut [0u8])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn find_roblox_path() -> Option<PathBuf> {
|
||||
if let Some(local_app_data) = env::var("LOCALAPPDATA").ok() {
|
||||
let path = PathBuf::from(local_app_data).join("Roblox");
|
||||
if path.is_dir() {
|
||||
return Some(path);
|
||||
}
|
||||
}
|
||||
|
||||
// This is here because Roblox installs into Program Files (x86) if you run the installer as administrator (??? lol)
|
||||
if let Some(program_files) = env::var("ProgramFiles(x86)").ok() {
|
||||
let path = PathBuf::from(program_files).join("Roblox");
|
||||
if path.is_dir() {
|
||||
return Some(path);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn read_fps_input() -> io::Result<i32> {
|
||||
let mut input = String::new();
|
||||
stdin().read_line(&mut input)?;
|
||||
|
||||
let input = input.trim();
|
||||
|
||||
input.parse::<i32>().map_err(|_| {
|
||||
io::Error::new(io::ErrorKind::InvalidInput, "Invalid FPS value")
|
||||
})
|
||||
}
|
||||
|
||||
fn update_client_settings(roblox_path: &Path, required_settings: Value) -> io::Result<()> {
|
||||
let versions_path = roblox_path.join("Versions");
|
||||
|
||||
for entry in fs::read_dir(versions_path)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
let path_str = path.to_string_lossy().to_lowercase();
|
||||
|
||||
if path_str.contains("version") {
|
||||
let executable = path.join("RobloxPlayerBeta.exe");
|
||||
|
||||
if executable.is_file() {
|
||||
let client_settings_dir = path.join("ClientSettings");
|
||||
|
||||
if !client_settings_dir.exists() {
|
||||
fs::create_dir(&client_settings_dir)?;
|
||||
println!("Created ClientSettings folder in {}", path.display());
|
||||
} else {
|
||||
println!("ClientSettings folder already exists in {}", path.display());
|
||||
}
|
||||
|
||||
let settings_file = client_settings_dir.join("ClientAppSettings.json");
|
||||
update_settings_file(&settings_file, &required_settings)?;
|
||||
println!("Updated ClientAppSettings.json in {}", path.display());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_settings_file(file_path: &Path, required_settings: &Value) -> io::Result<()> {
|
||||
// Funky code to edit or create the ClientAppSettings.json file (shoutout claude for the help with the object stuff (json sucks))
|
||||
let mut settings = if file_path.exists() {
|
||||
match fs::read_to_string(file_path) {
|
||||
Ok(content) if !content.trim().is_empty() => {
|
||||
match serde_json::from_str::<Value>(&content) {
|
||||
Ok(existing) => existing,
|
||||
Err(e) => {
|
||||
println!("Warning: Could not parse existing settings ({}), creating new file", e);
|
||||
json!({})
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => json!({})
|
||||
}
|
||||
} else {
|
||||
json!({})
|
||||
};
|
||||
|
||||
if let Some(required_obj) = required_settings.as_object() {
|
||||
if !settings.is_object() {
|
||||
settings = json!({});
|
||||
}
|
||||
|
||||
if let Some(settings_obj) = settings.as_object_mut() {
|
||||
for (key, value) in required_obj {
|
||||
println!("Adding {}: {}", key, value);
|
||||
settings_obj.insert(key.clone(), value.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let formatted = serde_json::to_string_pretty(&settings)?;
|
||||
fs::write(file_path, formatted)?;
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue
Block a user