190 lines
5.4 KiB
Rust
190 lines
5.4 KiB
Rust
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
use crate::error::MolotovError;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::collections::{HashMap, HashSet};
|
|
use std::path::Path;
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
#[serde(untagged)]
|
|
pub enum FlagValue {
|
|
Boolean(bool),
|
|
Integer(i64),
|
|
Text(String),
|
|
}
|
|
|
|
impl From<bool> for FlagValue {
|
|
fn from(v: bool) -> Self {
|
|
FlagValue::Boolean(v)
|
|
}
|
|
}
|
|
|
|
impl From<i64> for FlagValue {
|
|
fn from(v: i64) -> Self {
|
|
FlagValue::Integer(v)
|
|
}
|
|
}
|
|
|
|
impl From<i32> for FlagValue {
|
|
fn from(v: i32) -> Self {
|
|
FlagValue::Integer(v as i64)
|
|
}
|
|
}
|
|
|
|
impl From<&str> for FlagValue {
|
|
fn from(v: &str) -> Self {
|
|
FlagValue::Text(v.to_string())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum PresetProfile {
|
|
Default,
|
|
LowVram,
|
|
CompetitiveUncapped,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
pub struct FastFlagProfile {
|
|
pub name: String,
|
|
pub flags: HashMap<String, FlagValue>,
|
|
}
|
|
|
|
impl FastFlagProfile {
|
|
pub fn new(name: &str) -> Self {
|
|
Self {
|
|
name: name.to_string(),
|
|
flags: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
pub fn set(&mut self, key: &str, val: FlagValue) {
|
|
self.flags.insert(key.to_string(), val);
|
|
}
|
|
|
|
pub fn get_int(&self, key: &str) -> Option<i64> {
|
|
match self.flags.get(key) {
|
|
Some(FlagValue::Integer(v)) => Some(*v),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_bool(&self, key: &str) -> Option<bool> {
|
|
match self.flags.get(key) {
|
|
Some(FlagValue::Boolean(v)) => Some(*v),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct FastFlagManager {
|
|
active_profile: FastFlagProfile,
|
|
allowlist: HashSet<String>,
|
|
}
|
|
|
|
impl Default for FastFlagManager {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl FastFlagManager {
|
|
pub fn new() -> Self {
|
|
let mut allowlist = HashSet::new();
|
|
// Seed standard safe FastFlags
|
|
allowlist.insert("FIntDebugTextureManagerSkipMips".to_string());
|
|
allowlist.insert("DFIntTextureCompositorActiveJobs".to_string());
|
|
allowlist.insert("DFIntTaskSchedulerTargetFps".to_string());
|
|
allowlist.insert("FFlagDebugGraphicsDisableDirect3D11".to_string());
|
|
allowlist.insert("DFFlagDebugPauseVoxelizer".to_string());
|
|
|
|
Self {
|
|
active_profile: FastFlagProfile::new("Default"),
|
|
allowlist,
|
|
}
|
|
}
|
|
|
|
pub fn active_profile(&self) -> &FastFlagProfile {
|
|
&self.active_profile
|
|
}
|
|
|
|
pub fn active_profile_mut(&mut self) -> &mut FastFlagProfile {
|
|
&mut self.active_profile
|
|
}
|
|
|
|
pub fn set_flag(&mut self, key: &str, val: FlagValue) {
|
|
self.active_profile.set(key, val);
|
|
}
|
|
|
|
/// Sets a FastFlag, coercing the string input to the expected type based on standard prefix rules
|
|
pub fn set_flag_coerced(&mut self, key: &str, raw_val: &str) {
|
|
let value = if key.starts_with("FFlag") || key.starts_with("DFFlag") {
|
|
let lower = raw_val.trim().to_lowercase();
|
|
if lower == "true" || lower == "1" {
|
|
FlagValue::Boolean(true)
|
|
} else if lower == "false" || lower == "0" {
|
|
FlagValue::Boolean(false)
|
|
} else {
|
|
FlagValue::Text(raw_val.to_string())
|
|
}
|
|
} else if key.starts_with("FInt") || key.starts_with("DFInt") {
|
|
if let Ok(num) = raw_val.trim().parse::<i64>() {
|
|
FlagValue::Integer(num)
|
|
} else {
|
|
FlagValue::Text(raw_val.to_string())
|
|
}
|
|
} else {
|
|
FlagValue::Text(raw_val.to_string())
|
|
};
|
|
|
|
self.set_flag(key, value);
|
|
}
|
|
|
|
pub fn import_client_app_settings(&mut self, json_str: &str) -> Result<(), MolotovError> {
|
|
let flags: HashMap<String, FlagValue> = serde_json::from_str(json_str)?;
|
|
for (k, v) in flags {
|
|
self.set_flag(&k, v);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn get_preset(&self, preset: PresetProfile) -> FastFlagProfile {
|
|
match preset {
|
|
PresetProfile::Default => FastFlagProfile::new("Default"),
|
|
PresetProfile::LowVram => {
|
|
let mut p = FastFlagProfile::new("Low VRAM");
|
|
p.set("FIntDebugTextureManagerSkipMips", 1.into());
|
|
p.set("DFIntTextureCompositorActiveJobs", 2.into());
|
|
p
|
|
}
|
|
PresetProfile::CompetitiveUncapped => {
|
|
let mut p = FastFlagProfile::new("Competitive Uncapped");
|
|
p.set("DFIntTaskSchedulerTargetFps", 240.into());
|
|
p
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn apply_preset(&mut self, preset: PresetProfile) {
|
|
self.active_profile = self.get_preset(preset);
|
|
}
|
|
|
|
pub fn validate_against_allowlist(&self) -> Vec<String> {
|
|
let mut unverified = Vec::new();
|
|
for key in self.active_profile.flags.keys() {
|
|
if !self.allowlist.contains(key) {
|
|
unverified.push(key.clone());
|
|
}
|
|
}
|
|
unverified.sort();
|
|
unverified
|
|
}
|
|
|
|
pub fn export_client_app_settings(&self, target_path: &Path) -> Result<(), MolotovError> {
|
|
if let Some(parent) = target_path.parent() {
|
|
std::fs::create_dir_all(parent)?;
|
|
}
|
|
let json = serde_json::to_string_pretty(&self.active_profile.flags)?;
|
|
std::fs::write(target_path, json)?;
|
|
Ok(())
|
|
}
|
|
}
|